├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── ci.yaml │ └── e2e.yaml ├── .gitignore ├── Dockerfile ├── LICENSE ├── OWNERS.md ├── README.md ├── examples ├── default │ ├── conditions │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── connection_details │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── context │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── debug_print │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── dependencies │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── events │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── external_deps │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── extra_resources │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── extra_resources.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── function_ctx │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── read_ocds_resource │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── existing-observed-resources.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── resources │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions-docker.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ └── resources_without_xr │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml ├── patch_desired │ ├── patching │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ └── patching_multiple │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml ├── patch_resources │ ├── patching │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ └── patching_multiple │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml ├── resources │ ├── basic │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── condition │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── custom_composition_resource_name │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── custom_external_name │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── database │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.k │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── format │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── loop │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── network │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.k │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ ├── git_composition.yaml │ │ ├── local_composition.yaml │ │ ├── oci_composition.yaml │ │ └── xr.yaml │ ├── options │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── options_oxr │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ ├── pass_data │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml │ └── regex │ │ ├── Makefile │ │ ├── README.md │ │ ├── composition.yaml │ │ ├── functions.yaml │ │ └── xr.yaml └── xr │ └── patching │ ├── Makefile │ ├── README.md │ ├── composition.yaml │ ├── functions.yaml │ └── xr.yaml ├── examples_kcl └── eks │ ├── Makefile │ ├── composition.k │ └── kcl.yaml ├── fn.go ├── fn_test.go ├── go.mod ├── go.sum ├── input ├── generate.go └── v1beta1 │ ├── input.go │ └── zz_generated.deepcopy.go ├── main.go ├── manifests └── kcl-functions.yaml ├── package ├── crossplane.yaml └── input │ └── template.fn.crossplane.io_kclinputs.yaml ├── pkg └── resource │ ├── conditions.go │ ├── context.go │ ├── context_test.go │ ├── events.go │ ├── extraresources.go │ ├── res.go │ └── res_test.go ├── renovate.json └── scripts └── e2e.sh /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Help us diagnose and fix bugs in this Function 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 | Function version: 30 | 31 | 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Help us make this Function more useful 4 | labels: enhancement 5 | --- 6 | 13 | 14 | ### What problem are you facing? 15 | 20 | 21 | ### How could this Function help solve your problem? 22 | 25 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | ### Description of your changes 10 | 11 | 21 | 22 | Fixes # 23 | 24 | I have: 25 | 26 | - [ ] Read and followed Crossplane's [contribution process]. 27 | - [ ] Added or updated unit tests for my change. 28 | 29 | [contribution process]: https://git.io/fj2m9 30 | [docs]: https://docs.crossplane.io/contribute/contribute 31 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - release-* 8 | tags: 9 | - "v*" 10 | pull_request: {} 11 | workflow_dispatch: 12 | inputs: 13 | version: 14 | description: Package version (e.g. v0.1.0) 15 | required: false 16 | 17 | env: 18 | # Common versions 19 | GO_VERSION: '1.24' 20 | GOLANGCI_VERSION: 'v1.54.2' 21 | DOCKER_BUILDX_VERSION: 'v0.11.2' 22 | 23 | # These environment variables are important to the Crossplane CLI install.sh 24 | # script. They determine what version it installs. 25 | XP_CHANNEL: master # TODO(negz): Pin to stable once v1.14 is released. 26 | XP_VERSION: current # TODO(negz): Pin to a version once v1.14 is released. 27 | 28 | # This CI job will automatically push new builds to xpkg.upbound.io if the 29 | # XPKG_ACCESS_ID and XPKG_TOKEN secrets are set in the GitHub respository (or 30 | # organization) settings. Create a token at https://accounts.upbound.io. 31 | XPKG_ACCESS_ID: ${{ secrets.XPKG_ACCESS_ID }} 32 | 33 | # The package to push, without a version tag. The default matches GitHub. For 34 | # example xpkg.upbound.io/crossplane/function-template-go. 35 | XPKG: xpkg.upbound.io/${{ github.repository}} 36 | CROSSPLANE_REGORG: ghcr.io/${{ github.repository}} # xpkg.crossplane.io/crossplane-contrib 37 | 38 | # The package version to push. The default is 0.0.0-gitsha. 39 | XPKG_VERSION: v0.11.4 40 | 41 | jobs: 42 | lint: 43 | runs-on: ubuntu-24.04 44 | steps: 45 | - name: Checkout 46 | uses: actions/checkout@v4 47 | 48 | - name: Setup Go 49 | uses: actions/setup-go@v5 50 | with: 51 | go-version: ${{ env.GO_VERSION }} 52 | cache: false # The golangci-lint action does its own caching. 53 | 54 | - name: Check go mod tidy 55 | run: go mod tidy && git diff --exit-code go.mod go.sum 56 | 57 | unit-test: 58 | runs-on: ubuntu-24.04 59 | steps: 60 | - name: Checkout 61 | uses: actions/checkout@v4 62 | 63 | - name: Setup Go 64 | uses: actions/setup-go@v5 65 | with: 66 | go-version: ${{ env.GO_VERSION }} 67 | 68 | - name: Run Unit Tests 69 | run: go test -v -cover ./... 70 | 71 | # We want to build most packages for the amd64 and arm64 architectures. To 72 | # speed this up we build single-platform packages in parallel. We then upload 73 | # those packages to GitHub as a build artifact. The push job downloads those 74 | # artifacts and pushes them as a single multi-platform package. 75 | build: 76 | runs-on: ubuntu-24.04 77 | strategy: 78 | fail-fast: true 79 | matrix: 80 | arch: 81 | - amd64 82 | - arm64 83 | steps: 84 | - name: Setup QEMU 85 | uses: docker/setup-qemu-action@v3 86 | with: 87 | platforms: all 88 | 89 | - name: Setup Docker Buildx 90 | uses: docker/setup-buildx-action@v3 91 | with: 92 | version: ${{ env.DOCKER_BUILDX_VERSION }} 93 | install: true 94 | 95 | - name: Checkout 96 | uses: actions/checkout@v4 97 | 98 | - name: Build Runtime 99 | id: image 100 | uses: docker/build-push-action@v6 101 | with: 102 | context: . 103 | platforms: linux/${{ matrix.arch }} 104 | target: image 105 | build-args: 106 | GO_VERSION=${{ env.GO_VERSION }} 107 | outputs: type=docker,dest=runtime-${{ matrix.arch }}.tar 108 | 109 | - name: Setup the Crossplane CLI 110 | run: "curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh | sh" 111 | 112 | - name: Build Package 113 | run: ./crossplane xpkg build --package-file=${{ matrix.arch }}.xpkg --package-root=package/ --embed-runtime-image-tarball=runtime-${{ matrix.arch }}.tar 114 | 115 | - name: Upload Single-Platform Package 116 | uses: actions/upload-artifact@v4 117 | with: 118 | name: package-${{ matrix.arch }} 119 | path: "*.xpkg" 120 | if-no-files-found: error 121 | retention-days: 1 122 | 123 | # This job downloads the single-platform packages built by the build job, and 124 | # pushes them as a multi-platform package. We only push the package it the 125 | # XPKG_ACCESS_ID and XPKG_TOKEN secrets were provided. 126 | push: 127 | runs-on: ubuntu-24.04 128 | needs: 129 | - build 130 | if: "startsWith(github.ref, 'refs/tags/')" 131 | steps: 132 | - name: Checkout 133 | uses: actions/checkout@v4 134 | 135 | - name: Download Single-Platform Packages 136 | uses: actions/download-artifact@v4 137 | with: 138 | path: . 139 | merge-multiple: true 140 | 141 | - name: Setup the Crossplane CLI 142 | run: "curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh | sh" 143 | 144 | - name: Login to Upbound 145 | uses: docker/login-action@v3 146 | if: env.XPKG_ACCESS_ID != '' 147 | with: 148 | registry: xpkg.upbound.io 149 | username: ${{ secrets.XPKG_ACCESS_ID }} 150 | password: ${{ secrets.XPKG_TOKEN }} 151 | 152 | # If a version wasn't explicitly passed as a workflow_dispatch input we 153 | # default to version v0.0.0--, for example 154 | # v0.0.0-20231101115142-1091066df799. This is a simple implementation of 155 | # Go's pseudo-versions: https://go.dev/ref/mod#pseudo-versions. 156 | - name: Set Default Multi-Platform Package Version 157 | if: env.XPKG_VERSION == '' 158 | run: echo "XPKG_VERSION=v0.0.0-$(date -d@$(git show -s --format=%ct) +%Y%m%d%H%M%S)-$(git rev-parse --short=12 HEAD)" >> $GITHUB_ENV 159 | 160 | - name: Push Multi-Platform Package to Upbound 161 | if: env.XPKG_ACCESS_ID != '' 162 | run: "./crossplane --verbose xpkg push --package-files $(echo *.xpkg|tr ' ' ,) ${{ env.XPKG }}:${{ env.XPKG_VERSION }}" 163 | 164 | - name: Push Latest Package to Upbound 165 | if: env.XPKG_ACCESS_ID != '' 166 | run: "./crossplane --verbose xpkg push --package-files $(echo *.xpkg|tr ' ' ,) ${{ env.XPKG }}:latest" 167 | 168 | - name: Login to GHCR 169 | uses: docker/login-action@v3 170 | with: 171 | registry: ghcr.io 172 | username: ${{ github.repository_owner }} 173 | password: ${{ secrets.GITHUB_TOKEN }} 174 | 175 | - name: Push Multi-Platform Package to GHCR 176 | run: "./crossplane --verbose xpkg push --package-files $(echo *.xpkg|tr ' ' ,) ${{ env.CROSSPLANE_REGORG }}:${{ env.XPKG_VERSION }}" 177 | 178 | -------------------------------------------------------------------------------- /.github/workflows/e2e.yaml: -------------------------------------------------------------------------------- 1 | name: Function KCL e2e tests 2 | on: 3 | push: 4 | branches: 5 | - main 6 | - release-* 7 | tags: 8 | - "v*" 9 | pull_request: {} 10 | 11 | env: 12 | GO_VERSION: '1.24' 13 | 14 | jobs: 15 | test: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v4 20 | 21 | - name: Setup Go 22 | uses: actions/setup-go@v5 23 | with: 24 | go-version: ${{ env.GO_VERSION }} 25 | 26 | - name: Start Function Server 27 | run: | 28 | go run . --insecure --debug & 29 | sleep 10 30 | 31 | - name: Setup the Crossplane CLI 32 | run: "curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh | sh" 33 | 34 | - name: Run e2e Tests 35 | run: export PATH=$PATH:$PWD && scripts/e2e.sh 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | vendor/ 16 | .kclvm 17 | .DS_store 18 | package/*.xpkg 19 | 20 | .idea 21 | .vscode -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # We use the latest Go 1.x version unless asked to use something else. 2 | # The GitHub Actions CI job sets this argument for a consistent Go version. 3 | ARG GO_VERSION=1 4 | ARG BASE_IMAGE=kcllang/kcl 5 | 6 | # Setup the base environment. The BUILDPLATFORM is set automatically by Docker. 7 | # The --platform=${BUILDPLATFORM} flag tells Docker to build the function using 8 | # the OS and architecture of the host running the build, not the OS and 9 | # architecture that we're building the function for. 10 | FROM --platform=${BUILDPLATFORM} golang:${GO_VERSION} AS build 11 | 12 | WORKDIR /fn 13 | 14 | # Most functions don't want or need CGo support, so we disable it. 15 | ENV CGO_ENABLED=0 16 | 17 | # We run go mod download in a separate step so that we can cache its results. 18 | # This lets us avoid re-downloading modules if we don't need to. The type=target 19 | # mount tells Docker to mount the current directory read-only in the WORKDIR. 20 | # The type=cache mount tells Docker to cache the Go modules cache across builds. 21 | RUN --mount=target=. --mount=type=cache,target=/go/pkg/mod go mod download 22 | 23 | # The TARGETOS and TARGETARCH args are set by docker. We set GOOS and GOARCH to 24 | # these values to ask Go to compile a binary for these architectures. If 25 | # TARGETOS and TARGETOS are different from BUILDPLATFORM, Go will cross compile 26 | # for us (e.g. compile a linux/amd64 binary on a linux/arm64 build machine). 27 | ARG TARGETOS 28 | ARG TARGETARCH 29 | 30 | # Build the function binary. The type=target mount tells Docker to mount the 31 | # current directory read-only in the WORKDIR. The type=cache mount tells Docker 32 | # to cache the Go modules cache across builds. 33 | RUN --mount=target=. \ 34 | --mount=type=cache,target=/go/pkg/mod \ 35 | --mount=type=cache,target=/root/.cache/go-build \ 36 | GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags "-w -s" -o /function . 37 | 38 | # Produce the Function image. We use a very lightweight 'distroless' image that 39 | # does not include any of the build tools used in previous stages. 40 | FROM ${BASE_IMAGE} AS image 41 | WORKDIR / 42 | COPY --from=build /function /function 43 | RUN /function --help 44 | EXPOSE 9443 45 | ENTRYPOINT ["/function"] 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /OWNERS.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # OWNERS 8 | 9 | This page lists all maintainers for **this** repository. Each repository in the 10 | [Crossplane Contrib organization](https://github.com/crossplane-contrib/) will list their 11 | repository maintainers in their own `OWNERS.md` file. 12 | 13 | ## Maintainers 14 | * Peefy ([peefy](https://github.com/peefy)) 15 | * Zong Zhe ([zong-zhe](https://github.com/zong-zhe)) 16 | 17 | -------------------------------------------------------------------------------- /examples/default/conditions/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render --verbose xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/default/conditions/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render --verbose xr.yaml composition.yaml functions.yaml -r --extra-resources extra_resources.yaml 14 | --- 15 | --- 16 | apiVersion: example.crossplane.io/v1beta1 17 | kind: XR 18 | metadata: 19 | name: example 20 | status: 21 | conditions: 22 | - lastTransitionTime: "2024-01-01T00:00:00Z" 23 | message: 'Unready resources: another-awesome-dev-bucket, my-awesome-dev-bucket' 24 | reason: Creating 25 | status: "False" 26 | type: Ready 27 | --- 28 | apiVersion: example/v1alpha1 29 | kind: Foo 30 | metadata: 31 | annotations: 32 | crossplane.io/composition-resource-name: another-awesome-dev-bucket 33 | generateName: example- 34 | labels: 35 | crossplane.io/composite: example 36 | name: another-awesome-dev-bucket 37 | ownerReferences: 38 | - apiVersion: example.crossplane.io/v1beta1 39 | blockOwnerDeletion: true 40 | controller: true 41 | kind: XR 42 | name: example 43 | uid: "" 44 | --- 45 | apiVersion: example/v1alpha1 46 | kind: Foo 47 | metadata: 48 | annotations: 49 | crossplane.io/composition-resource-name: my-awesome-dev-bucket 50 | generateName: example- 51 | labels: 52 | crossplane.io/composite: example 53 | name: my-awesome-dev-bucket 54 | ownerReferences: 55 | - apiVersion: example.crossplane.io/v1beta1 56 | blockOwnerDeletion: true 57 | controller: true 58 | kind: XR 59 | name: example 60 | uid: "" 61 | 62 | ``` 63 | -------------------------------------------------------------------------------- /examples/default/conditions/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1beta1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | annotations: 19 | "krm.kcl.dev/default_ready": "True" 20 | name: basic 21 | spec: 22 | source: | 23 | oxr = option("params").oxr 24 | 25 | dxr = { 26 | **oxr 27 | } 28 | 29 | conditions = { 30 | apiVersion: "meta.krm.kcl.dev/v1alpha1" 31 | kind: "Conditions" 32 | conditions = [ 33 | { 34 | target: "CompositeAndClaim" 35 | force: False 36 | condition = { 37 | type: "DatabaseReady" 38 | status: "False" 39 | reason: "FailedToCreate" 40 | message: "Encountered an error creating the database" 41 | } 42 | } 43 | ] 44 | 45 | } 46 | items = [ 47 | conditions 48 | dxr 49 | ] 50 | -------------------------------------------------------------------------------- /examples/default/conditions/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/default/conditions/xr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: example.crossplane.io/v1beta1 2 | kind: XR 3 | metadata: 4 | name: example 5 | spec: 6 | count: 1 7 | -------------------------------------------------------------------------------- /examples/default/connection_details/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/default/connection_details/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: example.crossplane.io/v1beta1 16 | kind: XR 17 | metadata: 18 | name: example 19 | status: 20 | dummy: cool-status 21 | --- 22 | apiVersion: iam.aws.upbound.io/v1beta1 23 | kind: User 24 | metadata: 25 | annotations: 26 | crossplane.io/composition-resource-name: test-user-0 27 | generateName: example- 28 | labels: 29 | crossplane.io/composite: example 30 | dummy: foo 31 | testing.upbound.io/example-name: test-user-0 32 | name: test-user-0 33 | ownerReferences: 34 | - apiVersion: example.crossplane.io/v1beta1 35 | blockOwnerDeletion: true 36 | controller: true 37 | kind: XR 38 | name: example 39 | uid: "" 40 | spec: 41 | forProvider: {} 42 | --- 43 | apiVersion: iam.aws.upbound.io/v1beta1 44 | kind: AccessKey 45 | metadata: 46 | annotations: 47 | crossplane.io/composition-resource-name: sample-access-key-0 48 | generateName: example- 49 | labels: 50 | crossplane.io/composite: example 51 | name: sample-access-key-0 52 | ownerReferences: 53 | - apiVersion: example.crossplane.io/v1beta1 54 | blockOwnerDeletion: true 55 | controller: true 56 | kind: XR 57 | name: example 58 | uid: "" 59 | spec: 60 | forProvider: 61 | userSelector: 62 | matchLabels: 63 | testing.upbound.io/example-name: test-user-0 64 | writeConnectionSecretToRef: 65 | name: sample-access-key-secret-0 66 | namespace: crossplane-system 67 | --- 68 | dcds: 69 | sample-access-key-0: 70 | Ready: "True" 71 | Resource: 72 | apiVersion: iam.aws.upbound.io/v1beta1 73 | kind: AccessKey 74 | metadata: 75 | annotations: {} 76 | name: sample-access-key-0 77 | spec: 78 | forProvider: 79 | userSelector: 80 | matchLabels: 81 | testing.upbound.io/example-name: test-user-0 82 | writeConnectionSecretToRef: 83 | name: sample-access-key-secret-0 84 | namespace: crossplane-system 85 | test-user-0: 86 | Ready: "False" 87 | Resource: 88 | apiVersion: iam.aws.upbound.io/v1beta1 89 | kind: User 90 | metadata: 91 | annotations: {} 92 | labels: 93 | dummy: foo 94 | testing.upbound.io/example-name: test-user-0 95 | name: test-user-0 96 | spec: 97 | forProvider: {} 98 | metadata: 99 | annotations: 100 | crossplane.io/composition-resource-name: dcds 101 | generateName: example- 102 | labels: 103 | crossplane.io/composite: example 104 | name: dcds 105 | ownerReferences: 106 | - apiVersion: example.crossplane.io/v1beta1 107 | blockOwnerDeletion: true 108 | controller: true 109 | kind: XR 110 | name: example 111 | uid: "" 112 | ``` 113 | -------------------------------------------------------------------------------- /examples/default/connection_details/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | annotations: 19 | "krm.kcl.dev/default_ready": "True" 20 | name: basic 21 | spec: 22 | source: | 23 | import base64 24 | 25 | oxr = option("params").oxr 26 | count = oxr.spec.count or 1 27 | ocds = option("params").ocds 28 | dxr = { 29 | **option("params").dxr 30 | status.dummy = "cool-status" 31 | } 32 | details = { 33 | apiVersion: "meta.krm.kcl.dev/v1alpha1" 34 | kind: "CompositeConnectionDetails" 35 | if "sample-access-key-0" in ocds: 36 | data: { 37 | username = ocds["sample-access-key-0"].ConnectionDetails.username 38 | password = ocds["sample-access-key-0"].ConnectionDetails.password 39 | url = base64.encode("http://www.example.com") 40 | } 41 | else: 42 | data: {} 43 | } 44 | items = sum([[{ 45 | apiVersion: "iam.aws.upbound.io/v1beta1" 46 | kind: "User" 47 | metadata.name = "test-user-{}".format(i) 48 | metadata.labels: { 49 | "testing.upbound.io/example-name" = "test-user-{}".format(i) 50 | if "test-user-{}".format(i) in ocds: 51 | dummy = ocds["test-user-{}".format(i)].Resource.metadata.labels.dummy 52 | else: 53 | dummy = "foo" 54 | } 55 | metadata.annotations: { 56 | "krm.kcl.dev/ready": "False" 57 | } 58 | spec.forProvider: {} 59 | }, { 60 | apiVersion: "iam.aws.upbound.io/v1beta1" 61 | kind: "AccessKey" 62 | metadata.name = "sample-access-key-{}".format(i) 63 | metadata.annotations: { 64 | "krm.kcl.dev/ready": "True" 65 | } 66 | spec.forProvider.userSelector.matchLabels: { 67 | "testing.upbound.io/example-name" = "test-user-{}".format(i) 68 | } 69 | spec.writeConnectionSecretToRef: { 70 | name: "sample-access-key-secret-{}".format(i) 71 | namespace: "crossplane-system" 72 | } 73 | }] for i in range(count)], []) + [ 74 | details 75 | dxr 76 | ] 77 | 78 | # Just for test 79 | - step: normal1 80 | functionRef: 81 | name: kcl-function 82 | input: 83 | apiVersion: krm.kcl.dev/v1alpha1 84 | kind: KCLInput 85 | metadata: 86 | name: show-dcds 87 | spec: 88 | source: | 89 | { 90 | metadata.name = "dcds" 91 | dcds: option("params").dcds 92 | } 93 | -------------------------------------------------------------------------------- /examples/default/connection_details/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/default/connection_details/xr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: example.crossplane.io/v1beta1 2 | kind: XR 3 | metadata: 4 | name: example 5 | spec: 6 | count: 1 7 | -------------------------------------------------------------------------------- /examples/default/context/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render --verbose xr.yaml composition.yaml functions.yaml -rc 3 | -------------------------------------------------------------------------------- /examples/default/context/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render --verbose xr.yaml composition.yaml functions.yaml -rc 14 | --- 15 | apiVersion: example.crossplane.io/v1beta1 16 | kind: XR 17 | metadata: 18 | name: example 19 | status: 20 | conditions: 21 | - lastTransitionTime: "2024-01-01T00:00:00Z" 22 | reason: Available 23 | status: "True" 24 | type: Ready 25 | --- 26 | apiVersion: render.crossplane.io/v1beta1 27 | fields: 28 | contextField: contextValue 29 | moreComplexField: 30 | test: field 31 | kind: Context 32 | ``` 33 | -------------------------------------------------------------------------------- /examples/default/context/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1beta1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | annotations: 19 | "krm.kcl.dev/default_ready": "True" 20 | name: basic 21 | spec: 22 | source: | 23 | oxr = option("params").oxr 24 | 25 | dxr = { 26 | **oxr 27 | } 28 | 29 | context = { 30 | apiVersion: "meta.krm.kcl.dev/v1alpha1" 31 | kind: "Context" 32 | data = { 33 | contextField = "contextValue" 34 | moreComplexField = { 35 | test: "field" 36 | } 37 | } 38 | } 39 | items = [ 40 | context 41 | dxr 42 | ] 43 | -------------------------------------------------------------------------------- /examples/default/context/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/default/context/xr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: example.crossplane.io/v1beta1 2 | kind: XR 3 | metadata: 4 | name: example 5 | spec: 6 | count: 1 7 | -------------------------------------------------------------------------------- /examples/default/debug_print/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/default/debug_print/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: example.crossplane.io/v1beta1 16 | kind: XR 17 | metadata: 18 | name: example 19 | status: 20 | dummy: cool-status 21 | --- 22 | apiVersion: iam.aws.upbound.io/v1beta1 23 | kind: AccessKey 24 | metadata: 25 | annotations: 26 | crossplane.io/composition-resource-name: sample-access-key-1 27 | generateName: example- 28 | labels: 29 | crossplane.io/composite: example 30 | name: sample-access-key-1 31 | ownerReferences: 32 | - apiVersion: example.crossplane.io/v1beta1 33 | blockOwnerDeletion: true 34 | controller: true 35 | kind: XR 36 | name: example 37 | uid: "" 38 | spec: 39 | forProvider: 40 | userSelector: 41 | matchLabels: 42 | testing.upbound.io/example-name: test-user-1 43 | writeConnectionSecretToRef: 44 | name: sample-access-key-secret-1 45 | namespace: crossplane-system 46 | --- 47 | apiVersion: iam.aws.upbound.io/v1beta1 48 | kind: User 49 | metadata: 50 | annotations: 51 | crossplane.io/composition-resource-name: test-user-0 52 | generateName: example- 53 | labels: 54 | crossplane.io/composite: example 55 | dummy: foo 56 | testing.upbound.io/example-name: test-user-0 57 | name: test-user-0 58 | ownerReferences: 59 | - apiVersion: example.crossplane.io/v1beta1 60 | blockOwnerDeletion: true 61 | controller: true 62 | kind: XR 63 | name: example 64 | uid: "" 65 | spec: 66 | forProvider: {} 67 | --- 68 | apiVersion: iam.aws.upbound.io/v1beta1 69 | kind: AccessKey 70 | metadata: 71 | annotations: 72 | crossplane.io/composition-resource-name: sample-access-key-0 73 | generateName: example- 74 | labels: 75 | crossplane.io/composite: example 76 | name: sample-access-key-0 77 | ownerReferences: 78 | - apiVersion: example.crossplane.io/v1beta1 79 | blockOwnerDeletion: true 80 | controller: true 81 | kind: XR 82 | name: example 83 | uid: "" 84 | spec: 85 | forProvider: 86 | userSelector: 87 | matchLabels: 88 | testing.upbound.io/example-name: test-user-0 89 | writeConnectionSecretToRef: 90 | name: sample-access-key-secret-0 91 | namespace: crossplane-system 92 | --- 93 | apiVersion: iam.aws.upbound.io/v1beta1 94 | kind: User 95 | metadata: 96 | annotations: 97 | crossplane.io/composition-resource-name: test-user-1 98 | generateName: example- 99 | labels: 100 | crossplane.io/composite: example 101 | dummy: foo 102 | testing.upbound.io/example-name: test-user-1 103 | name: test-user-1 104 | ownerReferences: 105 | - apiVersion: example.crossplane.io/v1beta1 106 | blockOwnerDeletion: true 107 | controller: true 108 | kind: XR 109 | name: example 110 | uid: "" 111 | spec: 112 | forProvider: {} 113 | ``` 114 | -------------------------------------------------------------------------------- /examples/default/debug_print/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | source: | 21 | import base64 22 | 23 | oxr = option("params").oxr 24 | count = oxr.spec.count or 1 25 | print("The value of `count` field in the XR is", count) 26 | ocds = option("params").ocds 27 | dxr = { 28 | **option("params").dxr 29 | status.dummy = "cool-status" 30 | } 31 | details = { 32 | apiVersion: "meta.krm.kcl.dev/v1alpha1" 33 | kind: "CompositeConnectionDetails" 34 | if "sample-access-key-0" in ocds: 35 | data: { 36 | username = ocds["sample-access-key-0"].ConnectionDetails.username 37 | password = ocds["sample-access-key-0"].ConnectionDetails.password 38 | url = base64.encode("http://www.example.com") 39 | } 40 | else: 41 | data: {} 42 | } 43 | items = sum([[{ 44 | apiVersion: "iam.aws.upbound.io/v1beta1" 45 | kind: "User" 46 | metadata.name = "test-user-{}".format(i) 47 | metadata.labels: { 48 | "testing.upbound.io/example-name" = "test-user-{}".format(i) 49 | if "test-user-{}".format(i) in ocds: 50 | dummy = ocds["test-user-{}".format(i)].Resource.metadata.labels.dummy 51 | else: 52 | dummy = "foo" 53 | } 54 | spec.forProvider: {} 55 | }, { 56 | apiVersion: "iam.aws.upbound.io/v1beta1" 57 | kind: "AccessKey" 58 | metadata.name = "sample-access-key-{}".format(i) 59 | spec.forProvider.userSelector.matchLabels: { 60 | "testing.upbound.io/example-name" = "test-user-{}".format(i) 61 | } 62 | spec.writeConnectionSecretToRef: { 63 | name: "sample-access-key-secret-{}".format(i) 64 | namespace: "crossplane-system" 65 | } 66 | }] for i in range(count)], []) + [ 67 | details 68 | dxr 69 | ] 70 | -------------------------------------------------------------------------------- /examples/default/debug_print/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/default/debug_print/xr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: example.crossplane.io/v1beta1 2 | kind: XR 3 | metadata: 4 | name: example 5 | spec: 6 | count: 2 7 | -------------------------------------------------------------------------------- /examples/default/dependencies/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/default/dependencies/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: example.crossplane.io/v1beta1 16 | kind: XR 17 | metadata: 18 | name: example 19 | --- 20 | apiVersion: pkg.crossplane.io/v1beta1 21 | kind: DeploymentRuntimeConfig 22 | metadata: 23 | annotations: 24 | crossplane.io/composition-resource-name: provider-helm 25 | generateName: example- 26 | labels: 27 | crossplane.io/composite: example 28 | name: provider-helm 29 | ownerReferences: 30 | - apiVersion: example.crossplane.io/v1beta1 31 | blockOwnerDeletion: true 32 | controller: true 33 | kind: XR 34 | name: example 35 | uid: "" 36 | spec: 37 | serviceAccountTemplate: 38 | metadata: 39 | name: provider-helm 40 | --- 41 | apiVersion: rbac.authorization.k8s.io/v1 42 | kind: ClusterRoleBinding 43 | metadata: 44 | annotations: 45 | crossplane.io/composition-resource-name: provider-helm-cluster-admin 46 | generateName: example- 47 | labels: 48 | crossplane.io/composite: example 49 | name: provider-helm-cluster-admin 50 | ownerReferences: 51 | - apiVersion: example.crossplane.io/v1beta1 52 | blockOwnerDeletion: true 53 | controller: true 54 | kind: XR 55 | name: example 56 | uid: "" 57 | roleRef: 58 | apiGroup: rbac.authorization.k8s.io 59 | kind: ClusterRole 60 | name: provider-helm-cluster-admin 61 | subjects: 62 | - kind: ServiceAccount 63 | name: provider-helm 64 | namespace: crossplane-system 65 | --- 66 | apiVersion: helm.crossplane.io/v1beta1 67 | kind: ProviderConfig 68 | metadata: 69 | annotations: 70 | crossplane.io/composition-resource-name: helm-provider 71 | generateName: example- 72 | labels: 73 | crossplane.io/composite: example 74 | name: helm-provider 75 | ownerReferences: 76 | - apiVersion: example.crossplane.io/v1beta1 77 | blockOwnerDeletion: true 78 | controller: true 79 | kind: XR 80 | name: example 81 | uid: "" 82 | spec: 83 | credentials: 84 | source: InjectedIdentity 85 | ``` 86 | -------------------------------------------------------------------------------- /examples/default/dependencies/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | dependencies: | 21 | k8s = "1.31" 22 | crossplane = "1.16.0" 23 | source: | 24 | import k8s.api.rbac.v1 as k8srbac 25 | import crossplane.v1 as cpv1 26 | import crossplane.v1beta1 as cpv1beta1 27 | 28 | items = [ 29 | cpv1.Provider { 30 | metadata.name = "provider-helm" 31 | spec: { 32 | package = "xpkg.upbound.io/crossplane-contrib/provider-helm:v0.19.0" 33 | runtimeConfigRef.name = "provider-helm" 34 | } 35 | } 36 | cpv1beta1.DeploymentRuntimeConfig { 37 | metadata.name = "provider-helm" 38 | spec: { 39 | serviceAccountTemplate: {metadata.name = "provider-helm"} 40 | } 41 | } 42 | k8srbac.ClusterRoleBinding { 43 | metadata.name = "provider-helm-cluster-admin" 44 | subjects: [{ 45 | kind = "ServiceAccount" 46 | name = "provider-helm" 47 | namespace = "crossplane-system" 48 | }] 49 | roleRef: { 50 | kind = "ClusterRole" 51 | name = "provider-helm-cluster-admin" 52 | apiGroup = "rbac.authorization.k8s.io" 53 | } 54 | } 55 | cpv1beta1.ProviderConfig { 56 | metadata.name = "helm-provider" 57 | spec = { 58 | credentials.source = "InjectedIdentity" 59 | } 60 | } 61 | ] 62 | -------------------------------------------------------------------------------- /examples/default/dependencies/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/default/dependencies/xr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: example.crossplane.io/v1beta1 2 | kind: XR 3 | metadata: 4 | name: example 5 | spec: 6 | count: 1 7 | -------------------------------------------------------------------------------- /examples/default/events/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render --verbose xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/default/events/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render --verbose xr.yaml composition.yaml functions.yaml -r --extra-resources extra_resources.yaml 14 | --- 15 | --- 16 | apiVersion: example.crossplane.io/v1beta1 17 | kind: XR 18 | metadata: 19 | name: example 20 | status: 21 | conditions: 22 | - lastTransitionTime: "2024-01-01T00:00:00Z" 23 | message: 'Unready resources: another-awesome-dev-bucket, my-awesome-dev-bucket' 24 | reason: Creating 25 | status: "False" 26 | type: Ready 27 | --- 28 | apiVersion: example/v1alpha1 29 | kind: Foo 30 | metadata: 31 | annotations: 32 | crossplane.io/composition-resource-name: another-awesome-dev-bucket 33 | generateName: example- 34 | labels: 35 | crossplane.io/composite: example 36 | name: another-awesome-dev-bucket 37 | ownerReferences: 38 | - apiVersion: example.crossplane.io/v1beta1 39 | blockOwnerDeletion: true 40 | controller: true 41 | kind: XR 42 | name: example 43 | uid: "" 44 | --- 45 | apiVersion: example/v1alpha1 46 | kind: Foo 47 | metadata: 48 | annotations: 49 | crossplane.io/composition-resource-name: my-awesome-dev-bucket 50 | generateName: example- 51 | labels: 52 | crossplane.io/composite: example 53 | name: my-awesome-dev-bucket 54 | ownerReferences: 55 | - apiVersion: example.crossplane.io/v1beta1 56 | blockOwnerDeletion: true 57 | controller: true 58 | kind: XR 59 | name: example 60 | uid: "" 61 | 62 | ``` 63 | -------------------------------------------------------------------------------- /examples/default/events/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1beta1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | annotations: 19 | "krm.kcl.dev/default_ready": "True" 20 | name: basic 21 | spec: 22 | source: | 23 | oxr = option("params").oxr 24 | 25 | dxr = { 26 | **oxr 27 | } 28 | 29 | events = { 30 | apiVersion: "meta.krm.kcl.dev/v1alpha1" 31 | kind: "Events" 32 | events = [ 33 | { 34 | target: "CompositeAndClaim" 35 | event = { 36 | type: "Warning" 37 | reason: "ResourceLimitExceeded" 38 | message: "The resource limit has been exceeded" 39 | } 40 | } 41 | ] 42 | } 43 | items = [ 44 | events 45 | dxr 46 | ] 47 | -------------------------------------------------------------------------------- /examples/default/events/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/default/events/xr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: example.crossplane.io/v1beta1 2 | kind: XR 3 | metadata: 4 | name: example 5 | spec: 6 | count: 1 7 | -------------------------------------------------------------------------------- /examples/default/external_deps/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/default/external_deps/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: example.crossplane.io/v1beta1 16 | kind: XR 17 | metadata: 18 | name: example 19 | --- 20 | apiVersion: v1 21 | kind: Pod 22 | metadata: 23 | annotations: 24 | crossplane.io/composition-resource-name: "" 25 | generateName: example- 26 | labels: 27 | crossplane.io/composite: example 28 | ownerReferences: 29 | - apiVersion: example.crossplane.io/v1beta1 30 | blockOwnerDeletion: true 31 | controller: true 32 | kind: XR 33 | name: example 34 | uid: "" 35 | spec: 36 | containers: 37 | - name: main 38 | ``` 39 | -------------------------------------------------------------------------------- /examples/default/external_deps/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | dependencies: 21 | k8s = "1.31" 22 | source: | 23 | import k8s.api.core.v1 as k8core 24 | 25 | k8core.Pod { 26 | spec: k8core.PodSpec{ 27 | containers: [{ 28 | name = "main" 29 | }] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/default/external_deps/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/default/external_deps/xr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: example.crossplane.io/v1beta1 2 | kind: XR 3 | metadata: 4 | name: example 5 | spec: 6 | count: 1 7 | -------------------------------------------------------------------------------- /examples/default/extra_resources/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render --verbose xr.yaml composition.yaml functions.yaml -r --extra-resources extra_resources.yaml 3 | -------------------------------------------------------------------------------- /examples/default/extra_resources/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render --verbose xr.yaml composition.yaml functions.yaml -r --extra-resources extra_resources.yaml 14 | --- 15 | --- 16 | apiVersion: example.crossplane.io/v1beta1 17 | kind: XR 18 | metadata: 19 | name: example 20 | status: 21 | conditions: 22 | - lastTransitionTime: "2024-01-01T00:00:00Z" 23 | message: 'Unready resources: another-awesome-dev-bucket, my-awesome-dev-bucket' 24 | reason: Creating 25 | status: "False" 26 | type: Ready 27 | --- 28 | apiVersion: example/v1alpha1 29 | kind: Foo 30 | metadata: 31 | annotations: 32 | crossplane.io/composition-resource-name: another-awesome-dev-bucket 33 | generateName: example- 34 | labels: 35 | crossplane.io/composite: example 36 | name: another-awesome-dev-bucket 37 | ownerReferences: 38 | - apiVersion: example.crossplane.io/v1beta1 39 | blockOwnerDeletion: true 40 | controller: true 41 | kind: XR 42 | name: example 43 | uid: "" 44 | --- 45 | apiVersion: example/v1alpha1 46 | kind: Foo 47 | metadata: 48 | annotations: 49 | crossplane.io/composition-resource-name: my-awesome-dev-bucket 50 | generateName: example- 51 | labels: 52 | crossplane.io/composite: example 53 | name: my-awesome-dev-bucket 54 | ownerReferences: 55 | - apiVersion: example.crossplane.io/v1beta1 56 | blockOwnerDeletion: true 57 | controller: true 58 | kind: XR 59 | name: example 60 | uid: "" 61 | 62 | ``` 63 | -------------------------------------------------------------------------------- /examples/default/extra_resources/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1beta1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | annotations: 19 | "krm.kcl.dev/default_ready": "True" 20 | name: basic 21 | spec: 22 | source: | 23 | oxr = option("params").oxr 24 | er = option("params")?.extraResources 25 | 26 | foo = [{ 27 | apiVersion: "example/v1alpha1" 28 | kind: "Foo" 29 | metadata = { 30 | name: k.Resource.metadata.name 31 | } 32 | } for k in er?.bucket] if er?.bucket else [] 33 | 34 | dxr = { 35 | **oxr 36 | } 37 | 38 | details = { 39 | apiVersion: "meta.krm.kcl.dev/v1alpha1" 40 | kind: "ExtraResources" 41 | requirements = { 42 | bucket = { 43 | apiVersion: "s3.aws.upbound.io/v1beta1", 44 | kind: "Bucket", 45 | matchLabels: { 46 | "foo": "bar" 47 | } 48 | } 49 | } 50 | } 51 | items = [ 52 | details 53 | dxr 54 | ] + foo 55 | -------------------------------------------------------------------------------- /examples/default/extra_resources/extra_resources.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: s3.aws.upbound.io/v1beta1 2 | kind: Bucket 3 | metadata: 4 | annotations: 5 | crossplane.io/external-name: my-awesome-dev-bucket 6 | labels: 7 | foo: bar 8 | name: my-awesome-dev-bucket 9 | spec: 10 | forProvider: 11 | region: us-west-1 12 | status: 13 | atProvider: 14 | id: random-bucket-id 15 | --- 16 | apiVersion: s3.aws.upbound.io/v1beta1 17 | kind: Bucket 18 | metadata: 19 | annotations: 20 | crossplane.io/external-name: my-awesome-dev-bucket 21 | labels: 22 | foo: bar 23 | name: another-awesome-dev-bucket 24 | spec: 25 | forProvider: 26 | region: us-west-1 27 | status: 28 | atProvider: 29 | id: random-bucket-id 30 | -------------------------------------------------------------------------------- /examples/default/extra_resources/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/default/extra_resources/xr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: example.crossplane.io/v1beta1 2 | kind: XR 3 | metadata: 4 | name: example 5 | spec: 6 | count: 1 7 | -------------------------------------------------------------------------------- /examples/default/function_ctx/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/default/function_ctx/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: example.crossplane.io/v1beta1 16 | kind: XR 17 | metadata: 18 | name: example 19 | status: 20 | dummy: cool-status 21 | --- 22 | apiVersion: iam.aws.upbound.io/v1beta1 23 | kind: AccessKey 24 | metadata: 25 | annotations: 26 | crossplane.io/composition-resource-name: sample-access-key-1 27 | generateName: example- 28 | labels: 29 | crossplane.io/composite: example 30 | name: sample-access-key-1 31 | ownerReferences: 32 | - apiVersion: example.crossplane.io/v1beta1 33 | blockOwnerDeletion: true 34 | controller: true 35 | kind: XR 36 | name: example 37 | uid: "" 38 | spec: 39 | forProvider: 40 | userSelector: 41 | matchLabels: 42 | testing.upbound.io/example-name: test-user-1 43 | writeConnectionSecretToRef: 44 | name: sample-access-key-secret-1 45 | namespace: crossplane-system 46 | --- 47 | apiVersion: iam.aws.upbound.io/v1beta1 48 | kind: User 49 | metadata: 50 | annotations: 51 | crossplane.io/composition-resource-name: test-user-0 52 | generateName: example- 53 | labels: 54 | crossplane.io/composite: example 55 | dummy: foo 56 | testing.upbound.io/example-name: test-user-0 57 | name: test-user-0 58 | ownerReferences: 59 | - apiVersion: example.crossplane.io/v1beta1 60 | blockOwnerDeletion: true 61 | controller: true 62 | kind: XR 63 | name: example 64 | uid: "" 65 | spec: 66 | forProvider: {} 67 | --- 68 | apiVersion: iam.aws.upbound.io/v1beta1 69 | kind: AccessKey 70 | metadata: 71 | annotations: 72 | crossplane.io/composition-resource-name: sample-access-key-0 73 | generateName: example- 74 | labels: 75 | crossplane.io/composite: example 76 | name: sample-access-key-0 77 | ownerReferences: 78 | - apiVersion: example.crossplane.io/v1beta1 79 | blockOwnerDeletion: true 80 | controller: true 81 | kind: XR 82 | name: example 83 | uid: "" 84 | spec: 85 | forProvider: 86 | userSelector: 87 | matchLabels: 88 | testing.upbound.io/example-name: test-user-0 89 | writeConnectionSecretToRef: 90 | name: sample-access-key-secret-0 91 | namespace: crossplane-system 92 | --- 93 | apiVersion: iam.aws.upbound.io/v1beta1 94 | kind: User 95 | metadata: 96 | annotations: 97 | crossplane.io/composition-resource-name: test-user-1 98 | generateName: example- 99 | labels: 100 | crossplane.io/composite: example 101 | dummy: foo 102 | testing.upbound.io/example-name: test-user-1 103 | name: test-user-1 104 | ownerReferences: 105 | - apiVersion: example.crossplane.io/v1beta1 106 | blockOwnerDeletion: true 107 | controller: true 108 | kind: XR 109 | name: example 110 | uid: "" 111 | spec: 112 | forProvider: {} 113 | ``` 114 | -------------------------------------------------------------------------------- /examples/default/function_ctx/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | source: | 21 | ctx = option("params").ctx 22 | # Show function pipeline's context. 23 | items = [{ctx = ctx}] 24 | -------------------------------------------------------------------------------- /examples/default/function_ctx/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/default/function_ctx/xr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: example.crossplane.io/v1beta1 2 | kind: XR 3 | metadata: 4 | name: example 5 | spec: 6 | count: 2 7 | -------------------------------------------------------------------------------- /examples/default/read_ocds_resource/Makefile: -------------------------------------------------------------------------------- 1 | render: 2 | crossplane render xr.yaml composition.yaml functions.yaml \ 3 | --observed-resources=existing-observed-resources.yaml 4 | -------------------------------------------------------------------------------- /examples/default/read_ocds_resource/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml \ 14 | --observed-resources=existing-observed-resources.yaml 15 | --- 16 | apiVersion: example.crossplane.io/v1beta1 17 | kind: XR 18 | metadata: 19 | name: example 20 | --- 21 | metadata: 22 | annotations: 23 | crossplane.io/composition-resource-name: ocds 24 | generateName: example- 25 | labels: 26 | crossplane.io/composite: example 27 | name: ocds 28 | ownerReferences: 29 | - apiVersion: example.crossplane.io/v1beta1 30 | blockOwnerDeletion: true 31 | controller: true 32 | kind: XR 33 | name: example 34 | uid: "" 35 | spec: 36 | ocds: 37 | test-user: 38 | ConnectionDetails: {} 39 | Resource: 40 | apiVersion: iam.aws.upbound.io/v1beta1 41 | kind: User 42 | metadata: 43 | annotations: 44 | crossplane.io/composition-resource-name: test-user 45 | generateName: example- 46 | labels: 47 | crossplane.io/composite: example 48 | dummy: foo 49 | testing.upbound.io/example-name: test-user 50 | name: test-user 51 | ownerReferences: 52 | - apiVersion: example.crossplane.io/v1beta1 53 | blockOwnerDeletion: true 54 | controller: true 55 | kind: XR 56 | name: example 57 | uid: "" 58 | spec: 59 | forProvider: {} 60 | user_metadata: 61 | annotations: 62 | crossplane.io/composition-resource-name: test-user 63 | generateName: example- 64 | labels: 65 | crossplane.io/composite: example 66 | dummy: foo 67 | testing.upbound.io/example-name: test-user 68 | name: test-user 69 | ownerReferences: 70 | - apiVersion: example.crossplane.io/v1beta1 71 | blockOwnerDeletion: true 72 | controller: true 73 | kind: XR 74 | name: example 75 | uid: "" 76 | ``` 77 | -------------------------------------------------------------------------------- /examples/default/read_ocds_resource/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | 12 | # Just for test 13 | - step: normal 14 | functionRef: 15 | name: kcl-function 16 | input: 17 | apiVersion: krm.kcl.dev/v1alpha1 18 | kind: KCLInput 19 | metadata: 20 | name: show-ocds 21 | spec: 22 | source: | 23 | { 24 | metadata.name = "ocds" 25 | spec.ocds = option("params").ocds 26 | spec.user_kind = option("params").ocds["test-user"]?.Resource.Kind 27 | spec.user_metadata = option("params").ocds["test-user"]?.Resource.metadata 28 | spec.user_status = option("params").ocds["test-user"]?.Resource.status 29 | } 30 | -------------------------------------------------------------------------------- /examples/default/read_ocds_resource/existing-observed-resources.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: iam.aws.upbound.io/v1beta1 2 | kind: User 3 | metadata: 4 | annotations: 5 | crossplane.io/composition-resource-name: test-user 6 | generateName: example- 7 | labels: 8 | crossplane.io/composite: example 9 | dummy: foo 10 | testing.upbound.io/example-name: test-user 11 | name: test-user 12 | ownerReferences: 13 | - apiVersion: example.crossplane.io/v1beta1 14 | blockOwnerDeletion: true 15 | controller: true 16 | kind: XR 17 | name: example 18 | uid: "" 19 | spec: 20 | forProvider: {} 21 | -------------------------------------------------------------------------------- /examples/default/read_ocds_resource/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/default/read_ocds_resource/xr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: example.crossplane.io/v1beta1 2 | kind: XR 3 | metadata: 4 | name: example 5 | spec: 6 | count: 1 7 | -------------------------------------------------------------------------------- /examples/default/resources/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | 4 | run-in-docker: 5 | crossplane render xr.yaml composition.yaml functions.yaml -r 6 | -------------------------------------------------------------------------------- /examples/default/resources/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: example.crossplane.io/v1beta1 16 | kind: XR 17 | metadata: 18 | name: example 19 | status: 20 | dummy: cool-status 21 | --- 22 | apiVersion: iam.aws.upbound.io/v1beta1 23 | kind: AccessKey 24 | metadata: 25 | annotations: 26 | crossplane.io/composition-resource-name: sample-access-key-1 27 | generateName: example- 28 | labels: 29 | crossplane.io/composite: example 30 | name: sample-access-key-1 31 | ownerReferences: 32 | - apiVersion: example.crossplane.io/v1beta1 33 | blockOwnerDeletion: true 34 | controller: true 35 | kind: XR 36 | name: example 37 | uid: "" 38 | spec: 39 | forProvider: 40 | userSelector: 41 | matchLabels: 42 | testing.upbound.io/example-name: test-user-1 43 | writeConnectionSecretToRef: 44 | name: sample-access-key-secret-1 45 | namespace: crossplane-system 46 | --- 47 | apiVersion: iam.aws.upbound.io/v1beta1 48 | kind: User 49 | metadata: 50 | annotations: 51 | crossplane.io/composition-resource-name: test-user-0 52 | generateName: example- 53 | labels: 54 | crossplane.io/composite: example 55 | dummy: foo 56 | testing.upbound.io/example-name: test-user-0 57 | name: test-user-0 58 | ownerReferences: 59 | - apiVersion: example.crossplane.io/v1beta1 60 | blockOwnerDeletion: true 61 | controller: true 62 | kind: XR 63 | name: example 64 | uid: "" 65 | spec: 66 | forProvider: {} 67 | --- 68 | apiVersion: iam.aws.upbound.io/v1beta1 69 | kind: AccessKey 70 | metadata: 71 | annotations: 72 | crossplane.io/composition-resource-name: sample-access-key-0 73 | generateName: example- 74 | labels: 75 | crossplane.io/composite: example 76 | name: sample-access-key-0 77 | ownerReferences: 78 | - apiVersion: example.crossplane.io/v1beta1 79 | blockOwnerDeletion: true 80 | controller: true 81 | kind: XR 82 | name: example 83 | uid: "" 84 | spec: 85 | forProvider: 86 | userSelector: 87 | matchLabels: 88 | testing.upbound.io/example-name: test-user-0 89 | writeConnectionSecretToRef: 90 | name: sample-access-key-secret-0 91 | namespace: crossplane-system 92 | --- 93 | apiVersion: iam.aws.upbound.io/v1beta1 94 | kind: User 95 | metadata: 96 | annotations: 97 | crossplane.io/composition-resource-name: test-user-1 98 | generateName: example- 99 | labels: 100 | crossplane.io/composite: example 101 | dummy: foo 102 | testing.upbound.io/example-name: test-user-1 103 | name: test-user-1 104 | ownerReferences: 105 | - apiVersion: example.crossplane.io/v1beta1 106 | blockOwnerDeletion: true 107 | controller: true 108 | kind: XR 109 | name: example 110 | uid: "" 111 | spec: 112 | forProvider: {} 113 | ``` 114 | -------------------------------------------------------------------------------- /examples/default/resources/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | source: | 21 | import base64 22 | 23 | oxr = option("params").oxr 24 | count = oxr.spec.count or 1 25 | ocds = option("params").ocds 26 | dxr = { 27 | **option("params").dxr 28 | status.dummy = "cool-status" 29 | } 30 | details = { 31 | apiVersion: "meta.krm.kcl.dev/v1alpha1" 32 | kind: "CompositeConnectionDetails" 33 | if "sample-access-key-0" in ocds: 34 | data: { 35 | username = ocds["sample-access-key-0"].ConnectionDetails.username 36 | password = ocds["sample-access-key-0"].ConnectionDetails.password 37 | url = base64.encode("http://www.example.com") 38 | } 39 | else: 40 | data: {} 41 | } 42 | items = sum([[{ 43 | apiVersion: "iam.aws.upbound.io/v1beta1" 44 | kind: "User" 45 | metadata.name = "test-user-{}".format(i) 46 | metadata.labels: { 47 | "testing.upbound.io/example-name" = "test-user-{}".format(i) 48 | if "test-user-{}".format(i) in ocds: 49 | dummy = ocds["test-user-{}".format(i)].Resource.metadata.labels.dummy 50 | else: 51 | dummy = "foo" 52 | } 53 | spec.forProvider: {} 54 | }, { 55 | apiVersion: "iam.aws.upbound.io/v1beta1" 56 | kind: "AccessKey" 57 | metadata.name = "sample-access-key-{}".format(i) 58 | spec.forProvider.userSelector.matchLabels: { 59 | "testing.upbound.io/example-name" = "test-user-{}".format(i) 60 | } 61 | spec.writeConnectionSecretToRef: { 62 | name: "sample-access-key-secret-{}".format(i) 63 | namespace: "crossplane-system" 64 | } 65 | }] for i in range(count)], []) + [ 66 | details 67 | dxr 68 | ] 69 | -------------------------------------------------------------------------------- /examples/default/resources/functions-docker.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | spec: 6 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 7 | -------------------------------------------------------------------------------- /examples/default/resources/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/default/resources/xr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: example.crossplane.io/v1beta1 2 | kind: XR 3 | metadata: 4 | name: example 5 | spec: 6 | count: 2 7 | -------------------------------------------------------------------------------- /examples/default/resources_without_xr/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/default/resources_without_xr/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: example.crossplane.io/v1beta1 16 | kind: XR 17 | metadata: 18 | name: example 19 | --- 20 | apiVersion: iam.aws.upbound.io/v1beta1 21 | kind: User 22 | metadata: 23 | annotations: 24 | crossplane.io/composition-resource-name: test-user-0 25 | generateName: example- 26 | labels: 27 | crossplane.io/composite: example 28 | dummy: foo 29 | testing.upbound.io/example-name: test-user-0 30 | name: test-user-0 31 | ownerReferences: 32 | - apiVersion: example.crossplane.io/v1beta1 33 | blockOwnerDeletion: true 34 | controller: true 35 | kind: XR 36 | name: example 37 | uid: "" 38 | spec: 39 | forProvider: {} 40 | --- 41 | apiVersion: iam.aws.upbound.io/v1beta1 42 | kind: AccessKey 43 | metadata: 44 | annotations: 45 | crossplane.io/composition-resource-name: sample-access-key-0 46 | generateName: example- 47 | labels: 48 | crossplane.io/composite: example 49 | name: sample-access-key-0 50 | ownerReferences: 51 | - apiVersion: example.crossplane.io/v1beta1 52 | blockOwnerDeletion: true 53 | controller: true 54 | kind: XR 55 | name: example 56 | uid: "" 57 | spec: 58 | forProvider: 59 | userSelector: 60 | matchLabels: 61 | testing.upbound.io/example-name: test-user-0 62 | writeConnectionSecretToRef: 63 | name: sample-access-key-secret-0 64 | namespace: crossplane-system 65 | ``` 66 | -------------------------------------------------------------------------------- /examples/default/resources_without_xr/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | source: | 21 | import base64 22 | 23 | oxr = option("params").oxr 24 | count = oxr.spec.count or 1 25 | ocds = option("params").ocds 26 | dxr = { 27 | **option("params").dxr 28 | status.dummy = "cool-status" 29 | } 30 | details = { 31 | apiVersion: "meta.krm.kcl.dev/v1alpha1" 32 | kind: "CompositeConnectionDetails" 33 | if "sample-access-key-0" in ocds: 34 | data: { 35 | username = ocds["sample-access-key-0"].ConnectionDetails.username 36 | password = ocds["sample-access-key-0"].ConnectionDetails.password 37 | url = base64.encode("http://www.example.com") 38 | } 39 | else: 40 | data: {} 41 | } 42 | items = sum([[{ 43 | apiVersion: "iam.aws.upbound.io/v1beta1" 44 | kind: "User" 45 | metadata.name = "test-user-{}".format(i) 46 | metadata.labels: { 47 | "testing.upbound.io/example-name" = "test-user-{}".format(i) 48 | if "test-user-{}".format(i) in ocds: 49 | dummy = ocds["test-user-{}".format(i)].Resource.metadata.labels.dummy 50 | else: 51 | dummy = "foo" 52 | } 53 | spec.forProvider: {} 54 | }, { 55 | apiVersion: "iam.aws.upbound.io/v1beta1" 56 | kind: "AccessKey" 57 | metadata.name = "sample-access-key-{}".format(i) 58 | spec.forProvider.userSelector.matchLabels: { 59 | "testing.upbound.io/example-name" = "test-user-{}".format(i) 60 | } 61 | spec.writeConnectionSecretToRef: { 62 | name: "sample-access-key-secret-{}".format(i) 63 | namespace: "crossplane-system" 64 | } 65 | }] for i in range(count)], []) + [ 66 | details 67 | ] 68 | -------------------------------------------------------------------------------- /examples/default/resources_without_xr/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/default/resources_without_xr/xr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: example.crossplane.io/v1beta1 2 | kind: XR 3 | metadata: 4 | name: example 5 | spec: 6 | count: 1 7 | -------------------------------------------------------------------------------- /examples/patch_desired/patching/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/patch_desired/patching/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: nopexample.org/v1 16 | kind: XSubnetwork 17 | metadata: 18 | name: test-xrender 19 | --- 20 | apiVersion: v1 21 | kind: XR 22 | metadata: 23 | annotations: 24 | crossplane.io/composition-resource-name: bucket 25 | generateName: test-xrender- 26 | labels: 27 | crossplane.io/composite: test-xrender 28 | ownerReferences: 29 | - apiVersion: nopexample.org/v1 30 | blockOwnerDeletion: true 31 | controller: true 32 | kind: XSubnetwork 33 | name: test-xrender 34 | uid: "" 35 | spec: 36 | forProvider: 37 | network: some-override-network 38 | ``` 39 | -------------------------------------------------------------------------------- /examples/patch_desired/patching/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: generate desired resources 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: kcl-function-1 19 | spec: 20 | source: | 21 | { 22 | apiVersion: "v1" 23 | kind: "XR" 24 | metadata.annotations: { 25 | "krm.kcl.dev/composition-resource-name" = "bucket" 26 | } 27 | spec.forProvider.network: "some-network" 28 | } 29 | - step: patch desired resources 30 | functionRef: 31 | name: kcl-function 32 | input: 33 | apiVersion: krm.kcl.dev/v1alpha1 34 | kind: KCLInput 35 | metadata: 36 | name: kcl-function-2 37 | spec: 38 | target: PatchDesired 39 | source: | 40 | { 41 | metadata.name = "bucket" 42 | spec.forProvider.network: "some-override-network" 43 | } 44 | -------------------------------------------------------------------------------- /examples/patch_desired/patching/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/patch_desired/patching/xr.yaml: -------------------------------------------------------------------------------- 1 | # Replace this with your XR! 2 | apiVersion: nopexample.org/v1 3 | kind: XSubnetwork 4 | metadata: 5 | name: test-xrender 6 | -------------------------------------------------------------------------------- /examples/patch_desired/patching_multiple/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/patch_desired/patching_multiple/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: nopexample.org/v1 16 | kind: XSubnetwork 17 | metadata: 18 | name: test-xrender 19 | --- 20 | apiVersion: v1 21 | kind: XR 22 | metadata: 23 | annotations: 24 | crossplane.io/composition-resource-name: bucket2 25 | generateName: test-xrender- 26 | labels: 27 | crossplane.io/composite: test-xrender 28 | name: bucket2 29 | ownerReferences: 30 | - apiVersion: nopexample.org/v1 31 | blockOwnerDeletion: true 32 | controller: true 33 | kind: XSubnetwork 34 | name: test-xrender 35 | uid: "" 36 | spec: 37 | forProvider: 38 | network: some-override-network2 39 | --- 40 | apiVersion: v1 41 | kind: XR 42 | metadata: 43 | annotations: 44 | crossplane.io/composition-resource-name: bucket1 45 | generateName: test-xrender- 46 | labels: 47 | crossplane.io/composite: test-xrender 48 | ownerReferences: 49 | - apiVersion: nopexample.org/v1 50 | blockOwnerDeletion: true 51 | controller: true 52 | kind: XSubnetwork 53 | name: test-xrender 54 | uid: "" 55 | spec: 56 | forProvider: 57 | network: some-override-network1 58 | ``` 59 | -------------------------------------------------------------------------------- /examples/patch_desired/patching_multiple/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: generate desired resources 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: kcl-function-1 19 | spec: 20 | source: | 21 | items = [{ 22 | apiVersion: "v1" 23 | kind: "XR" 24 | metadata.annotations: { 25 | "krm.kcl.dev/composition-resource-name" = "bucket1" 26 | } 27 | spec.forProvider.network: "some-network" 28 | }, { 29 | apiVersion: "v1" 30 | kind: "XR" 31 | metadata.name = "bucket2" 32 | spec.forProvider.network: "some-network" 33 | }] 34 | - step: patch desired resources 35 | functionRef: 36 | name: kcl-function 37 | input: 38 | apiVersion: krm.kcl.dev/v1alpha1 39 | kind: KCLInput 40 | metadata: 41 | name: kcl-function-2 42 | spec: 43 | target: PatchDesired 44 | source: | 45 | items = [{ 46 | metadata.name = "bucket1" 47 | spec.forProvider.network: "some-override-network1" 48 | }, { 49 | metadata.name = "bucket2" 50 | spec.forProvider.network: "some-override-network2" 51 | }] 52 | -------------------------------------------------------------------------------- /examples/patch_desired/patching_multiple/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/patch_desired/patching_multiple/xr.yaml: -------------------------------------------------------------------------------- 1 | # Replace this with your XR! 2 | apiVersion: nopexample.org/v1 3 | kind: XSubnetwork 4 | metadata: 5 | name: test-xrender 6 | -------------------------------------------------------------------------------- /examples/patch_resources/patching/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/patch_resources/patching/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: nopexample.org/v1 16 | kind: XSubnetwork 17 | metadata: 18 | name: test-xrender 19 | --- 20 | apiVersion: nobu.dev/v1 21 | kind: Bucket 22 | metadata: 23 | annotations: 24 | crossplane.io/composition-resource-name: bucket 25 | nobu.dev/app: someapp 26 | nobu.dev/cueified: "true" 27 | generateName: test-xrender- 28 | labels: 29 | crossplane.io/composite: test-xrender 30 | name: bucket 31 | ownerReferences: 32 | - apiVersion: nopexample.org/v1 33 | blockOwnerDeletion: true 34 | controller: true 35 | kind: XSubnetwork 36 | name: test-xrender 37 | uid: "" 38 | spec: 39 | forProvider: 40 | network: somenetwork 41 | --- 42 | apiVersion: render.crossplane.io/v1beta1 43 | kind: Result 44 | message: created resource "bucket:" 45 | severity: SEVERITY_NORMAL 46 | step: normal 47 | ``` 48 | -------------------------------------------------------------------------------- /examples/patch_resources/patching/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | target: PatchResources 21 | resources: 22 | - name: bucket 23 | base: 24 | apiVersion: nobu.dev/v1 25 | kind: Bucket 26 | metadata: 27 | name: bucket 28 | source: | 29 | { 30 | metadata.name = "bucket" 31 | metadata.annotations: { 32 | "nobu.dev/cueified": "true", 33 | "nobu.dev/app": "someapp", 34 | } 35 | spec.forProvider.network: "somenetwork" 36 | } 37 | -------------------------------------------------------------------------------- /examples/patch_resources/patching/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/patch_resources/patching/xr.yaml: -------------------------------------------------------------------------------- 1 | # Replace this with your XR! 2 | apiVersion: nopexample.org/v1 3 | kind: XSubnetwork 4 | metadata: 5 | name: test-xrender 6 | -------------------------------------------------------------------------------- /examples/patch_resources/patching_multiple/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/patch_resources/patching_multiple/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | crossplane render xr.yaml composition.yaml functions.yaml -r 15 | --- 16 | apiVersion: nopexample.org/v1 17 | kind: XSubnetwork 18 | metadata: 19 | name: test-xrender 20 | --- 21 | apiVersion: nobu.dev/v1 22 | kind: Bucket 23 | metadata: 24 | annotations: 25 | crossplane.io/composition-resource-name: bucket 26 | nobu.dev/app: someapp 27 | nobu.dev/cueified: "true" 28 | generateName: test-xrender- 29 | labels: 30 | crossplane.io/composite: test-xrender 31 | name: bucket 32 | ownerReferences: 33 | - apiVersion: nopexample.org/v1 34 | blockOwnerDeletion: true 35 | controller: true 36 | kind: XSubnetwork 37 | name: test-xrender 38 | uid: "" 39 | spec: 40 | forProvider: 41 | policy: some-bucket-policy 42 | --- 43 | apiVersion: nobu.dev/v1 44 | kind: User 45 | metadata: 46 | annotations: 47 | crossplane.io/composition-resource-name: iam-user 48 | nobu.dev/app: someapp 49 | nobu.dev/cueified: "true" 50 | generateName: test-xrender- 51 | labels: 52 | crossplane.io/composite: test-xrender 53 | name: iam-user 54 | ownerReferences: 55 | - apiVersion: nopexample.org/v1 56 | blockOwnerDeletion: true 57 | controller: true 58 | kind: XSubnetwork 59 | name: test-xrender 60 | uid: "" 61 | spec: 62 | forProvider: 63 | name: somename 64 | --- 65 | apiVersion: nobu.dev/v1 66 | kind: Role 67 | metadata: 68 | annotations: 69 | crossplane.io/composition-resource-name: iam-role 70 | nobu.dev/app: someapp 71 | nobu.dev/cueified: "true" 72 | generateName: test-xrender- 73 | labels: 74 | crossplane.io/composite: test-xrender 75 | name: iam-role 76 | ownerReferences: 77 | - apiVersion: nopexample.org/v1 78 | blockOwnerDeletion: true 79 | controller: true 80 | kind: XSubnetwork 81 | name: test-xrender 82 | uid: "" 83 | spec: 84 | forProvider: 85 | policy: some-role-policy 86 | --- 87 | apiVersion: render.crossplane.io/v1beta1 88 | kind: Result 89 | message: created resource "bucket:" 90 | severity: SEVERITY_NORMAL 91 | step: normal 92 | --- 93 | apiVersion: render.crossplane.io/v1beta1 94 | kind: Result 95 | message: created resource "iam-role:" 96 | severity: SEVERITY_NORMAL 97 | step: normal 98 | --- 99 | apiVersion: render.crossplane.io/v1beta1 100 | kind: Result 101 | message: created resource "iam-user:" 102 | severity: SEVERITY_NORMAL 103 | step: normal 104 | ``` 105 | -------------------------------------------------------------------------------- /examples/patch_resources/patching_multiple/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | target: PatchResources 21 | resources: 22 | - name: bucket 23 | base: 24 | apiVersion: nobu.dev/v1 25 | kind: Bucket 26 | metadata: 27 | name: bucket 28 | - name: iam-user 29 | base: 30 | apiVersion: nobu.dev/v1 31 | kind: User 32 | metadata: 33 | name: iam-user 34 | - name: iam-role 35 | base: 36 | apiVersion: nobu.dev/v1 37 | kind: Role 38 | metadata: 39 | name: iam-role 40 | source: | 41 | items = [ 42 | { 43 | # Target the bucket by name 44 | metadata.name = "bucket" 45 | metadata.annotations: { 46 | "nobu.dev/cueified": "true", 47 | "nobu.dev/app": "someapp", 48 | } 49 | spec.forProvider.policy: "some-bucket-policy" 50 | } 51 | { 52 | # Target the user by name 53 | metadata.name = "iam-user" 54 | metadata.annotations: { 55 | "nobu.dev/cueified": "true", 56 | "nobu.dev/app": "someapp", 57 | } 58 | spec.forProvider.name: "somename" 59 | } 60 | { 61 | # Target the role by name 62 | metadata.name = "iam-role" 63 | metadata.annotations: { 64 | "nobu.dev/cueified": "true", 65 | "nobu.dev/app": "someapp", 66 | } 67 | spec.forProvider.policy: "some-role-policy" 68 | } 69 | ] 70 | -------------------------------------------------------------------------------- /examples/patch_resources/patching_multiple/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/patch_resources/patching_multiple/xr.yaml: -------------------------------------------------------------------------------- 1 | # Replace this with your XR! 2 | apiVersion: nopexample.org/v1 3 | kind: XSubnetwork 4 | metadata: 5 | name: test-xrender 6 | -------------------------------------------------------------------------------- /examples/resources/basic/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/resources/basic/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: example.crossplane.io/v1 16 | kind: XR 17 | metadata: 18 | name: example-xr 19 | --- 20 | apiVersion: ec2.aws.upbound.io/v1beta1 21 | kind: Instance 22 | metadata: 23 | annotations: 24 | crossplane.io/composition-resource-name: instance 25 | generateName: example-xr- 26 | labels: 27 | crossplane.io/composite: example-xr 28 | name: instance 29 | ownerReferences: 30 | - apiVersion: example.crossplane.io/v1 31 | blockOwnerDeletion: true 32 | controller: true 33 | kind: XR 34 | name: example-xr 35 | uid: "" 36 | spec: 37 | forProvider: 38 | ami: ami-0d9858aa3c6322f73 39 | instanceType: t2.micro 40 | region: us-east-2 41 | --- 42 | apiVersion: render.crossplane.io/v1beta1 43 | kind: Result 44 | message: created resource "instance:Instance" 45 | severity: SEVERITY_NORMAL 46 | step: normal 47 | ``` 48 | -------------------------------------------------------------------------------- /examples/resources/basic/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | target: Resources 21 | source: | 22 | { 23 | apiVersion: "ec2.aws.upbound.io/v1beta1" 24 | kind: "Instance" 25 | metadata.name = "instance" 26 | spec.forProvider.ami: "ami-0d9858aa3c6322f73" 27 | spec.forProvider.instanceType: "t2.micro" 28 | spec.forProvider.region: "us-east-2" 29 | } 30 | -------------------------------------------------------------------------------- /examples/resources/basic/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/resources/basic/xr.yaml: -------------------------------------------------------------------------------- 1 | # Replace this with your XR! 2 | apiVersion: example.crossplane.io/v1 3 | kind: XR 4 | metadata: 5 | name: example-xr 6 | spec: {} 7 | -------------------------------------------------------------------------------- /examples/resources/condition/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/resources/condition/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: nopexample.org/v1 16 | kind: XNopResource 17 | metadata: 18 | name: test-xrender 19 | --- 20 | apiVersion: eks.nobu.dev/v1beta 21 | kind: XNodepool 22 | metadata: 23 | annotations: 24 | crossplane.io/composition-resource-name: "" 25 | generateName: test-xrender- 26 | labels: 27 | crossplane.io/composite: test-xrender 28 | ownerReferences: 29 | - apiVersion: nopexample.org/v1 30 | blockOwnerDeletion: true 31 | controller: true 32 | kind: XNopResource 33 | name: test-xrender 34 | uid: "" 35 | spec: 36 | parameters: 37 | autoscaling: 38 | - maxNodeCount: 1 39 | minNodeCount: 1 40 | clusterName: example-injections 41 | region: us-east-2 42 | --- 43 | apiVersion: render.crossplane.io/v1beta1 44 | kind: Result 45 | message: created resource ":XNodepool" 46 | severity: SEVERITY_NORMAL 47 | step: normal 48 | ``` 49 | -------------------------------------------------------------------------------- /examples/resources/condition/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | target: Resources 21 | params: 22 | name: "input-instance" 23 | source: | 24 | env: str = option("params").oxr.spec.provider 25 | items = [ 26 | { 27 | if env == "aws": 28 | apiVersion = "eks.nobu.dev/v1beta" 29 | if env == "gcp": 30 | apiVersion = "gke.nobu.dev/v1beta1" 31 | kind = "XNodepool" 32 | spec.parameters: { 33 | autoscaling: [{ 34 | maxNodeCount: 1 35 | minNodeCount: 1 36 | }] 37 | clusterName: "example-injections" 38 | region: "us-east-2" 39 | } 40 | } 41 | ] 42 | -------------------------------------------------------------------------------- /examples/resources/condition/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/resources/condition/xr.yaml: -------------------------------------------------------------------------------- 1 | # Replace this with your XR! 2 | apiVersion: nopexample.org/v1 3 | kind: XNopResource 4 | metadata: 5 | name: test-xrender 6 | spec: 7 | provider: aws 8 | 9 | -------------------------------------------------------------------------------- /examples/resources/custom_composition_resource_name/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/resources/custom_composition_resource_name/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: example.crossplane.io/v1 16 | kind: XR 17 | metadata: 18 | name: example-xr 19 | status: 20 | conditions: 21 | - lastTransitionTime: "2024-01-01T00:00:00Z" 22 | message: 'Unready resources: custom-composition-resource-name' 23 | reason: Creating 24 | status: "False" 25 | type: Ready 26 | --- 27 | apiVersion: ec2.aws.upbound.io/v1beta1 28 | kind: Instance 29 | metadata: 30 | annotations: 31 | crossplane.io/composition-resource-name: custom-composition-resource-name 32 | generateName: example-xr- 33 | labels: 34 | crossplane.io/composite: example-xr 35 | name: instance 36 | ownerReferences: 37 | - apiVersion: example.crossplane.io/v1 38 | blockOwnerDeletion: true 39 | controller: true 40 | kind: XR 41 | name: example-xr 42 | uid: "" 43 | spec: 44 | forProvider: 45 | ami: ami-0d9858aa3c6322f73 46 | instanceType: t2.micro 47 | region: us-east-2 48 | ``` 49 | -------------------------------------------------------------------------------- /examples/resources/custom_composition_resource_name/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | source: | 21 | { 22 | apiVersion: "ec2.aws.upbound.io/v1beta1" 23 | kind: "Instance" 24 | metadata.name = "instance" 25 | metadata.annotations = { 26 | "krm.kcl.dev/composition-resource-name" = "custom-composition-resource-name" 27 | } 28 | spec.forProvider.ami: "ami-0d9858aa3c6322f73" 29 | spec.forProvider.instanceType: "t2.micro" 30 | spec.forProvider.region: "us-east-2" 31 | } 32 | -------------------------------------------------------------------------------- /examples/resources/custom_composition_resource_name/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/resources/custom_composition_resource_name/xr.yaml: -------------------------------------------------------------------------------- 1 | # Replace this with your XR! 2 | apiVersion: example.crossplane.io/v1 3 | kind: XR 4 | metadata: 5 | name: example-xr 6 | spec: {} 7 | -------------------------------------------------------------------------------- /examples/resources/custom_external_name/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/resources/custom_external_name/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: example.crossplane.io/v1 16 | kind: XR 17 | metadata: 18 | name: example-xr 19 | status: 20 | conditions: 21 | - lastTransitionTime: "2024-01-01T00:00:00Z" 22 | message: 'Unready resources: instance' 23 | reason: Creating 24 | status: "False" 25 | type: Ready 26 | --- 27 | apiVersion: ec2.aws.upbound.io/v1beta1 28 | kind: Instance 29 | metadata: 30 | annotations: 31 | crossplane.io/composition-resource-name: instance 32 | crossplane.io/external-name: custom-external-name 33 | generateName: example-xr- 34 | labels: 35 | crossplane.io/composite: example-xr 36 | name: instance 37 | ownerReferences: 38 | - apiVersion: example.crossplane.io/v1 39 | blockOwnerDeletion: true 40 | controller: true 41 | kind: XR 42 | name: example-xr 43 | uid: "" 44 | spec: 45 | forProvider: 46 | ami: ami-0d9858aa3c6322f73 47 | instanceType: t2.micro 48 | region: us-east-2 49 | ``` 50 | -------------------------------------------------------------------------------- /examples/resources/custom_external_name/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | source: | 21 | { 22 | apiVersion: "ec2.aws.upbound.io/v1beta1" 23 | kind: "Instance" 24 | metadata.name = "instance" 25 | metadata.annotations = { 26 | "crossplane.io/external-name" = "custom-external-name" 27 | } 28 | spec.forProvider.ami: "ami-0d9858aa3c6322f73" 29 | spec.forProvider.instanceType: "t2.micro" 30 | spec.forProvider.region: "us-east-2" 31 | } 32 | -------------------------------------------------------------------------------- /examples/resources/custom_external_name/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/resources/custom_external_name/xr.yaml: -------------------------------------------------------------------------------- 1 | # Replace this with your XR! 2 | apiVersion: example.crossplane.io/v1 3 | kind: XR 4 | metadata: 5 | name: example-xr 6 | spec: {} 7 | -------------------------------------------------------------------------------- /examples/resources/database/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/resources/database/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: fn-demo.crossplane.io/v1alpha1 16 | kind: Database 17 | metadata: 18 | name: database-test-functions 19 | --- 20 | apiVersion: sql.gcp.upbound.io/v1beta1 21 | kind: DatabaseInstance 22 | metadata: 23 | annotations: 24 | crossplane.io/composition-resource-name: "" 25 | generateName: database-test-functions- 26 | labels: 27 | crossplane.io/composite: database-test-functions 28 | ownerReferences: 29 | - apiVersion: fn-demo.crossplane.io/v1alpha1 30 | blockOwnerDeletion: true 31 | controller: true 32 | kind: Database 33 | name: database-test-functions 34 | uid: "" 35 | spec: 36 | forProvider: 37 | project: test-project 38 | settings: 39 | - databaseFlags: 40 | - name: log_checkpoints 41 | value: "on" 42 | ``` 43 | -------------------------------------------------------------------------------- /examples/resources/database/composition.k: -------------------------------------------------------------------------------- 1 | items = [{ 2 | apiVersion: "sql.gcp.upbound.io/v1beta1" 3 | kind: "DatabaseInstance" 4 | spec: { 5 | forProvider: { 6 | project: "test-project" 7 | settings: [{databaseFlags: [{ 8 | name: "log_checkpoints" 9 | value: "on" 10 | }]}] 11 | } 12 | } 13 | }] 14 | -------------------------------------------------------------------------------- /examples/resources/database/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: xlabels.fn-demo.crossplane.io 5 | labels: 6 | provider: aws 7 | spec: 8 | writeConnectionSecretsToNamespace: crossplane-system 9 | compositeTypeRef: 10 | apiVersion: fn-demo.crossplane.io/v1alpha1 11 | kind: XNetwork 12 | mode: Pipeline 13 | pipeline: 14 | - step: normal 15 | functionRef: 16 | name: kcl-function 17 | input: 18 | apiVersion: krm.kcl.dev/v1alpha1 19 | kind: KCLInput 20 | metadata: 21 | name: basic 22 | spec: 23 | target: Resources 24 | source: | 25 | items = [{ 26 | apiVersion: "sql.gcp.upbound.io/v1beta1" 27 | kind: "DatabaseInstance" 28 | spec: { 29 | forProvider: { 30 | project: "test-project" 31 | settings: [{databaseFlags: [{ 32 | name: "log_checkpoints" 33 | value: "on" 34 | }]}] 35 | } 36 | } 37 | }] 38 | -------------------------------------------------------------------------------- /examples/resources/database/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/resources/database/xr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: fn-demo.crossplane.io/v1alpha1 2 | kind: Database 3 | metadata: 4 | name: database-test-functions 5 | namespace: default 6 | spec: 7 | id: database-test-functions 8 | -------------------------------------------------------------------------------- /examples/resources/format/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/resources/format/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: nopexample.org/v1 16 | kind: XNopResource 17 | metadata: 18 | name: test-xrender 19 | --- 20 | apiVersion: eks.nobu.dev/v1beta 21 | kind: XNodepool 22 | metadata: 23 | annotations: 24 | crossplane.io/composition-resource-name: pool-1 25 | generateName: test-xrender- 26 | labels: 27 | crossplane.io/composite: test-xrender 28 | name: pool-1 29 | ownerReferences: 30 | - apiVersion: nopexample.org/v1 31 | blockOwnerDeletion: true 32 | controller: true 33 | kind: XNopResource 34 | name: test-xrender 35 | uid: "" 36 | spec: 37 | parameters: 38 | autoscaling: 39 | - maxNodeCount: 1 40 | minNodeCount: 1 41 | clusterName: example-injections 42 | region: us-east-2 43 | --- 44 | apiVersion: render.crossplane.io/v1beta1 45 | kind: Result 46 | message: created resource "pool-1:XNodepool" 47 | severity: SEVERITY_NORMAL 48 | step: normal 49 | ``` 50 | -------------------------------------------------------------------------------- /examples/resources/format/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | target: Resources 21 | params: 22 | name_suffix: 1 23 | source: | 24 | name_suffix: str = str(option("params").name_suffix) or "" 25 | name_suffix_with_line: str = "-{}".format(name_suffix) if name_suffix else "" 26 | items = [ 27 | { 28 | apiVersion = "eks.nobu.dev/v1beta" 29 | kind = "XNodepool" 30 | metadata.name = "pool" + name_suffix_with_line 31 | spec.parameters: { 32 | autoscaling: [{ 33 | maxNodeCount: 1 34 | minNodeCount: 1 35 | }] 36 | clusterName: "example-injections" 37 | region: "us-east-2" 38 | } 39 | } 40 | ] 41 | -------------------------------------------------------------------------------- /examples/resources/format/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/resources/format/xr.yaml: -------------------------------------------------------------------------------- 1 | # Replace this with your XR! 2 | apiVersion: nopexample.org/v1 3 | kind: XNopResource 4 | metadata: 5 | name: test-xrender 6 | spec: 7 | provider: aws 8 | 9 | -------------------------------------------------------------------------------- /examples/resources/loop/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/resources/loop/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: example.crossplane.io/v1 16 | kind: XR 17 | metadata: 18 | name: example-xr 19 | --- 20 | apiVersion: ec2.aws.upbound.io/v1beta1 21 | kind: Instance 22 | metadata: 23 | annotations: 24 | crossplane.io/composition-resource-name: basic-instance-us-east-1 25 | generateName: example-xr- 26 | labels: 27 | crossplane.io/composite: example-xr 28 | name: instance-us-east-1 29 | ownerReferences: 30 | - apiVersion: example.crossplane.io/v1 31 | blockOwnerDeletion: true 32 | controller: true 33 | kind: XR 34 | name: example-xr 35 | uid: "" 36 | spec: 37 | forProvider: 38 | ami: ami-0d9858aa3c6322f73 39 | instanceType: t2.micro 40 | region: us-east-1 41 | --- 42 | apiVersion: ec2.aws.upbound.io/v1beta1 43 | kind: Instance 44 | metadata: 45 | annotations: 46 | crossplane.io/composition-resource-name: basic-instance-us-east-2 47 | generateName: example-xr- 48 | labels: 49 | crossplane.io/composite: example-xr 50 | name: instance-us-east-2 51 | ownerReferences: 52 | - apiVersion: example.crossplane.io/v1 53 | blockOwnerDeletion: true 54 | controller: true 55 | kind: XR 56 | name: example-xr 57 | uid: "" 58 | spec: 59 | forProvider: 60 | ami: ami-0d9858aa3c6322f73 61 | instanceType: t2.micro 62 | region: us-east-2 63 | --- 64 | apiVersion: render.crossplane.io/v1beta1 65 | kind: Result 66 | message: created resource "instance-us-east-1:Instance" 67 | severity: SEVERITY_NORMAL 68 | step: normal 69 | --- 70 | apiVersion: render.crossplane.io/v1beta1 71 | kind: Result 72 | message: created resource "instance-us-east-2:Instance" 73 | severity: SEVERITY_NORMAL 74 | step: normal 75 | ``` 76 | -------------------------------------------------------------------------------- /examples/resources/loop/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | target: Resources 21 | params: 22 | name: "input-instance" 23 | source: | 24 | regions = ["us-east-1", "us-east-2"] 25 | items = [{ 26 | apiVersion: "ec2.aws.upbound.io/v1beta1" 27 | kind: "Instance" 28 | metadata.name = "instance-" + r 29 | spec.forProvider: { 30 | ami: "ami-0d9858aa3c6322f73" 31 | instanceType: "t2.micro" 32 | region: r 33 | } 34 | } for r in regions] 35 | -------------------------------------------------------------------------------- /examples/resources/loop/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/resources/loop/xr.yaml: -------------------------------------------------------------------------------- 1 | # Replace this with your XR! 2 | apiVersion: example.crossplane.io/v1 3 | kind: XR 4 | metadata: 5 | name: example-xr 6 | spec: {} 7 | -------------------------------------------------------------------------------- /examples/resources/network/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | 4 | oci: 5 | crossplane render xr.yaml oci_composition.yaml functions.yaml -r 6 | 7 | git: 8 | crossplane render xr.yaml git_composition.yaml functions.yaml -r 9 | 10 | local: 11 | crossplane render xr.yaml local_composition.yaml functions.yaml -r 12 | -------------------------------------------------------------------------------- /examples/resources/network/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: fn-demo.crossplane.io/v1alpha1 16 | kind: Network 17 | metadata: 18 | name: network-test-functions 19 | --- 20 | apiVersion: ec2.aws.upbound.io/v1beta1 21 | kind: InternetGateway 22 | metadata: 23 | annotations: 24 | crossplane.io/composition-resource-name: basic-gateway 25 | generateName: network-test-functions- 26 | labels: 27 | crossplane.io/composite: network-test-functions 28 | networks.meta.fn.crossplane.io/network-id: network-test-functions 29 | name: gateway 30 | ownerReferences: 31 | - apiVersion: fn-demo.crossplane.io/v1alpha1 32 | blockOwnerDeletion: true 33 | controller: true 34 | kind: Network 35 | name: network-test-functions 36 | uid: "" 37 | spec: 38 | forProvider: 39 | region: eu-west-1 40 | vpcIdSelector: 41 | matchControllerRef: true 42 | --- 43 | apiVersion: ec2.aws.upbound.io/v1beta1 44 | kind: VPC 45 | metadata: 46 | annotations: 47 | crossplane.io/composition-resource-name: basic-vpc 48 | generateName: network-test-functions- 49 | labels: 50 | crossplane.io/composite: network-test-functions 51 | networks.meta.fn.crossplane.io/network-id: network-test-functions 52 | name: vpc 53 | ownerReferences: 54 | - apiVersion: fn-demo.crossplane.io/v1alpha1 55 | blockOwnerDeletion: true 56 | controller: true 57 | kind: Network 58 | name: network-test-functions 59 | uid: "" 60 | spec: 61 | forProvider: 62 | cidrBlock: 192.168.0.0/16 63 | enableDnsHostnames: true 64 | enableDnsSupport: true 65 | region: eu-west-1 66 | --- 67 | apiVersion: render.crossplane.io/v1beta1 68 | kind: Result 69 | message: created resource "gateway:InternetGateway" 70 | severity: SEVERITY_NORMAL 71 | step: normal 72 | --- 73 | apiVersion: render.crossplane.io/v1beta1 74 | kind: Result 75 | message: created resource "vpc:VPC" 76 | severity: SEVERITY_NORMAL 77 | step: normal 78 | ``` 79 | -------------------------------------------------------------------------------- /examples/resources/network/composition.k: -------------------------------------------------------------------------------- 1 | # Get the XR spec fields 2 | id = option("params")?.oxr?.spec.id or "" 3 | # Render XR to crossplane managed resources 4 | network_id_labels = {"networks.meta.fn.crossplane.io/network-id" = id} if id else {} 5 | vpc = { 6 | apiVersion = "ec2.aws.upbound.io/v1beta1" 7 | kind = "VPC" 8 | metadata.name = "vpc" 9 | metadata.labels: network_id_labels 10 | spec.forProvider = { 11 | region = "eu-west-1" 12 | cidrBlock = "192.168.0.0/16" 13 | enableDnsSupport = True 14 | enableDnsHostnames = True 15 | } 16 | } 17 | gateway = { 18 | apiVersion = "ec2.aws.upbound.io/v1beta1" 19 | kind = "InternetGateway" 20 | metadata.name = "gateway" 21 | metadata.labels: network_id_labels 22 | spec.forProvider = { 23 | region = "eu-west-1" 24 | vpcIdSelector.matchControllerRef = True 25 | } 26 | } 27 | items = [vpc, gateway] 28 | -------------------------------------------------------------------------------- /examples/resources/network/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: xlabels.fn-demo.crossplane.io 5 | labels: 6 | provider: aws 7 | spec: 8 | writeConnectionSecretsToNamespace: crossplane-system 9 | compositeTypeRef: 10 | apiVersion: fn-demo.crossplane.io/v1alpha1 11 | kind: XNetwork 12 | mode: Pipeline 13 | pipeline: 14 | - step: normal 15 | functionRef: 16 | name: kcl-function 17 | input: 18 | apiVersion: krm.kcl.dev/v1alpha1 19 | kind: KCLInput 20 | metadata: 21 | name: basic 22 | spec: 23 | target: Resources 24 | source: | 25 | # Get the XR spec fields 26 | id = option("params")?.oxr?.spec.id or "" 27 | # Render XR to crossplane managed resources 28 | network_id_labels = {"networks.meta.fn.crossplane.io/network-id" = id} if id else {} 29 | vpc = { 30 | apiVersion = "ec2.aws.upbound.io/v1beta1" 31 | kind = "VPC" 32 | metadata.name = "vpc" 33 | metadata.labels: network_id_labels 34 | spec.forProvider = { 35 | region = "eu-west-1" 36 | cidrBlock = "192.168.0.0/16" 37 | enableDnsSupport = True 38 | enableDnsHostnames = True 39 | } 40 | } 41 | gateway = { 42 | apiVersion = "ec2.aws.upbound.io/v1beta1" 43 | kind = "InternetGateway" 44 | metadata.name = "gateway" 45 | metadata.labels: network_id_labels 46 | spec.forProvider = { 47 | region = "eu-west-1" 48 | vpcIdSelector.matchControllerRef = True 49 | } 50 | } 51 | items = [vpc, gateway] 52 | -------------------------------------------------------------------------------- /examples/resources/network/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/resources/network/git_composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: xlabels.fn-demo.crossplane.io 5 | labels: 6 | provider: aws 7 | spec: 8 | writeConnectionSecretsToNamespace: crossplane-system 9 | compositeTypeRef: 10 | apiVersion: fn-demo.crossplane.io/v1alpha1 11 | kind: XNetwork 12 | mode: Pipeline 13 | pipeline: 14 | - step: normal 15 | functionRef: 16 | name: kcl-function 17 | input: 18 | apiVersion: krm.kcl.dev/v1alpha1 19 | kind: KCLInput 20 | metadata: 21 | name: basic 22 | spec: 23 | target: Resources 24 | source: github.com/kcl-lang/modules/crossplane-xnetwork-kcl-function 25 | -------------------------------------------------------------------------------- /examples/resources/network/local_composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: xlabels.fn-demo.crossplane.io 5 | labels: 6 | provider: aws 7 | spec: 8 | writeConnectionSecretsToNamespace: crossplane-system 9 | compositeTypeRef: 10 | apiVersion: fn-demo.crossplane.io/v1alpha1 11 | kind: XNetwork 12 | mode: Pipeline 13 | pipeline: 14 | - step: normal 15 | functionRef: 16 | name: kcl-function 17 | input: 18 | apiVersion: krm.kcl.dev/v1alpha1 19 | kind: KCLInput 20 | metadata: 21 | name: basic 22 | spec: 23 | target: Resources 24 | source: ./examples/resources/network/composition.k 25 | -------------------------------------------------------------------------------- /examples/resources/network/oci_composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: xlabels.fn-demo.crossplane.io 5 | labels: 6 | provider: aws 7 | spec: 8 | writeConnectionSecretsToNamespace: crossplane-system 9 | compositeTypeRef: 10 | apiVersion: fn-demo.crossplane.io/v1alpha1 11 | kind: XNetwork 12 | mode: Pipeline 13 | pipeline: 14 | - step: normal 15 | functionRef: 16 | name: kcl-function 17 | input: 18 | apiVersion: krm.kcl.dev/v1alpha1 19 | kind: KCLInput 20 | metadata: 21 | name: basic 22 | spec: 23 | target: Resources 24 | source: oci://ghcr.io/kcl-lang/crossplane-xnetwork-kcl-function 25 | -------------------------------------------------------------------------------- /examples/resources/network/xr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: fn-demo.crossplane.io/v1alpha1 2 | kind: Network 3 | metadata: 4 | name: network-test-functions 5 | namespace: default 6 | spec: 7 | id: network-test-functions 8 | -------------------------------------------------------------------------------- /examples/resources/options/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/resources/options/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: example.crossplane.io/v1 16 | kind: XR 17 | metadata: 18 | name: example-xr 19 | --- 20 | apiVersion: ec2.aws.upbound.io/v1beta1 21 | kind: Instance 22 | metadata: 23 | annotations: 24 | crossplane.io/composition-resource-name: input-instance 25 | generateName: example-xr- 26 | labels: 27 | crossplane.io/composite: example-xr 28 | name: input-instance 29 | ownerReferences: 30 | - apiVersion: example.crossplane.io/v1 31 | blockOwnerDeletion: true 32 | controller: true 33 | kind: XR 34 | name: example-xr 35 | uid: "" 36 | spec: 37 | forProvider: 38 | ami: ami-0d9858aa3c6322f73 39 | instanceType: t2.micro 40 | region: us-east-2 41 | --- 42 | apiVersion: render.crossplane.io/v1beta1 43 | kind: Result 44 | message: created resource "input-instance:Instance" 45 | severity: SEVERITY_NORMAL 46 | step: normal 47 | ``` 48 | -------------------------------------------------------------------------------- /examples/resources/options/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | target: Resources 21 | params: 22 | name: "input-instance" 23 | source: | 24 | { 25 | apiVersion: "ec2.aws.upbound.io/v1beta1" 26 | kind: "Instance" 27 | metadata.name = option("params").name 28 | spec.forProvider: { 29 | ami: "ami-0d9858aa3c6322f73" 30 | instanceType: "t2.micro" 31 | region: "us-east-2" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/resources/options/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/resources/options/xr.yaml: -------------------------------------------------------------------------------- 1 | # Replace this with your XR! 2 | apiVersion: example.crossplane.io/v1 3 | kind: XR 4 | metadata: 5 | name: example-xr 6 | spec: {} 7 | -------------------------------------------------------------------------------- /examples/resources/options_oxr/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/resources/options_oxr/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: example.crossplane.io/v1 16 | kind: XR 17 | metadata: 18 | name: example-xr 19 | --- 20 | apiVersion: ec2.aws.upbound.io/v1beta1 21 | kind: Instance 22 | metadata: 23 | annotations: 24 | crossplane.io/composition-resource-name: example-xr 25 | generateName: example-xr- 26 | labels: 27 | crossplane.io/composite: example-xr 28 | name: example-xr 29 | ownerReferences: 30 | - apiVersion: example.crossplane.io/v1 31 | blockOwnerDeletion: true 32 | controller: true 33 | kind: XR 34 | name: example-xr 35 | uid: "" 36 | spec: 37 | forProvider: 38 | ami: ami-0d9858aa3c6322f73 39 | instanceType: t2.micro 40 | region: us-east-2 41 | --- 42 | apiVersion: render.crossplane.io/v1beta1 43 | kind: Result 44 | message: created resource "example-xr:Instance" 45 | severity: SEVERITY_NORMAL 46 | step: normal 47 | ``` 48 | -------------------------------------------------------------------------------- /examples/resources/options_oxr/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | target: Resources 21 | params: 22 | name: "input-instance" 23 | source: | 24 | oxr = option("params").oxr 25 | # Return YAML list and append to dxr. 26 | items = [{ 27 | apiVersion: "ec2.aws.upbound.io/v1beta1" 28 | kind: "Instance" 29 | metadata.name = oxr.metadata.name 30 | spec.forProvider: { 31 | ami: "ami-0d9858aa3c6322f73" 32 | instanceType: "t2.micro" 33 | region: "us-east-2" 34 | } 35 | }] 36 | -------------------------------------------------------------------------------- /examples/resources/options_oxr/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/resources/options_oxr/xr.yaml: -------------------------------------------------------------------------------- 1 | # Replace this with your XR! 2 | apiVersion: example.crossplane.io/v1 3 | kind: XR 4 | metadata: 5 | name: example-xr 6 | spec: {} 7 | -------------------------------------------------------------------------------- /examples/resources/pass_data/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/resources/pass_data/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: nopexample.org/v1 16 | kind: XSubnetwork 17 | metadata: 18 | annotations: 19 | nobu.dev/app: someapp 20 | nobu.dev/cueified: "true" 21 | name: test-xrender 22 | spec: 23 | forProvider: 24 | network: somenetwork 25 | --- 26 | apiVersion: render.crossplane.io/v1beta1 27 | kind: Result 28 | message: updated xr ":XSubnetwork" 29 | severity: SEVERITY_NORMAL 30 | step: normal 31 | ``` 32 | -------------------------------------------------------------------------------- /examples/resources/pass_data/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal1 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | target: Resources 21 | source: | 22 | { 23 | apiVersion: "nopexample.org/v1" 24 | metadata.name = "resource1" 25 | spec.atProvider.network: "somenetwork" 26 | } 27 | - step: normal2 28 | functionRef: 29 | name: kcl-function 30 | input: 31 | apiVersion: krm.kcl.dev/v1alpha1 32 | kind: KCLInput 33 | metadata: 34 | name: basic 35 | spec: 36 | target: Resources 37 | source: | 38 | { 39 | apiVersion: "nopexample.org/v1" 40 | metadata.name = "resource2" 41 | spec.forProvider = option("params").dcds["resource1"].Resource.spec.atProvider 42 | } 43 | -------------------------------------------------------------------------------- /examples/resources/pass_data/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/resources/pass_data/xr.yaml: -------------------------------------------------------------------------------- 1 | # Replace this with your XR! 2 | apiVersion: nopexample.org/v1 3 | kind: XSubnetwork 4 | metadata: 5 | name: test-xrender 6 | -------------------------------------------------------------------------------- /examples/resources/regex/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/resources/regex/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: nopexample.org/v1 16 | kind: XNopResource 17 | metadata: 18 | name: test-xrender 19 | --- 20 | apiVersion: eks.nobu.dev/v1beta 21 | kind: XNodepool 22 | metadata: 23 | annotations: 24 | crossplane.io/composition-resource-name: name 25 | generateName: test-xrender- 26 | labels: 27 | crossplane.io/composite: test-xrender 28 | name: name 29 | ownerReferences: 30 | - apiVersion: nopexample.org/v1 31 | blockOwnerDeletion: true 32 | controller: true 33 | kind: XNopResource 34 | name: test-xrender 35 | uid: "" 36 | spec: 37 | parameters: 38 | autoscaling: 39 | - maxNodeCount: 1 40 | minNodeCount: 1 41 | clusterName: example-injections 42 | region: us-east-2 43 | --- 44 | apiVersion: render.crossplane.io/v1beta1 45 | kind: Result 46 | message: created resource "name:XNodepool" 47 | severity: SEVERITY_NORMAL 48 | step: normal 49 | ``` 50 | -------------------------------------------------------------------------------- /examples/resources/regex/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | target: Resources 21 | params: 22 | name: name 23 | source: | 24 | import regex 25 | 26 | name: str = option("params").name or "" 27 | assert regex.match(name, r"[A-Za-z_][A-Za-z0-9_]*"), "invalid name: ${name}, expected the regex [A-Za-z_][A-Za-z0-9_]*" 28 | items = [ 29 | { 30 | apiVersion = "eks.nobu.dev/v1beta" 31 | kind = "XNodepool" 32 | metadata.name = name 33 | spec.parameters: { 34 | autoscaling: [{ 35 | maxNodeCount: 1 36 | minNodeCount: 1 37 | }] 38 | clusterName: "example-injections" 39 | region: "us-east-2" 40 | } 41 | } 42 | ] 43 | -------------------------------------------------------------------------------- /examples/resources/regex/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/resources/regex/xr.yaml: -------------------------------------------------------------------------------- 1 | # Replace this with your XR! 2 | apiVersion: nopexample.org/v1 3 | kind: XNopResource 4 | metadata: 5 | name: test-xrender 6 | spec: 7 | provider: aws 8 | 9 | -------------------------------------------------------------------------------- /examples/xr/patching/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | crossplane render xr.yaml composition.yaml functions.yaml -r 3 | -------------------------------------------------------------------------------- /examples/xr/patching/README.md: -------------------------------------------------------------------------------- 1 | # Example Manifests 2 | 3 | You can run your function locally and test it using `crossplane render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: nopexample.org/v1 16 | kind: XSubnetwork 17 | metadata: 18 | annotations: 19 | nobu.dev/app: someapp 20 | nobu.dev/cueified: "true" 21 | name: test-xrender 22 | spec: 23 | forProvider: 24 | network: somenetwork 25 | --- 26 | apiVersion: render.crossplane.io/v1beta1 27 | kind: Result 28 | message: updated xr ":XSubnetwork" 29 | severity: SEVERITY_NORMAL 30 | step: normal 31 | ``` 32 | -------------------------------------------------------------------------------- /examples/xr/patching/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-template-go 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: normal 12 | functionRef: 13 | name: kcl-function 14 | input: 15 | apiVersion: krm.kcl.dev/v1alpha1 16 | kind: KCLInput 17 | metadata: 18 | name: basic 19 | spec: 20 | target: XR 21 | source: | 22 | { 23 | metadata.annotations: { 24 | "nobu.dev/cueified": "true", 25 | "nobu.dev/app": "someapp", 26 | } 27 | spec.forProvider.network: "somenetwork" 28 | status.is_ok = True 29 | } 30 | -------------------------------------------------------------------------------- /examples/xr/patching/functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | annotations: 6 | # This tells crossplane render to connect to the function locally. 7 | render.crossplane.io/runtime: Development 8 | spec: 9 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 10 | -------------------------------------------------------------------------------- /examples/xr/patching/xr.yaml: -------------------------------------------------------------------------------- 1 | # Replace this with your XR! 2 | apiVersion: nopexample.org/v1 3 | kind: XSubnetwork 4 | metadata: 5 | name: test-xrender 6 | -------------------------------------------------------------------------------- /examples_kcl/eks/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | kcl composition.k 3 | -------------------------------------------------------------------------------- /examples_kcl/eks/kcl.yaml: -------------------------------------------------------------------------------- 1 | kcl_options: 2 | - key: params 3 | value: 4 | oxr: 5 | apiVersion: devopstoolkitseries.com/v1alpha1 6 | kind: ClusterClaim 7 | metadata: 8 | name: a-team-eks 9 | spec: 10 | claimRef: 11 | apiVersion: devopstoolkitseries.com/v1alpha1 12 | kind: ClusterClaim 13 | name: a-team-eks 14 | compositionRef: 15 | name: cluster-aws 16 | compositionSelector: 17 | matchLabels: 18 | cluster: eks 19 | provider: aws 20 | id: a-team-eks 21 | parameters: 22 | nodeSize: medium 23 | minNodeCount: 3 24 | apps: 25 | crossplane: 26 | enabled: true 27 | openfunction: 28 | enabled: true 29 | externalSecrets: 30 | enabled: true 31 | creds: 32 | name: aws-creds 33 | namespace: crossplane-system 34 | keys: 35 | - access-key-id 36 | - secret-access-key 37 | resourceRef: 38 | apiVersion: devopstoolkitseries.com/v1alpha1 39 | kind: CompositeCluster 40 | -------------------------------------------------------------------------------- /fn.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "os" 8 | 9 | "github.com/crossplane/crossplane-runtime/pkg/errors" 10 | "github.com/crossplane/crossplane-runtime/pkg/logging" 11 | "google.golang.org/protobuf/types/known/structpb" 12 | "k8s.io/apimachinery/pkg/runtime" 13 | "kcl-lang.io/krm-kcl/pkg/api" 14 | "kcl-lang.io/krm-kcl/pkg/api/v1alpha1" 15 | "kcl-lang.io/krm-kcl/pkg/kio" 16 | 17 | fnv1 "github.com/crossplane/function-sdk-go/proto/v1" 18 | "github.com/crossplane/function-sdk-go/request" 19 | "github.com/crossplane/function-sdk-go/response" 20 | 21 | "github.com/crossplane-contrib/function-kcl/input/v1beta1" 22 | pkgresource "github.com/crossplane-contrib/function-kcl/pkg/resource" 23 | "sigs.k8s.io/yaml" 24 | ) 25 | 26 | var defaultSource = os.Getenv("FUNCTION_KCL_DEFAULT_SOURCE") 27 | 28 | // Function returns whatever response you ask it to. 29 | type Function struct { 30 | fnv1.UnimplementedFunctionRunnerServiceServer 31 | 32 | log logging.Logger 33 | dependencies string 34 | } 35 | 36 | // RunFunction runs the Function. 37 | func (f *Function) RunFunction(_ context.Context, req *fnv1.RunFunctionRequest) (*fnv1.RunFunctionResponse, error) { 38 | log := f.log.WithValues("tag", req.GetMeta().GetTag()) 39 | log.Info("Running Function") 40 | 41 | rsp := response.To(req, response.DefaultTTL) 42 | in := &v1beta1.KCLInput{} 43 | if err := request.GetInput(req, in); err != nil { 44 | response.Fatal(rsp, errors.Wrapf(err, "cannot get Function input from %T", req)) 45 | return rsp, nil 46 | } 47 | // Set default source 48 | if in.Spec.Source == "" { 49 | in.Spec.Source = defaultSource 50 | } 51 | // Set default target 52 | if in.Spec.Target == "" { 53 | in.Spec.Target = pkgresource.Default 54 | } 55 | // Set default params 56 | if in.Spec.Params == nil { 57 | in.Spec.Params = make(map[string]runtime.RawExtension) 58 | } 59 | // Add base dependencies 60 | if f.dependencies != "" { 61 | in.Spec.Dependencies = f.dependencies + "\n" + in.Spec.Dependencies 62 | } 63 | if err := in.Validate(); err != nil { 64 | response.Fatal(rsp, errors.Wrap(err, "invalid function input")) 65 | return rsp, nil 66 | } 67 | // The composite resource that actually exists. 68 | oxr, err := request.GetObservedCompositeResource(req) 69 | if err != nil { 70 | response.Fatal(rsp, errors.Wrap(err, "cannot get observed composite resource")) 71 | return rsp, nil 72 | } 73 | // Set option("params").oxr 74 | in.Spec.Params["oxr"], err = pkgresource.UnstructuredToRawExtension(&oxr.Resource.Unstructured) 75 | if err != nil { 76 | response.Fatal(rsp, err) 77 | return rsp, nil 78 | } 79 | log = log.WithValues( 80 | "xr-version", oxr.Resource.GetAPIVersion(), 81 | "xr-kind", oxr.Resource.GetKind(), 82 | "xr-name", oxr.Resource.GetName(), 83 | "target", in.Spec.Target, 84 | ) 85 | 86 | // The composite resource desired by previous functions in the pipeline. 87 | dxr, err := request.GetDesiredCompositeResource(req) 88 | if err != nil { 89 | response.Fatal(rsp, errors.Wrap(err, "cannot get desired composite resource")) 90 | return rsp, nil 91 | } 92 | // Set option("params").dxr 93 | dxr.Resource.SetAPIVersion(oxr.Resource.GetAPIVersion()) 94 | dxr.Resource.SetKind(oxr.Resource.GetKind()) 95 | in.Spec.Params["dxr"], err = pkgresource.UnstructuredToRawExtension(&dxr.Resource.Unstructured) 96 | if err != nil { 97 | response.Fatal(rsp, err) 98 | return rsp, nil 99 | } 100 | // The composed resources desired by any previous Functions in the pipeline. 101 | desired, err := request.GetDesiredComposedResources(req) 102 | if err != nil { 103 | response.Fatal(rsp, errors.Wrapf(err, "cannot get desired composed resources from %T", req)) 104 | return rsp, nil 105 | } 106 | log.Debug(fmt.Sprintf("DesiredComposed resources: %d", len(desired))) 107 | in.Spec.Params["dcds"], err = pkgresource.ObjToRawExtension(desired) 108 | if err != nil { 109 | response.Fatal(rsp, err) 110 | return rsp, nil 111 | } 112 | 113 | // The composed resources desired by any previous Functions in the pipeline. 114 | observed, err := request.GetObservedComposedResources(req) 115 | if err != nil { 116 | response.Fatal(rsp, errors.Wrapf(err, "cannot get observed composed resources from %T", req)) 117 | return rsp, nil 118 | } 119 | log.Debug(fmt.Sprintf("ObservedComposed resources: %d", len(observed))) 120 | in.Spec.Params["ocds"], err = pkgresource.ObjToRawExtension(observed) 121 | if err != nil { 122 | response.Fatal(rsp, err) 123 | return rsp, nil 124 | } 125 | // Set function context 126 | ctxByte, err := req.Context.MarshalJSON() 127 | if err != nil { 128 | response.Fatal(rsp, err) 129 | return rsp, nil 130 | } 131 | ctxObj, err := pkgresource.JsonByteToRawExtension(ctxByte) 132 | if err != nil { 133 | response.Fatal(rsp, err) 134 | return rsp, nil 135 | } 136 | in.Spec.Params["ctx"] = ctxObj 137 | // The extra resources by myself or any previous Functions in the pipeline. 138 | extras, err := request.GetExtraResources(req) 139 | if err != nil { 140 | response.Fatal(rsp, errors.Wrapf(err, "cannot get extra resources from %T", req)) 141 | return rsp, nil 142 | } 143 | log.Debug(fmt.Sprintf("Extra resources: %d", len(extras))) 144 | in.Spec.Params["extraResources"], err = pkgresource.ObjToRawExtension(extras) 145 | if err != nil { 146 | response.Fatal(rsp, err) 147 | return rsp, nil 148 | } 149 | inputBytes, outputBytes := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}) 150 | // Convert the function-kcl KCLInput to the KRM-KCL spec and run function pipelines. 151 | // Input Example: https://github.com/kcl-lang/krm-kcl/blob/main/examples/mutation/set-annotations/suite/good.yaml 152 | in.APIVersion = v1alpha1.KCLRunAPIVersion 153 | in.Kind = api.KCLRunKind 154 | // Note use "sigs.k8s.io/yaml" here. 155 | kclRunBytes, err := yaml.Marshal(in) 156 | if err != nil { 157 | response.Fatal(rsp, errors.Wrap(err, "cannot marshal input to yaml")) 158 | return rsp, nil 159 | } 160 | inputBytes.Write(kclRunBytes) 161 | // Run pipeline to get the result mutated or validated by the KCL source. 162 | pipeline := kio.NewPipeline(inputBytes, outputBytes, false) 163 | 164 | if err := pipeline.Execute(); err != nil { 165 | response.Fatal(rsp, errors.Wrap(err, "failed to run kcl function pipelines")) 166 | return rsp, nil 167 | } 168 | log.Debug(fmt.Sprintf("Pipeline output: %v", outputBytes.String())) 169 | data, err := pkgresource.DataResourcesFromYaml(outputBytes.Bytes()) 170 | if err != nil { 171 | response.Fatal(rsp, errors.Wrapf(err, "cannot parse data resources from the pipeline output in %T", rsp)) 172 | return rsp, nil 173 | } 174 | log.Debug(fmt.Sprintf("Pipeline data: %v", data)) 175 | 176 | var resources pkgresource.ResourceList 177 | for _, r := range in.Spec.Resources { 178 | base, err := pkgresource.JsonByteToUnstructured(r.Base.Raw) 179 | if err != nil { 180 | response.Fatal(rsp, errors.Wrapf(err, "cannot parse data resources from the pipeline output in %T", rsp)) 181 | return rsp, nil 182 | } 183 | resources = append(resources, pkgresource.Resource{ 184 | Name: r.Name, 185 | Base: *base, 186 | }) 187 | } 188 | log.Debug(fmt.Sprintf("Input resources: %v", resources)) 189 | extraResources := map[string]*fnv1.ResourceSelector{} 190 | var conditions pkgresource.ConditionResources 191 | var events pkgresource.EventResources 192 | contextData := make(map[string]interface{}) 193 | result, err := pkgresource.ProcessResources(dxr, oxr, desired, observed, extraResources, &conditions, &events, &contextData, in.Spec.Target, resources, &pkgresource.AddResourcesOptions{ 194 | Basename: in.Name, 195 | Data: data, 196 | Overwrite: true, 197 | }) 198 | if err != nil { 199 | response.Fatal(rsp, errors.Wrapf(err, "cannot process xr and state with the pipeline output in %T", rsp)) 200 | return rsp, nil 201 | } 202 | if len(extraResources) > 0 { 203 | for n, d := range extraResources { 204 | log.Debug(fmt.Sprintf("Requesting ExtraResources from %s named %s", d.String(), n)) 205 | } 206 | rsp.Requirements = &fnv1.Requirements{ExtraResources: extraResources} 207 | } 208 | 209 | if len(conditions) > 0 { 210 | err := pkgresource.SetConditions(rsp, conditions, log) 211 | if err != nil { 212 | return rsp, nil 213 | } 214 | } 215 | 216 | if len(events) > 0 { 217 | err := pkgresource.SetEvents(rsp, events) 218 | if err != nil { 219 | return rsp, nil 220 | } 221 | } 222 | 223 | if len(contextData) > 0 { 224 | mergedCtx, err := pkgresource.MergeContext(req, contextData) 225 | if err != nil { 226 | response.Fatal(rsp, errors.Wrapf(err, "cannot merge Context")) 227 | return rsp, nil 228 | } 229 | for key, v := range mergedCtx { 230 | vv, err := structpb.NewValue(v) 231 | if err != nil { 232 | response.Fatal(rsp, errors.Wrap(err, "cannot convert value to structpb.Value")) 233 | return rsp, nil 234 | } 235 | f.log.Debug("Updating Composition environment", "key", key, "data", v) 236 | response.SetContextKey(rsp, key, vv) 237 | } 238 | 239 | } 240 | 241 | log.Debug(fmt.Sprintf("Set %d resource(s) to the desired state", result.MsgCount)) 242 | // Set dxr and desired state 243 | log.Debug(fmt.Sprintf("Setting desired XR state to %+v", dxr.Resource)) 244 | if err := response.SetDesiredCompositeResource(rsp, dxr); err != nil { 245 | response.Fatal(rsp, errors.Wrapf(err, "cannot set desired composite resource in %T", rsp)) 246 | return rsp, nil 247 | } 248 | for n, d := range desired { 249 | log.Debug(fmt.Sprintf("Setting DesiredComposed state to %+v named %s", d.Resource, n)) 250 | } 251 | if err := response.SetDesiredComposedResources(rsp, desired); err != nil { 252 | response.Fatal(rsp, errors.Wrapf(err, "cannot set desired composed resources in %T", rsp)) 253 | return rsp, nil 254 | } 255 | log.Info("Successfully processed crossplane KCL function resources", "input", in.Name) 256 | return rsp, nil 257 | } 258 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/crossplane-contrib/function-kcl 2 | 3 | go 1.24.0 4 | 5 | toolchain go1.24.2 6 | 7 | require ( 8 | dario.cat/mergo v1.0.1 9 | github.com/alecthomas/kong v1.10.0 10 | github.com/crossplane/crossplane-runtime v1.20.0 11 | github.com/crossplane/function-sdk-go v0.4.0 12 | github.com/go-logr/logr v1.4.3 13 | github.com/google/go-cmp v0.7.0 14 | github.com/pkg/errors v0.9.1 15 | google.golang.org/protobuf v1.36.6 16 | gopkg.in/yaml.v2 v2.4.0 17 | k8s.io/apimachinery v0.33.1 18 | k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 19 | kcl-lang.io/krm-kcl v0.11.2 20 | sigs.k8s.io/controller-tools v0.17.3 21 | sigs.k8s.io/yaml v1.4.0 22 | ) 23 | 24 | require ( 25 | cel.dev/expr v0.20.0 // indirect 26 | cloud.google.com/go v0.112.1 // indirect 27 | cloud.google.com/go/compute/metadata v0.6.0 // indirect 28 | cloud.google.com/go/iam v1.1.6 // indirect 29 | cloud.google.com/go/storage v1.38.0 // indirect 30 | github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect 31 | github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect 32 | github.com/BurntSushi/toml v1.5.0 // indirect 33 | github.com/Microsoft/go-winio v0.6.2 // indirect 34 | github.com/ProtonMail/go-crypto v1.1.5 // indirect 35 | github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect 36 | github.com/antlr4-go/antlr/v4 v4.13.1 // indirect 37 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect 38 | github.com/aws/aws-sdk-go v1.48.6 // indirect 39 | github.com/bahlo/generic-list-go v0.2.0 // indirect 40 | github.com/beorn7/perks v1.0.1 // indirect 41 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect 42 | github.com/blang/semver/v4 v4.0.0 // indirect 43 | github.com/buger/jsonparser v1.1.1 // indirect 44 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 45 | github.com/chai2010/jsonv v1.1.3 // indirect 46 | github.com/chai2010/protorpc v1.1.4 // indirect 47 | github.com/chainguard-dev/git-urls v1.0.2 // indirect 48 | github.com/cloudflare/circl v1.3.7 // indirect 49 | github.com/containerd/containerd v1.7.22 // indirect 50 | github.com/containerd/errdefs v0.3.0 // indirect 51 | github.com/containerd/log v0.1.0 // indirect 52 | github.com/containerd/platforms v0.2.1 // indirect 53 | github.com/containers/image/v5 v5.34.3 // indirect 54 | github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect 55 | github.com/containers/ocicrypt v1.2.1 // indirect 56 | github.com/containers/storage v1.57.2 // indirect 57 | github.com/cyphar/filepath-securejoin v0.3.6 // indirect 58 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 59 | github.com/dchest/siphash v1.2.3 // indirect 60 | github.com/distribution/reference v0.6.0 // indirect 61 | github.com/docker/cli v27.5.1+incompatible // indirect 62 | github.com/docker/distribution v2.8.3+incompatible // indirect 63 | github.com/docker/docker v27.5.1+incompatible // indirect 64 | github.com/docker/docker-credential-helpers v0.8.2 // indirect 65 | github.com/docker/go-connections v0.5.0 // indirect 66 | github.com/docker/go-metrics v0.0.1 // indirect 67 | github.com/docker/go-units v0.5.0 // indirect 68 | github.com/dominikbraun/graph v0.23.0 // indirect 69 | github.com/ebitengine/purego v0.8.3-0.20250507171810-1638563e3615 // indirect 70 | github.com/elliotchance/orderedmap/v2 v2.7.0 // indirect 71 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect 72 | github.com/emicklei/proto v1.14.1 // indirect 73 | github.com/emirpasic/gods v1.18.1 // indirect 74 | github.com/evanphx/json-patch/v5 v5.9.0 // indirect 75 | github.com/fatih/color v1.18.0 // indirect 76 | github.com/felixge/httpsnoop v1.0.4 // indirect 77 | github.com/fxamacker/cbor/v2 v2.7.0 // indirect 78 | github.com/getkin/kin-openapi v0.132.0 // indirect 79 | github.com/go-errors/errors v1.5.1 // indirect 80 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect 81 | github.com/go-git/go-billy/v5 v5.6.2 // indirect 82 | github.com/go-git/go-git/v5 v5.13.2 // indirect 83 | github.com/go-json-experiment/json v0.0.0-20240815175050-ebd3a8989ca1 // indirect 84 | github.com/go-logr/stdr v1.2.2 // indirect 85 | github.com/go-logr/zapr v1.3.0 // indirect 86 | github.com/go-openapi/analysis v0.23.0 // indirect 87 | github.com/go-openapi/errors v0.22.0 // indirect 88 | github.com/go-openapi/inflect v0.21.0 // indirect 89 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 90 | github.com/go-openapi/jsonreference v0.21.0 // indirect 91 | github.com/go-openapi/loads v0.22.0 // indirect 92 | github.com/go-openapi/spec v0.21.0 // indirect 93 | github.com/go-openapi/strfmt v0.23.0 // indirect 94 | github.com/go-openapi/swag v0.23.0 // indirect 95 | github.com/go-openapi/validate v0.24.0 // indirect 96 | github.com/gobuffalo/flect v1.0.3 // indirect 97 | github.com/goccy/go-yaml v1.17.1 // indirect 98 | github.com/gofrs/flock v0.12.1 // indirect 99 | github.com/gogo/protobuf v1.3.2 // indirect 100 | github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 // indirect 101 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 102 | github.com/golang/protobuf v1.5.4 // indirect 103 | github.com/golang/snappy v0.0.4 // indirect 104 | github.com/google/cel-go v0.22.0 // indirect 105 | github.com/google/gnostic-models v0.6.9 // indirect 106 | github.com/google/s2a-go v0.1.7 // indirect 107 | github.com/google/uuid v1.6.0 // indirect 108 | github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect 109 | github.com/googleapis/gax-go/v2 v2.12.2 // indirect 110 | github.com/gorilla/mux v1.8.1 // indirect 111 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 112 | github.com/hashicorp/go-getter v1.7.8 // indirect 113 | github.com/hashicorp/go-safetemp v1.0.0 // indirect 114 | github.com/hashicorp/go-version v1.7.0 // indirect 115 | github.com/iancoleman/strcase v0.3.0 // indirect 116 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 117 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect 118 | github.com/jinzhu/copier v0.4.0 // indirect 119 | github.com/jmespath/go-jmespath v0.4.0 // indirect 120 | github.com/josharian/intern v1.0.0 // indirect 121 | github.com/json-iterator/go v1.1.12 // indirect 122 | github.com/kevinburke/ssh_config v1.2.0 // indirect 123 | github.com/klauspost/compress v1.17.11 // indirect 124 | github.com/kr/pretty v0.3.1 // indirect 125 | github.com/kr/text v0.2.0 // indirect 126 | github.com/kubescape/go-git-url v0.0.30 // indirect 127 | github.com/mailru/easyjson v0.7.7 // indirect 128 | github.com/mattn/go-colorable v0.1.13 // indirect 129 | github.com/mattn/go-isatty v0.0.20 // indirect 130 | github.com/mitchellh/go-homedir v1.1.0 // indirect 131 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect 132 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect 133 | github.com/mitchellh/mapstructure v1.5.0 // indirect 134 | github.com/moby/locker v1.0.1 // indirect 135 | github.com/moby/sys/capability v0.4.0 // indirect 136 | github.com/moby/sys/mountinfo v0.7.2 // indirect 137 | github.com/moby/sys/user v0.3.0 // indirect 138 | github.com/moby/term v0.5.2 // indirect 139 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 140 | github.com/modern-go/reflect2 v1.0.2 // indirect 141 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect 142 | github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect 143 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 144 | github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect 145 | github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect 146 | github.com/oklog/ulid v1.3.1 // indirect 147 | github.com/opencontainers/go-digest v1.0.0 // indirect 148 | github.com/opencontainers/image-spec v1.1.1 // indirect 149 | github.com/opencontainers/runtime-spec v1.2.0 // indirect 150 | github.com/otiai10/copy v1.14.1 // indirect 151 | github.com/otiai10/mint v1.6.3 // indirect 152 | github.com/perimeterx/marshmallow v1.1.5 // indirect 153 | github.com/pjbgf/sha1cd v0.3.2 // indirect 154 | github.com/prometheus/client_golang v1.20.5 // indirect 155 | github.com/prometheus/client_model v0.6.1 // indirect 156 | github.com/prometheus/common v0.57.0 // indirect 157 | github.com/prometheus/procfs v0.15.1 // indirect 158 | github.com/protocolbuffers/txtpbfmt v0.0.0-20240416193709-1e18ef0a7fdc // indirect 159 | github.com/qri-io/jsonpointer v0.1.1 // indirect 160 | github.com/rogpeppe/go-internal v1.13.1 // indirect 161 | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect 162 | github.com/sirupsen/logrus v1.9.3 // indirect 163 | github.com/skeema/knownhosts v1.3.0 // indirect 164 | github.com/spf13/afero v1.11.0 // indirect 165 | github.com/spf13/cobra v1.9.1 // indirect 166 | github.com/spf13/pflag v1.0.6 // indirect 167 | github.com/stoewer/go-strcase v1.3.0 // indirect 168 | github.com/thoas/go-funk v0.9.3 // indirect 169 | github.com/ulikunitz/xz v0.5.12 // indirect 170 | github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect 171 | github.com/x448/float16 v0.8.4 // indirect 172 | github.com/xanzy/ssh-agent v0.3.3 // indirect 173 | github.com/xlab/treeprint v1.2.0 // indirect 174 | github.com/yuin/goldmark v1.7.11 // indirect 175 | go.mongodb.org/mongo-driver v1.14.0 // indirect 176 | go.opencensus.io v0.24.0 // indirect 177 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect 178 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect 179 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 // indirect 180 | go.opentelemetry.io/otel v1.34.0 // indirect 181 | go.opentelemetry.io/otel/metric v1.34.0 // indirect 182 | go.opentelemetry.io/otel/trace v1.34.0 // indirect 183 | go.uber.org/multierr v1.11.0 // indirect 184 | go.uber.org/zap v1.27.0 // indirect 185 | golang.org/x/crypto v0.36.0 // indirect 186 | golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect 187 | golang.org/x/mod v0.24.0 // indirect 188 | golang.org/x/net v0.38.0 // indirect 189 | golang.org/x/oauth2 v0.27.0 // indirect 190 | golang.org/x/sync v0.12.0 // indirect 191 | golang.org/x/sys v0.32.0 // indirect 192 | golang.org/x/term v0.30.0 // indirect 193 | golang.org/x/text v0.23.0 // indirect 194 | golang.org/x/time v0.9.0 // indirect 195 | golang.org/x/tools v0.31.0 // indirect 196 | google.golang.org/api v0.169.0 // indirect 197 | google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect 198 | google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect 199 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect 200 | google.golang.org/grpc v1.72.0 // indirect 201 | gopkg.in/inf.v0 v0.9.1 // indirect 202 | gopkg.in/warnings.v0 v0.1.2 // indirect 203 | gopkg.in/yaml.v3 v3.0.1 // indirect 204 | k8s.io/api v0.33.1 // indirect 205 | k8s.io/apiextensions-apiserver v0.32.2 // indirect 206 | k8s.io/apiserver v0.32.2 // indirect 207 | k8s.io/client-go v0.33.1 // indirect 208 | k8s.io/component-base v0.32.2 // indirect 209 | k8s.io/klog/v2 v2.130.1 // indirect 210 | k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect 211 | kcl-lang.io/cli v0.11.2 // indirect 212 | kcl-lang.io/kcl-go v0.11.2 // indirect 213 | kcl-lang.io/kcl-openapi v0.10.0 // indirect 214 | kcl-lang.io/kpm v0.11.2 // indirect 215 | kcl-lang.io/lib v0.11.2 // indirect 216 | oras.land/oras-go v1.2.6 // indirect 217 | oras.land/oras-go/v2 v2.5.0 // indirect 218 | sigs.k8s.io/controller-runtime v0.19.4 // indirect 219 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect 220 | sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect 221 | sigs.k8s.io/randfill v1.0.0 // indirect 222 | sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect 223 | ) 224 | -------------------------------------------------------------------------------- /input/generate.go: -------------------------------------------------------------------------------- 1 | //go:build generate 2 | // +build generate 3 | 4 | // NOTE(negz): See the below link for details on what is happening here. 5 | // https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module 6 | 7 | // Remove existing and generate new input manifests 8 | //go:generate rm -rf ../package/input/ 9 | //go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen paths=./v1beta1 object crd:crdVersions=v1 output:artifacts:config=../package/input 10 | 11 | package input 12 | 13 | import ( 14 | _ "sigs.k8s.io/controller-tools/cmd/controller-gen" //nolint:typecheck 15 | ) 16 | -------------------------------------------------------------------------------- /input/v1beta1/input.go: -------------------------------------------------------------------------------- 1 | // Package v1beta1 contains the input type for this Function 2 | // +kubebuilder:object:generate=true 3 | // +groupName=template.fn.crossplane.io 4 | // +versionName=v1beta1 5 | package v1beta1 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/crossplane-contrib/function-kcl/pkg/resource" 11 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | runtime "k8s.io/apimachinery/pkg/runtime" 13 | "k8s.io/apimachinery/pkg/util/validation/field" 14 | ) 15 | 16 | // This isn't a custom resource, in the sense that we never install its CRD. 17 | // It is a KRM-like object, so we generate a CRD to describe its schema. 18 | 19 | // KCLInput can be used to provide input to this Function. 20 | // +kubebuilder:object:root=true 21 | // +kubebuilder:storageversion 22 | // +kubebuilder:resource:categories=crossplane 23 | type KCLInput struct { 24 | metav1.TypeMeta `json:",inline"` 25 | metav1.ObjectMeta `json:"metadata,omitempty"` 26 | 27 | Spec RunSpec `json:"spec,omitempty" yaml:"spec,omitempty"` 28 | } 29 | 30 | func (in *KCLInput) Validate() error { 31 | if in.Spec.Source == "" { 32 | return field.Required(field.NewPath("spec.source"), "kcl source cannot be empty") 33 | } 34 | 35 | switch in.Spec.Target { 36 | // Allowed targets 37 | case resource.Default, resource.PatchDesired, resource.Resources, resource.XR: 38 | case resource.PatchResources: 39 | if len(in.Spec.Resources) == 0 { 40 | return field.Required(field.NewPath("spec.Resources"), fmt.Sprintf("%s target requires at least one resource", resource.PatchResources)) 41 | } 42 | 43 | for i, r := range in.Spec.Resources { 44 | if r.Name == "" { 45 | return field.Required(field.NewPath("spec.Resources").Index(i).Child("name"), "name cannot be empty") 46 | } 47 | if r.Base == nil { 48 | return field.Required(field.NewPath("spec.Resources").Index(i).Child("base"), "base cannot be empty") 49 | } 50 | } 51 | default: 52 | in.Spec.Target = resource.Default 53 | } 54 | 55 | return nil 56 | } 57 | 58 | // RunSpec defines the desired state of Crossplane KCL function. 59 | type RunSpec struct { 60 | // Source is a required field for providing a KCL script inline. 61 | Source string `json:"source" yaml:"source"` 62 | // Config is the compile config. 63 | Config ConfigSpec `json:"config,omitempty" yaml:"config,omitempty"` 64 | // Credentials for remote locations 65 | Credentials CredSpec `json:"credentials,omitempty" yaml:"credentials,omitempty"` 66 | // Dependencies are the external dependencies for the KCL code. 67 | // The format of the `dependencies` field is same as the `[dependencies]` in the `kcl.mod` file 68 | Dependencies string `json:"dependencies,omitempty" yaml:"dependencies,omitempty"` 69 | // Params are the parameters in key-value pairs format. 70 | Params map[string]runtime.RawExtension `json:"params,omitempty" yaml:"params,omitempty"` 71 | // Resources is a list of resources to patch and create 72 | // This is utilized when a Target is set to PatchResources 73 | Resources ResourceList `json:"resources,omitempty"` 74 | // Target determines what object the export output should be applied to 75 | // +kubebuilder:default:=Resources 76 | // +kubebuilder:validation:Enum:=Default;PatchDesired;PatchResources;Resources;XR 77 | Target resource.Target `json:"target"` 78 | } 79 | 80 | // ConfigSpec defines the compile config. 81 | type ConfigSpec struct { 82 | // Arguments is the list of top level dynamic arguments for the kcl option function, e.g., env="prod" 83 | Arguments []string `json:"arguments,omitempty" yaml:"arguments,omitempty"` 84 | // Settings is the list of kcl setting files including all of the CLI config. 85 | Settings []string `json:"settings,omitempty" yaml:"settings,omitempty"` 86 | // Overrides is the list of override paths and values, e.g., app.image="v2" 87 | Overrides []string `json:"overrides,omitempty" yaml:"overrides,omitempty"` 88 | // PathSelectors is the list of path selectors to select output result, e.g., a.b.c 89 | PathSelectors []string `json:"pathSelectors,omitempty" yaml:"pathSelectors,omitempty"` 90 | // Vendor denotes running kcl in the vendor mode. 91 | Vendor bool `json:"vendor,omitempty" yaml:"vendor,omitempty"` 92 | // SortKeys denotes sorting the output result keys, e.g., `{b = 1, a = 2} => {a = 2, b = 1}`. 93 | SortKeys bool `json:"sortKeys,omitempty" yaml:"sortKeys,omitempty"` 94 | // ShowHidden denotes output the hidden attribute in the result. 95 | ShowHidden bool `json:"showHidden,omitempty" yaml:"showHidden,omitempty"` 96 | // DisableNone denotes running kcl and disable dumping None values. 97 | DisableNone bool `json:"disableNone,omitempty" yaml:"disableNone,omitempty"` 98 | // Debug denotes running kcl in debug mode. 99 | Debug bool `json:"debug,omitempty" yaml:"debug,omitempty"` 100 | // StrictRangeCheck performs the 32-bit strict numeric range checks on numbers. 101 | StrictRangeCheck bool `json:"strictRangeCheck,omitempty" yaml:"strictRangeCheck,omitempty"` 102 | } 103 | 104 | // CredSpec defines authentication credentials for remote locations 105 | type CredSpec struct { 106 | Url string `json:"url,omitempty" yaml:"url,omitempty"` 107 | Username string `json:"username" yaml:"username"` 108 | Password string `json:"password" yaml:"password"` 109 | } 110 | 111 | type ResourceList []Resource 112 | 113 | type Resource struct { 114 | // Name is a unique identifier for this entry in a ResourceList 115 | Name string `json:"name"` 116 | // Base of the composed resource that patches will be applied to. 117 | // According to the patches and transforms functions, this may be ommited on 118 | // occassion by a previous pipeline 119 | // +kubebuilder:pruning:PreserveUnknownFields 120 | // +kubebuilder:validation:EmbeddedResource 121 | // +optional 122 | Base *runtime.RawExtension `json:"base,omitempty"` 123 | } 124 | -------------------------------------------------------------------------------- /input/v1beta1/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | 3 | // Code generated by controller-gen. DO NOT EDIT. 4 | 5 | package v1beta1 6 | 7 | import ( 8 | "k8s.io/apimachinery/pkg/runtime" 9 | ) 10 | 11 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 12 | func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { 13 | *out = *in 14 | if in.Arguments != nil { 15 | in, out := &in.Arguments, &out.Arguments 16 | *out = make([]string, len(*in)) 17 | copy(*out, *in) 18 | } 19 | if in.Settings != nil { 20 | in, out := &in.Settings, &out.Settings 21 | *out = make([]string, len(*in)) 22 | copy(*out, *in) 23 | } 24 | if in.Overrides != nil { 25 | in, out := &in.Overrides, &out.Overrides 26 | *out = make([]string, len(*in)) 27 | copy(*out, *in) 28 | } 29 | if in.PathSelectors != nil { 30 | in, out := &in.PathSelectors, &out.PathSelectors 31 | *out = make([]string, len(*in)) 32 | copy(*out, *in) 33 | } 34 | } 35 | 36 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec. 37 | func (in *ConfigSpec) DeepCopy() *ConfigSpec { 38 | if in == nil { 39 | return nil 40 | } 41 | out := new(ConfigSpec) 42 | in.DeepCopyInto(out) 43 | return out 44 | } 45 | 46 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 47 | func (in *CredSpec) DeepCopyInto(out *CredSpec) { 48 | *out = *in 49 | } 50 | 51 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CredSpec. 52 | func (in *CredSpec) DeepCopy() *CredSpec { 53 | if in == nil { 54 | return nil 55 | } 56 | out := new(CredSpec) 57 | in.DeepCopyInto(out) 58 | return out 59 | } 60 | 61 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 62 | func (in *KCLInput) DeepCopyInto(out *KCLInput) { 63 | *out = *in 64 | out.TypeMeta = in.TypeMeta 65 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 66 | in.Spec.DeepCopyInto(&out.Spec) 67 | } 68 | 69 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KCLInput. 70 | func (in *KCLInput) DeepCopy() *KCLInput { 71 | if in == nil { 72 | return nil 73 | } 74 | out := new(KCLInput) 75 | in.DeepCopyInto(out) 76 | return out 77 | } 78 | 79 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 80 | func (in *KCLInput) DeepCopyObject() runtime.Object { 81 | if c := in.DeepCopy(); c != nil { 82 | return c 83 | } 84 | return nil 85 | } 86 | 87 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 88 | func (in *Resource) DeepCopyInto(out *Resource) { 89 | *out = *in 90 | if in.Base != nil { 91 | in, out := &in.Base, &out.Base 92 | *out = new(runtime.RawExtension) 93 | (*in).DeepCopyInto(*out) 94 | } 95 | } 96 | 97 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resource. 98 | func (in *Resource) DeepCopy() *Resource { 99 | if in == nil { 100 | return nil 101 | } 102 | out := new(Resource) 103 | in.DeepCopyInto(out) 104 | return out 105 | } 106 | 107 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 108 | func (in ResourceList) DeepCopyInto(out *ResourceList) { 109 | { 110 | in := &in 111 | *out = make(ResourceList, len(*in)) 112 | for i := range *in { 113 | (*in)[i].DeepCopyInto(&(*out)[i]) 114 | } 115 | } 116 | } 117 | 118 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceList. 119 | func (in ResourceList) DeepCopy() ResourceList { 120 | if in == nil { 121 | return nil 122 | } 123 | out := new(ResourceList) 124 | in.DeepCopyInto(out) 125 | return *out 126 | } 127 | 128 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 129 | func (in *RunSpec) DeepCopyInto(out *RunSpec) { 130 | *out = *in 131 | in.Config.DeepCopyInto(&out.Config) 132 | out.Credentials = in.Credentials 133 | if in.Params != nil { 134 | in, out := &in.Params, &out.Params 135 | *out = make(map[string]runtime.RawExtension, len(*in)) 136 | for key, val := range *in { 137 | (*out)[key] = *val.DeepCopy() 138 | } 139 | } 140 | if in.Resources != nil { 141 | in, out := &in.Resources, &out.Resources 142 | *out = make(ResourceList, len(*in)) 143 | for i := range *in { 144 | (*in)[i].DeepCopyInto(&(*out)[i]) 145 | } 146 | } 147 | } 148 | 149 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunSpec. 150 | func (in *RunSpec) DeepCopy() *RunSpec { 151 | if in == nil { 152 | return nil 153 | } 154 | out := new(RunSpec) 155 | in.DeepCopyInto(out) 156 | return out 157 | } 158 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a Composition Function. 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | 8 | "github.com/alecthomas/kong" 9 | 10 | "github.com/crossplane/function-sdk-go" 11 | ) 12 | 13 | // CLI of this Function. 14 | type CLI struct { 15 | Debug bool `short:"d" help:"Emit debug logs in addition to info logs."` 16 | 17 | Network string `help:"Network on which to listen for gRPC connections." default:"tcp"` 18 | Address string `help:"Address at which to listen for gRPC connections." default:":9443"` 19 | TLSCertsDir string `help:"Directory containing server certs (tls.key, tls.crt) and the CA used to verify client certificates (ca.crt)" env:"TLS_SERVER_CERTS_DIR"` 20 | Dependencies string `help:"File containing dependencies to add to all functions."` 21 | Insecure bool `help:"Run without mTLS credentials. If you supply this flag --tls-server-certs-dir will be ignored."` 22 | } 23 | 24 | // Run this Function. 25 | func (c *CLI) Run() error { 26 | dependencies := "" 27 | if c.Dependencies != "" { 28 | if bytes, err := os.ReadFile(c.Dependencies); err != nil { 29 | return fmt.Errorf("reading %q: %w", c.Dependencies, err) 30 | } else { 31 | dependencies = string(bytes) 32 | } 33 | } 34 | log, err := function.NewLogger(c.Debug) 35 | if err != nil { 36 | return err 37 | } 38 | return function.Serve(&Function{dependencies: dependencies, log: log}, 39 | function.Listen(c.Network, c.Address), 40 | function.MTLSCertificates(c.TLSCertsDir), 41 | function.Insecure(c.Insecure)) 42 | } 43 | 44 | func main() { 45 | ctx := kong.Parse(&CLI{}, kong.Description("A Crossplane Composition Function using KCL.")) 46 | ctx.FatalIfErrorf(ctx.Run()) 47 | } 48 | -------------------------------------------------------------------------------- /manifests/kcl-functions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: kcl-function 5 | spec: 6 | package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest 7 | -------------------------------------------------------------------------------- /package/crossplane.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: meta.pkg.crossplane.io/v1beta1 3 | kind: Function 4 | metadata: 5 | name: function-kcl 6 | annotations: 7 | meta.crossplane.io/maintainer: The KCL Programming Language Team 8 | meta.crossplane.io/source: github.com/crossplane-contrib/function-kcl 9 | meta.crossplane.io/license: Apache-2.0 10 | meta.crossplane.io/description: A KCL programming language composition function 11 | meta.crossplane.io/readme: | 12 | Crossplane KCL function allows developers to use [KCL](https://kcl-lang.io/) (a DSL) 13 | to write composite logic without the need for repeated packaging of crossplane functions, 14 | and we support package management and the [KRM KCL specification](https://github.com/kcl-lang/krm-kcl), 15 | which allows for OCI/Git source and the reuse of [KCL's module ecosystem](https://artifacthub.io/packages/search?org=kcl&sort=relevance&page=1). 16 | spec: {} 17 | -------------------------------------------------------------------------------- /package/input/template.fn.crossplane.io_kclinputs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.17.0 7 | name: kclinputs.template.fn.crossplane.io 8 | spec: 9 | group: template.fn.crossplane.io 10 | names: 11 | categories: 12 | - crossplane 13 | kind: KCLInput 14 | listKind: KCLInputList 15 | plural: kclinputs 16 | singular: kclinput 17 | scope: Namespaced 18 | versions: 19 | - name: v1beta1 20 | schema: 21 | openAPIV3Schema: 22 | description: KCLInput can be used to provide input to this Function. 23 | properties: 24 | apiVersion: 25 | description: |- 26 | APIVersion defines the versioned schema of this representation of an object. 27 | Servers should convert recognized schemas to the latest internal value, and 28 | may reject unrecognized values. 29 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 30 | type: string 31 | kind: 32 | description: |- 33 | Kind is a string value representing the REST resource this object represents. 34 | Servers may infer this from the endpoint the client submits requests to. 35 | Cannot be updated. 36 | In CamelCase. 37 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 38 | type: string 39 | metadata: 40 | type: object 41 | spec: 42 | description: RunSpec defines the desired state of Crossplane KCL function. 43 | properties: 44 | config: 45 | description: Config is the compile config. 46 | properties: 47 | arguments: 48 | description: Arguments is the list of top level dynamic arguments 49 | for the kcl option function, e.g., env="prod" 50 | items: 51 | type: string 52 | type: array 53 | debug: 54 | description: Debug denotes running kcl in debug mode. 55 | type: boolean 56 | disableNone: 57 | description: DisableNone denotes running kcl and disable dumping 58 | None values. 59 | type: boolean 60 | overrides: 61 | description: Overrides is the list of override paths and values, 62 | e.g., app.image="v2" 63 | items: 64 | type: string 65 | type: array 66 | pathSelectors: 67 | description: PathSelectors is the list of path selectors to select 68 | output result, e.g., a.b.c 69 | items: 70 | type: string 71 | type: array 72 | settings: 73 | description: Settings is the list of kcl setting files including 74 | all of the CLI config. 75 | items: 76 | type: string 77 | type: array 78 | showHidden: 79 | description: ShowHidden denotes output the hidden attribute in 80 | the result. 81 | type: boolean 82 | sortKeys: 83 | description: SortKeys denotes sorting the output result keys, 84 | e.g., `{b = 1, a = 2} => {a = 2, b = 1}`. 85 | type: boolean 86 | strictRangeCheck: 87 | description: StrictRangeCheck performs the 32-bit strict numeric 88 | range checks on numbers. 89 | type: boolean 90 | vendor: 91 | description: Vendor denotes running kcl in the vendor mode. 92 | type: boolean 93 | type: object 94 | credentials: 95 | description: Credentials for remote locations 96 | properties: 97 | password: 98 | type: string 99 | url: 100 | type: string 101 | username: 102 | type: string 103 | required: 104 | - password 105 | - username 106 | type: object 107 | dependencies: 108 | description: |- 109 | Dependencies are the external dependencies for the KCL code. 110 | The format of the `dependencies` field is same as the `[dependencies]` in the `kcl.mod` file 111 | type: string 112 | params: 113 | additionalProperties: 114 | type: object 115 | x-kubernetes-preserve-unknown-fields: true 116 | description: Params are the parameters in key-value pairs format. 117 | type: object 118 | resources: 119 | description: |- 120 | Resources is a list of resources to patch and create 121 | This is utilized when a Target is set to PatchResources 122 | items: 123 | properties: 124 | base: 125 | description: |- 126 | Base of the composed resource that patches will be applied to. 127 | According to the patches and transforms functions, this may be ommited on 128 | occassion by a previous pipeline 129 | type: object 130 | x-kubernetes-embedded-resource: true 131 | x-kubernetes-preserve-unknown-fields: true 132 | name: 133 | description: Name is a unique identifier for this entry in a 134 | ResourceList 135 | type: string 136 | required: 137 | - name 138 | type: object 139 | type: array 140 | source: 141 | description: Source is a required field for providing a KCL script 142 | inline. 143 | type: string 144 | target: 145 | default: Resources 146 | description: Target determines what object the export output should 147 | be applied to 148 | enum: 149 | - Default 150 | - PatchDesired 151 | - PatchResources 152 | - Resources 153 | - XR 154 | type: string 155 | required: 156 | - source 157 | - target 158 | type: object 159 | type: object 160 | served: true 161 | storage: true 162 | -------------------------------------------------------------------------------- /pkg/resource/conditions.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" 5 | "github.com/crossplane/crossplane-runtime/pkg/errors" 6 | "github.com/crossplane/crossplane-runtime/pkg/logging" 7 | fnv1 "github.com/crossplane/function-sdk-go/proto/v1" 8 | "github.com/crossplane/function-sdk-go/response" 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | "k8s.io/utils/ptr" 11 | ) 12 | 13 | // Target determines which objects to set the condition on. 14 | type BindingTarget string 15 | 16 | const ( 17 | // TargetComposite targets only the composite resource. 18 | TargetComposite BindingTarget = "Composite" 19 | 20 | // TargetCompositeAndClaim targets both the composite and the claim. 21 | TargetCompositeAndClaim BindingTarget = "CompositeAndClaim" 22 | ) 23 | 24 | type ConditionResources []ConditionResource 25 | 26 | // ConditionResource will set a condition on the target. 27 | type ConditionResource struct { 28 | // The target(s) to receive the condition. Can be Composite or 29 | // CompositeAndClaim. Defaults to Composite 30 | Target *BindingTarget `json:"target"` 31 | // If true, the condition will override a condition of the same Type. Defaults 32 | // to false. 33 | Force *bool `json:"force"` 34 | // Condition to set. 35 | Condition Condition `json:"condition"` 36 | } 37 | 38 | // Condition allows you to specify fields to set on a composite resource and 39 | // claim. 40 | type Condition struct { 41 | // Type of the condition. Required. 42 | Type string `json:"type"` 43 | // Status of the condition. Required. 44 | Status metav1.ConditionStatus `json:"status"` 45 | // Reason of the condition. Required. 46 | Reason string `json:"reason"` 47 | // Message of the condition. Optional. A template can be used. The available 48 | // template variables come from capturing groups in MatchCondition message 49 | // regular expressions. 50 | Message *string `json:"message"` 51 | } 52 | 53 | // transformCondition converts a ConditionResource into an fnv1.Condition while mapping status and target accordingly. 54 | func transformCondition(cs ConditionResource) *fnv1.Condition { 55 | c := &fnv1.Condition{ 56 | Type: cs.Condition.Type, 57 | Reason: cs.Condition.Reason, 58 | Target: transformTarget(cs.Target), 59 | } 60 | 61 | switch cs.Condition.Status { 62 | case metav1.ConditionTrue: 63 | c.Status = fnv1.Status_STATUS_CONDITION_TRUE 64 | case metav1.ConditionFalse: 65 | c.Status = fnv1.Status_STATUS_CONDITION_FALSE 66 | case metav1.ConditionUnknown: 67 | fallthrough 68 | default: 69 | c.Status = fnv1.Status_STATUS_CONDITION_UNKNOWN 70 | } 71 | 72 | c.Message = cs.Condition.Message 73 | 74 | return c 75 | } 76 | 77 | func transformTarget(t *BindingTarget) *fnv1.Target { 78 | target := ptr.Deref(t, TargetComposite) 79 | if target == TargetCompositeAndClaim { 80 | return fnv1.Target_TARGET_COMPOSITE_AND_CLAIM.Enum() 81 | } 82 | return fnv1.Target_TARGET_COMPOSITE.Enum() 83 | } 84 | 85 | // SetConditions updates the RunFunctionResponse with specified conditions from ConditionResources, ensuring no duplicates. 86 | // It validates that system-reserved Crossplane condition types are not set and permits forced updates when specified. 87 | func SetConditions(rsp *fnv1.RunFunctionResponse, cr ConditionResources, log logging.Logger) error { 88 | conditionsSet := map[string]bool{} 89 | // All matchConditions matched, set the desired conditions. 90 | for _, cs := range cr { 91 | if xpv1.IsSystemConditionType(xpv1.ConditionType(cs.Condition.Type)) { 92 | response.Fatal(rsp, errors.Errorf("cannot set ClaimCondition type: %s is a reserved Crossplane Condition", cs.Condition.Type)) 93 | return errors.New("error updating response") 94 | } 95 | if conditionsSet[cs.Condition.Type] && (cs.Force == nil || !*cs.Force) { 96 | // The condition is already set and this setter is not forceful. 97 | log.Debug("skipping because condition is already set and setCondition is not forceful") 98 | continue 99 | } 100 | log.Debug("setting condition") 101 | 102 | c := transformCondition(cs) 103 | rsp.Conditions = append(rsp.Conditions, c) 104 | conditionsSet[cs.Condition.Type] = true 105 | } 106 | return nil 107 | } 108 | -------------------------------------------------------------------------------- /pkg/resource/context.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "dario.cat/mergo" 5 | "github.com/crossplane/function-sdk-go/errors" 6 | fnv1 "github.com/crossplane/function-sdk-go/proto/v1" 7 | ) 8 | 9 | // MergeContext merges existing Context with new values provided 10 | func MergeContext(req *fnv1.RunFunctionRequest, val map[string]interface{}) (map[string]interface{}, error) { 11 | mergedContext := req.GetContext().AsMap() 12 | if len(val) == 0 { 13 | return mergedContext, nil 14 | } 15 | if err := mergo.Merge(&mergedContext, val, mergo.WithOverride); err != nil { 16 | return mergedContext, errors.Wrapf(err, "cannot merge data %T", req) 17 | } 18 | return mergedContext, nil 19 | } 20 | -------------------------------------------------------------------------------- /pkg/resource/context_test.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "testing" 5 | 6 | fnv1 "github.com/crossplane/function-sdk-go/proto/v1" 7 | "github.com/crossplane/function-sdk-go/resource" 8 | "github.com/google/go-cmp/cmp" 9 | "github.com/google/go-cmp/cmp/cmpopts" 10 | "google.golang.org/protobuf/testing/protocmp" 11 | ) 12 | 13 | func TestMergeContext(t *testing.T) { 14 | type args struct { 15 | val map[string]interface{} 16 | req *fnv1.RunFunctionRequest 17 | } 18 | type want struct { 19 | us map[string]any 20 | err error 21 | } 22 | 23 | cases := map[string]struct { 24 | reason string 25 | args args 26 | want want 27 | }{ 28 | "NoContextAtKey": { 29 | reason: "When there is no existing context data at the key to merge, return the value", 30 | args: args{ 31 | req: &fnv1.RunFunctionRequest{ 32 | Context: nil, 33 | }, 34 | val: map[string]interface{}{"hello": "world"}, 35 | }, 36 | want: want{ 37 | us: map[string]interface{}{"hello": "world"}, 38 | err: nil, 39 | }, 40 | }, 41 | "SuccessfulMerge": { 42 | reason: "Confirm that keys are merged with source overwriting destination", 43 | args: args{ 44 | req: &fnv1.RunFunctionRequest{ 45 | Context: resource.MustStructJSON(`{"apiextensions.crossplane.io/environment":{"complex":{"a":"b","c":{"d":"e","f":"1","overWrite": "fromContext"}}}}`), 46 | }, 47 | val: map[string]interface{}{ 48 | "newKey": "newValue", 49 | "apiextensions.crossplane.io/environment": map[string]any{ 50 | "complex": map[string]any{ 51 | "c": map[string]any{ 52 | "overWrite": "fromFunction", 53 | }, 54 | }, 55 | }, 56 | }, 57 | }, 58 | want: want{ 59 | us: map[string]interface{}{ 60 | "apiextensions.crossplane.io/environment": map[string]any{ 61 | "complex": map[string]any{ 62 | "a": "b", 63 | "c": map[string]any{ 64 | "d": "e", 65 | "f": "1", 66 | "overWrite": "fromFunction", 67 | }, 68 | }, 69 | }, 70 | "newKey": "newValue"}, 71 | err: nil, 72 | }, 73 | }, 74 | } 75 | for name, tc := range cases { 76 | t.Run(name, func(t *testing.T) { 77 | rsp, err := MergeContext(tc.args.req, tc.args.val) 78 | 79 | if diff := cmp.Diff(tc.want.us, rsp, protocmp.Transform()); diff != "" { 80 | t.Errorf("%s\nf.MergeContext(...): -want rsp, +got rsp:\n%s", tc.reason, diff) 81 | } 82 | 83 | if diff := cmp.Diff(tc.want.err, err, cmpopts.EquateErrors()); diff != "" { 84 | t.Errorf("%s\nf.RunFunction(...): -want err, +got err:\n%s", tc.reason, diff) 85 | } 86 | }) 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /pkg/resource/events.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "github.com/crossplane/crossplane-runtime/pkg/errors" 5 | fnv1 "github.com/crossplane/function-sdk-go/proto/v1" 6 | "github.com/crossplane/function-sdk-go/response" 7 | "k8s.io/utils/ptr" 8 | ) 9 | 10 | // EventType type of an event. 11 | type EventType string 12 | 13 | const ( 14 | // EventTypeNormal signifies a normal event. 15 | EventTypeNormal EventType = "Normal" 16 | 17 | // EventTypeWarning signifies a warning event. 18 | EventTypeWarning EventType = "Warning" 19 | ) 20 | 21 | type EventResources []CreateEvent 22 | 23 | // Event allows you to specify the fields of an event to create. 24 | type Event struct { 25 | // Type of the event. Optional. Should be either Normal or Warning. 26 | Type *EventType `json:"type"` 27 | // Reason of the event. Optional. 28 | Reason *string `json:"reason"` 29 | // Message of the event. Required. A template can be used. The available 30 | // template variables come from capturing groups in MatchCondition message 31 | // regular expressions. 32 | Message string `json:"message"` 33 | } 34 | 35 | // CreateEvent will create an event for the target(s). 36 | type CreateEvent struct { 37 | // The target(s) to create an event for. Can be Composite or 38 | // CompositeAndClaim. Defaults to Composite 39 | Target *BindingTarget `json:"target"` 40 | 41 | // Event to create. 42 | Event Event `json:"event"` 43 | } 44 | 45 | // SetEvents processes a list of EventResources, transforms them into Results, and appends them to the RunFunctionResponse. 46 | // Returns an error if any transformation fails. 47 | func SetEvents(rsp *fnv1.RunFunctionResponse, ers EventResources) error { 48 | for _, er := range ers { 49 | r, err := transformEvent(er) 50 | if err != nil { 51 | response.Fatal(rsp, err) 52 | return errors.New("error updating response") 53 | } 54 | rsp.Results = append(rsp.Results, r) 55 | } 56 | return nil 57 | } 58 | 59 | // transformEvent converts a CreateEvent into a fnv1.Result object, handling event severity, reason, message, and target. 60 | // Returns a fnv1.Result object or an error if the event type is invalid. 61 | func transformEvent(ec CreateEvent) (*fnv1.Result, error) { 62 | e := &fnv1.Result{ 63 | Reason: ec.Event.Reason, 64 | Target: transformTarget(ec.Target), 65 | } 66 | 67 | deref := ptr.Deref(ec.Event.Type, EventTypeNormal) 68 | switch deref { 69 | case EventTypeNormal: 70 | e.Severity = fnv1.Severity_SEVERITY_NORMAL 71 | break 72 | case EventTypeWarning: 73 | e.Severity = fnv1.Severity_SEVERITY_WARNING 74 | break 75 | default: 76 | return &fnv1.Result{}, errors.Errorf("invalid type %s, must be one of [Normal, Warning]", *ec.Event.Type) 77 | } 78 | 79 | e.Message = ec.Event.Message 80 | return e, nil 81 | } 82 | -------------------------------------------------------------------------------- /pkg/resource/extraresources.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | fnv1 "github.com/crossplane/function-sdk-go/proto/v1" 5 | ) 6 | 7 | // ExtraResourcesRequirements defines the requirements for extra resources. 8 | type ExtraResourcesRequirements map[string]ExtraResourcesRequirement 9 | 10 | // ExtraResourcesRequirement defines a single requirement for extra resources. 11 | // Needed to have camelCase keys instead of the snake_case keys as defined 12 | // through json tags by fnv1.ResourceSelector. 13 | type ExtraResourcesRequirement struct { 14 | // APIVersion of the resource. 15 | APIVersion string `json:"apiVersion"` 16 | // Kind of the resource. 17 | Kind string `json:"kind"` 18 | // MatchLabels defines the labels to match the resource, if defined, 19 | // matchName is ignored. 20 | MatchLabels map[string]string `json:"matchLabels,omitempty"` 21 | // MatchName defines the name to match the resource, if MatchLabels is 22 | // empty. 23 | MatchName string `json:"matchName,omitempty"` 24 | } 25 | 26 | // ToResourceSelector converts the ExtraResourcesRequirement to a fnv1.ResourceSelector. 27 | func (e *ExtraResourcesRequirement) ToResourceSelector() *fnv1.ResourceSelector { 28 | out := &fnv1.ResourceSelector{ 29 | ApiVersion: e.APIVersion, 30 | Kind: e.Kind, 31 | } 32 | if e.MatchName == "" { 33 | out.Match = &fnv1.ResourceSelector_MatchLabels{ 34 | MatchLabels: &fnv1.MatchLabels{Labels: e.MatchLabels}, 35 | } 36 | return out 37 | } 38 | 39 | out.Match = &fnv1.ResourceSelector_MatchName{ 40 | MatchName: e.MatchName, 41 | } 42 | return out 43 | } 44 | -------------------------------------------------------------------------------- /pkg/resource/res_test.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "testing" 5 | 6 | res "github.com/crossplane/function-sdk-go/resource" 7 | "github.com/crossplane/function-sdk-go/resource/composed" 8 | "github.com/google/go-cmp/cmp" 9 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 10 | ) 11 | 12 | func TestObjToRawExtension(t *testing.T) { 13 | u := unstructured.Unstructured{ 14 | Object: map[string]interface{}{ 15 | "apiVersion": "v1", 16 | "kind": "ConfigMap", 17 | "metadata": map[string]interface{}{ 18 | "name": "my-config", 19 | "namespace": "default", 20 | }, 21 | "data": map[string]interface{}{ 22 | "some-key": "some-value", 23 | }, 24 | }, 25 | } 26 | tests := []struct { 27 | input interface{} 28 | expected []byte 29 | wantErr bool 30 | }{ 31 | { 32 | input: nil, 33 | expected: nil, 34 | wantErr: false, 35 | }, 36 | { 37 | input: struct{ Name string }{Name: "test"}, 38 | expected: []byte(`{"Name":"test"}`), 39 | wantErr: false, 40 | }, 41 | { 42 | input: map[string]res.Extra{ 43 | "test": { 44 | Resource: &u, 45 | }, 46 | }, 47 | expected: []byte(`{"test":{"Resource":{"apiVersion":"v1","data":{"some-key":"some-value"},"kind":"ConfigMap","metadata":{"name":"my-config","namespace":"default"}}}}`), 48 | wantErr: false, 49 | }, 50 | { 51 | input: make(chan int), 52 | expected: nil, 53 | wantErr: true, 54 | }, 55 | } 56 | 57 | for _, tt := range tests { 58 | t.Run("", func(t *testing.T) { 59 | got, err := ObjToRawExtension(tt.input) 60 | 61 | if (err != nil) != tt.wantErr { 62 | t.Errorf("ObjToRawExtension() error = %v, wantErr %v", err, tt.wantErr) 63 | return 64 | } 65 | 66 | if !tt.wantErr && !equal(got.Raw, tt.expected) { 67 | t.Errorf("ObjToRawExtension() = %s, want %s", got.Raw, tt.expected) 68 | } 69 | }) 70 | } 71 | } 72 | 73 | func equal(a, b []byte) bool { 74 | return (a == nil && b == nil) || (a != nil && b != nil && string(a) == string(b)) 75 | } 76 | 77 | func TestSetData(t *testing.T) { 78 | type args struct { 79 | data any 80 | path string 81 | o any 82 | overwrite bool 83 | } 84 | tests := []struct { 85 | name string 86 | args args 87 | expected *res.DesiredComposed 88 | wantErr bool 89 | }{ 90 | { 91 | name: "Should create a new element on existing array", 92 | args: args{ 93 | data: "c", 94 | path: ".some-array[2]", 95 | o: &res.DesiredComposed{ 96 | Resource: &composed.Unstructured{ 97 | Unstructured: unstructured.Unstructured{ 98 | Object: map[string]interface{}{ 99 | "some-array": []interface{}{"a", "b"}, 100 | }, 101 | }, 102 | }, 103 | }, 104 | overwrite: true, 105 | }, 106 | expected: &res.DesiredComposed{ 107 | Resource: &composed.Unstructured{ 108 | Unstructured: unstructured.Unstructured{ 109 | Object: map[string]interface{}{ 110 | "some-array": []interface{}{"a", "b", "c"}, 111 | }, 112 | }, 113 | }, 114 | }, 115 | wantErr: false, 116 | }, 117 | } 118 | for _, tt := range tests { 119 | t.Run(tt.name, func(t *testing.T) { 120 | if err := SetData(tt.args.data, tt.args.path, tt.args.o, tt.args.overwrite); (err != nil) != tt.wantErr { 121 | t.Errorf("SetData() error = %v, wantErr %v", err, tt.wantErr) 122 | } 123 | if diff := cmp.Diff(tt.args.o, tt.expected); diff != "" { 124 | t.Errorf("SetData(): -want rsp, +got rsp:\n%s", diff) 125 | } 126 | }) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ], 6 | "crossplane": { 7 | "fileMatch": ["(^|/)example/.*\\.ya?ml$"] 8 | }, 9 | "packageRules": [ 10 | { 11 | "matchManagers": ["crossplane"], 12 | "matchFileNames": ["example/**"], 13 | "groupName": "examples" 14 | } 15 | ], 16 | "postUpdateOptions": [ 17 | "gomodTidy", 18 | "gomodUpdateImportPaths" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /scripts/e2e.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | find examples -type f -name "Makefile" -exec sh -c ' 6 | dir="{}" 7 | cd "$(dirname "$dir")" || exit 8 | make 9 | ' \; 10 | 11 | find examples -type f -name "Makefile" -exec sh -c ' 12 | dir="{}" 13 | cd "$(dirname "$dir")" || exit 14 | make run-in-docker 15 | ' \; 16 | 17 | find examples_kcl -type f -name "Makefile" -exec sh -c ' 18 | dir="{}" 19 | cd "$(dirname "$dir")" || exit 20 | make 21 | ' \; 22 | --------------------------------------------------------------------------------