Compare commits

..

No commits in common. "main" and "v0.2.0-rc.0" have entirely different histories.

31 changed files with 995 additions and 4981 deletions

View File

@ -18,16 +18,16 @@ jobs:
# The main gotchas with this action are that it _only_ supports merge commits,
# and that PRs _must_ be labelled before they're merged to trigger a backport.
open-pr:
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
if: github.event.pull_request.merged
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
fetch-depth: 0
- name: Open Backport PR
uses: zeebe-io/backport-action@be567af183754f6a5d831ae90f648954763f17f5 # v3.1.0
uses: zeebe-io/backport-action@408fae11ed190c2f91bf15d15af01b8f8b45709b # v2.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
github_workspace: ${{ github.workspace }}

View File

@ -10,21 +10,21 @@ on:
env:
# Common versions
GO_VERSION: '1.23.4'
GOLANGCI_VERSION: 'v1.61.0'
GO_VERSION: '1.21.3'
GOLANGCI_VERSION: 'v1.55.1'
jobs:
check-diff:
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
submodules: true
- name: Setup Go
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4
with:
go-version: ${{ env.GO_VERSION }}
@ -33,27 +33,27 @@ jobs:
run: echo "::set-output name=cache::$(make go.cachedir)"
- name: Cache the Go Build Cache
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3
with:
path: ${{ steps.go.outputs.cache }}
key: ${{ runner.os }}-build-check-diff-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-build-check-diff-
- name: Cache Go Dependencies
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3
with:
path: .work/pkg
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-pkg-
- name: Download Go Modules
run: make modules.download modules.check
- name: Vendor Dependencies
run: make vendor vendor.check
- name: Check Diff
run: make check-diff
detect-noop:
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
outputs:
noop: ${{ steps.noop.outputs.should_skip }}
steps:
@ -67,18 +67,18 @@ jobs:
concurrent_skipping: false
lint:
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs: detect-noop
if: needs.detect-noop.outputs.noop != 'true'
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
submodules: true
- name: Setup Go
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4
with:
go-version: ${{ env.GO_VERSION }}
@ -87,44 +87,44 @@ jobs:
run: echo "::set-output name=cache::$(make go.cachedir)"
- name: Cache the Go Build Cache
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3
with:
path: ${{ steps.go.outputs.cache }}
key: ${{ runner.os }}-build-lint-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-build-lint-
- name: Cache Go Dependencies
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3
with:
path: .work/pkg
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-pkg-
- name: Download Go Modules
run: make modules.download modules.check
- name: Vendor Dependencies
run: make vendor vendor.check
# We could run 'make lint' to ensure our desired Go version, but we prefer
# this action because it leaves 'annotations' (i.e. it comments on PRs to
# point out linter violations).
- name: Lint
uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6
uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3
with:
version: ${{ env.GOLANGCI_VERSION }}
skip-cache: true # We do our own caching.
codeql:
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs: detect-noop
if: needs.detect-noop.outputs.noop != 'true'
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
submodules: true
- name: Setup Go
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4
with:
go-version: ${{ env.GO_VERSION }}
@ -133,42 +133,42 @@ jobs:
run: echo "::set-output name=cache::$(make go.cachedir)"
- name: Cache the Go Build Cache
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3
with:
path: ${{ steps.go.outputs.cache }}
key: ${{ runner.os }}-build-check-diff-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-build-check-diff-
- name: Cache Go Dependencies
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3
with:
path: .work/pkg
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-pkg-
- name: Download Go Modules
run: make modules.download modules.check
- name: Vendor Dependencies
run: make vendor vendor.check
- name: Initialize CodeQL
uses: github/codeql-action/init@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3
uses: github/codeql-action/init@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2
with:
languages: go
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3
uses: github/codeql-action/analyze@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2
trivy-scan-fs:
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs: detect-noop
if: needs.detect-noop.outputs.noop != 'true'
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
submodules: true
- name: Run Trivy vulnerability scanner in fs mode
uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # 0.24.0
uses: aquasecurity/trivy-action@f78e9ecf42a1271402d4f484518b9313235990e1 # 0.13.1
with:
scan-type: 'fs'
ignore-unfixed: true
@ -178,13 +178,13 @@ jobs:
severity: 'CRITICAL,HIGH'
unit-tests:
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
needs: detect-noop
if: needs.detect-noop.outputs.noop != 'true'
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
submodules: true
@ -192,7 +192,7 @@ jobs:
run: git fetch --prune --unshallow
- name: Setup Go
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4
with:
go-version: ${{ env.GO_VERSION }}
@ -201,27 +201,27 @@ jobs:
run: echo "::set-output name=cache::$(make go.cachedir)"
- name: Cache the Go Build Cache
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3
with:
path: ${{ steps.go.outputs.cache }}
key: ${{ runner.os }}-build-unit-tests-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-build-unit-tests-
- name: Cache Go Dependencies
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3
with:
path: .work/pkg
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-pkg-
- name: Download Go Modules
run: make modules.download modules.check
- name: Vendor Dependencies
run: make vendor vendor.check
- name: Run Unit Tests
run: make -j2 test
- name: Publish Unit Test Coverage
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
uses: codecov/codecov-action@c4cf8a4f03f0ac8585acb7c1b7ce3460ec15782f # v4
with:
flags: unittests
file: _output/tests/linux_amd64/coverage.txt

View File

@ -4,7 +4,7 @@ on: issue_comment
jobs:
points:
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
if: startsWith(github.event.comment.body, '/points')
steps:
@ -19,7 +19,7 @@ jobs:
allow-edits: "false"
permission-level: write
- name: Handle Command
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6
env:
POINTS: ${{ steps.command.outputs.command-arguments }}
with:
@ -65,7 +65,7 @@ jobs:
# NOTE(negz): See also backport.yml, which is the variant that triggers on PR
# merge rather than on comment.
backport:
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/backport')
steps:
- name: Extract Command
@ -80,12 +80,12 @@ jobs:
permission-level: write
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
fetch-depth: 0
- name: Open Backport PR
uses: zeebe-io/backport-action@be567af183754f6a5d831ae90f648954763f17f5 # v3.1.0
uses: zeebe-io/backport-action@408fae11ed190c2f91bf15d15af01b8f8b45709b # v2.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
github_workspace: ${{ github.workspace }}

View File

@ -20,11 +20,11 @@ env:
jobs:
promote-artifacts:
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
submodules: true
@ -32,7 +32,7 @@ jobs:
run: git fetch --prune --unshallow
- name: Login to Docker
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3
if: env.DOCKER_USR != ''
with:
username: ${{ secrets.DOCKER_USR }}

View File

@ -12,11 +12,11 @@ on:
jobs:
create-tag:
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- name: Create Tag
uses: negz/create-tag@39bae1e0932567a58c20dea5a1a0d18358503320 # v1

2
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "build"]
path = build
url = https://github.com/crossplane/build
url = https://github.com/upbound/build

View File

@ -24,9 +24,9 @@ NPROCS ?= 1
GO_TEST_PARALLEL := $(shell echo $$(( $(NPROCS) / 2 )))
GO_LDFLAGS += -X $(GO_PROJECT)/pkg/version.Version=$(VERSION)
GO_SUBDIRS += errors proto resource response request
GO_SUBDIRS += proto
GO111MODULE = on
GOLANGCILINT_VERSION = 1.61.0
GOLANGCILINT_VERSION = 1.55.1
GO_LINT_ARGS ?= "--fix"
-include build/makelib/golang.mk

View File

@ -1,5 +1,5 @@
# function-sdk-go
[![CI](https://github.com/crossplane/function-sdk-go/actions/workflows/ci.yml/badge.svg)](https://github.com/crossplane/function-sdk-go/actions/workflows/ci.yml) ![GitHub release (latest SemVer)](https://img.shields.io/github/release/crossplane/function-sdk-go) [![Go Reference](https://pkg.go.dev/badge/github.com/crossplane/function-sdk-go.svg)](https://pkg.go.dev/github.com/crossplane/function-sdk-go)
[![CI](https://github.com/crossplane/function-sdk-go/actions/workflows/ci.yml/badge.svg)](https://github.com/crossplane/function-sdk-go/actions/workflows/ci.yml) ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/crossplane/function-sdk-go) [![Go Reference](https://pkg.go.dev/badge/github.com/crossplane/function-sdk-go.svg)](https://pkg.go.dev/github.com/crossplane/function-sdk-go)
The [Go][go] SDK for writing [composition functions][functions].

2
build

@ -1 +1 @@
Subproject commit 231258db281237379d8ec0c6e4af9d7c1ae5cc4a
Subproject commit a6e25afa0d43da62b11af96a5d29627a52f32cd9

View File

@ -117,6 +117,7 @@ func Cause(err error) error {
}
for err != nil {
//nolint:errorlint // We actually do want to check the outermost error.
w, ok := err.(wrapped)
if !ok {
return err

228
go.mod
View File

@ -1,201 +1,155 @@
module github.com/crossplane/function-sdk-go
go 1.23.0
toolchain go1.23.2
go 1.21
require (
github.com/bufbuild/buf v1.42.0
github.com/crossplane/crossplane-runtime v1.18.0
github.com/go-json-experiment/json v0.0.0-20240815175050-ebd3a8989ca1
github.com/go-logr/logr v1.4.2
github.com/go-logr/zapr v1.3.0
github.com/bufbuild/buf v1.27.2
github.com/crossplane/crossplane-runtime v1.14.0
github.com/go-json-experiment/json v0.0.0-20231013223334-54c864be5b8d
github.com/go-logr/logr v1.3.0
github.com/go-logr/zapr v1.2.4
github.com/google/go-cmp v0.6.0
github.com/pkg/errors v0.9.1
github.com/upbound/provider-aws v1.14.0
go.uber.org/zap v1.27.0
google.golang.org/grpc v1.67.0
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
google.golang.org/protobuf v1.34.3-0.20240816073751-94ecbc261689
k8s.io/api v0.31.0
k8s.io/apimachinery v0.31.0
k8s.io/utils v0.0.0-20240902221715-702e33fdd3c3
github.com/upbound/provider-aws v0.43.0
go.uber.org/zap v1.26.0
google.golang.org/grpc v1.59.0
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0
google.golang.org/protobuf v1.31.0
k8s.io/api v0.28.3
k8s.io/apimachinery v0.28.3
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
sigs.k8s.io/yaml v1.4.0
)
require (
buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.34.2-20240904181154-a0be11449112.2 // indirect
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.2-20240717164558-a6c49f84cc0f.2 // indirect
buf.build/gen/go/bufbuild/registry/connectrpc/go v1.16.2-20240821192916-45ba72cdd479.1 // indirect
buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.34.2-20240821192916-45ba72cdd479.2 // indirect
buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.34.2-20240828222655-5345c0a56177.2 // indirect
buf.build/go/bufplugin v0.2.0 // indirect
buf.build/go/protoyaml v0.2.0 // indirect
connectrpc.com/connect v1.16.2 // indirect
connectrpc.com/otelconnect v0.7.1 // indirect
dario.cat/mergo v1.0.1 // indirect
connectrpc.com/connect v1.11.1 // indirect
connectrpc.com/otelconnect v0.6.0 // indirect
dario.cat/mergo v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.12.6 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/antchfx/htmlquery v1.2.4 // indirect
github.com/antchfx/xpath v1.2.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/bufbuild/protocompile v0.14.1 // indirect
github.com/bufbuild/protoplugin v0.0.0-20240911180120-7bb73e41a54a // indirect
github.com/bufbuild/protovalidate-go v0.6.5 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/cgroups/v3 v3.0.3 // indirect
github.com/containerd/containerd v1.7.22 // indirect
github.com/containerd/continuity v0.4.3 // indirect
github.com/containerd/errdefs v0.2.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
github.com/containerd/ttrpc v1.2.5 // indirect
github.com/containerd/typeurl/v2 v2.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/crossplane/upjet v1.4.1-0.20240911184956-3afbb7796d46 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/cli v27.2.1+incompatible // indirect
github.com/bufbuild/protocompile v0.6.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/crossplane/upjet v0.11.0-rc.0.0.20231012093706-c4a76d2a7505 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/docker/cli v24.0.6+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v27.2.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/docker v24.0.7+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch v5.9.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/fatih/camelcase v1.0.0 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/felixge/fgprof v0.9.5 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-chi/chi/v5 v5.1.0 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/felixge/fgprof v0.9.3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-chi/chi/v5 v5.0.10 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/gobuffalo/flect v1.0.2 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/gofrs/uuid/v5 v5.3.0 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/gofrs/uuid/v5 v5.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/cel-go v0.21.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-containerregistry v0.20.2 // indirect
github.com/google/go-containerregistry v0.16.1 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/go-cty v1.4.1-0.20200723130312-85980079f637 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect
github.com/hashicorp/go-hclog v1.2.1 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/hashicorp/hcl/v2 v2.21.0 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/hcl/v2 v2.14.1 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/terraform-json v0.22.1 // indirect
github.com/hashicorp/terraform-plugin-framework v1.10.0 // indirect
github.com/hashicorp/terraform-plugin-go v0.23.0 // indirect
github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 // indirect
github.com/hashicorp/terraform-json v0.14.0 // indirect
github.com/hashicorp/terraform-plugin-go v0.14.0 // indirect
github.com/hashicorp/terraform-plugin-log v0.7.0 // indirect
github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.0 // indirect
github.com/iancoleman/strcase v0.2.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jdx/go-netrc v1.0.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/compress v1.17.2 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/mount v0.3.4 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/onsi/ginkgo/v2 v2.20.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opencontainers/runtime-spec v1.2.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/profile v1.7.0 // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.48.2 // indirect
github.com/rs/cors v1.11.1 // indirect
github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/rs/cors v1.10.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/afero v1.10.0 // indirect
github.com/spf13/cobra v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/tetratelabs/wazero v1.5.0 // indirect
github.com/tmccombs/hcl2json v0.3.3 // indirect
github.com/vbatts/tar-split v0.11.5 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect
github.com/vmihailenco/tagparser v0.1.1 // indirect
github.com/yuin/goldmark v1.4.13 // indirect
github.com/zclconf/go-cty v1.14.4 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 // indirect
go.opentelemetry.io/otel v1.30.0 // indirect
go.opentelemetry.io/otel/metric v1.30.0 // indirect
go.opentelemetry.io/otel/sdk v1.30.0 // indirect
go.opentelemetry.io/otel/trace v1.30.0 // indirect
github.com/zclconf/go-cty v1.11.0 // indirect
go.opentelemetry.io/otel v1.19.0 // indirect
go.opentelemetry.io/otel/metric v1.19.0 // indirect
go.opentelemetry.io/otel/sdk v1.19.0 // indirect
go.opentelemetry.io/otel/trace v1.19.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/mock v0.4.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.35.0 // indirect
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/oauth2 v0.22.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.25.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/mod v0.13.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.11.0 // indirect
golang.org/x/sync v0.4.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.14.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.31.0 // indirect
k8s.io/client-go v0.31.0 // indirect
k8s.io/component-base v0.31.0 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
pluginrpc.com/pluginrpc v0.3.0 // indirect
sigs.k8s.io/controller-runtime v0.19.0 // indirect
sigs.k8s.io/controller-tools v0.16.0 // indirect
k8s.io/client-go v0.28.3 // indirect
k8s.io/component-base v0.28.3 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
sigs.k8s.io/controller-runtime v0.16.3 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
)

872
go.sum

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,335 +0,0 @@
/*
Copyright 2022 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
syntax = "proto3";
//buf:lint:ignore PACKAGE_DIRECTORY_MATCH // Too late to fix this now.
package apiextensions.fn.proto.v1;
import "google/protobuf/duration.proto";
import "google/protobuf/struct.proto";
option go_package = "github.com/crossplane/crossplane/apis/apiextensions/fn/proto/v1";
// A FunctionRunnerService is a Composition Function.
service FunctionRunnerService {
// RunFunction runs the Composition Function.
rpc RunFunction(RunFunctionRequest) returns (RunFunctionResponse) {}
}
// A RunFunctionRequest requests that the Composition Function be run.
message RunFunctionRequest {
// Metadata pertaining to this request.
RequestMeta meta = 1;
// The observed state prior to invocation of a Function pipeline. State passed
// to each Function is fresh as of the time the pipeline was invoked, not as
// of the time each Function was invoked.
State observed = 2;
// Desired state according to a Function pipeline. The state passed to a
// particular Function may have been accumulated by previous Functions in the
// pipeline.
//
// Note that the desired state must be a partial object with only the fields
// that this function (and its predecessors in the pipeline) wants to have
// set in the object. Copying a non-partial observed state to desired is most
// likely not what you want to do. Leaving out fields that had been returned
// as desired before will result in them being deleted from the objects in the
// cluster.
State desired = 3;
// Optional input specific to this Function invocation. A JSON representation
// of the 'input' block of the relevant entry in a Composition's pipeline.
optional google.protobuf.Struct input = 4;
// Optional context. Crossplane may pass arbitrary contextual information to a
// Function. A Function may also return context in its RunFunctionResponse,
// and that context will be passed to subsequent Functions. Crossplane
// discards all context returned by the last Function in the pipeline.
optional google.protobuf.Struct context = 5;
// Optional extra resources that the Function required.
// Note that extra resources is a map to Resources, plural.
// The map key corresponds to the key in a RunFunctionResponse's
// extra_resources field. If a Function requested extra resources that
// did not exist, Crossplane sets the map key to an empty Resources message to
// indicate that it attempted to satisfy the request.
map<string, Resources> extra_resources = 6;
// Optional credentials that this Function may use to communicate with an
// external system.
map<string, Credentials> credentials = 7;
}
// Credentials that a Function may use to communicate with an external system.
message Credentials {
// Source of the credentials.
oneof source {
// Credential data loaded by Crossplane, for example from a Secret.
CredentialData credential_data = 1;
}
}
// CredentialData loaded by Crossplane, for example from a Secret.
message CredentialData {
map<string, bytes> data = 1;
}
// Resources represents the state of several Crossplane resources.
message Resources {
repeated Resource items = 1;
}
// A RunFunctionResponse contains the result of a Composition Function run.
message RunFunctionResponse {
// Metadata pertaining to this response.
ResponseMeta meta = 1;
// Desired state according to a Function pipeline. Functions may add desired
// state, and may mutate or delete any part of the desired state they are
// concerned with. A Function must pass through any part of the desired state
// that it is not concerned with.
//
//
// Note that the desired state must be a partial object with only the fields
// that this function (and its predecessors in the pipeline) wants to have
// set in the object. Copying a non-partial observed state to desired is most
// likely not what you want to do. Leaving out fields that had been returned
// as desired before will result in them being deleted from the objects in the
// cluster.
State desired = 2;
// Results of the Function run. Results are used for observability purposes.
repeated Result results = 3;
// Optional context to be passed to the next Function in the pipeline as part
// of the RunFunctionRequest. Dropped on the last function in the pipeline.
optional google.protobuf.Struct context = 4;
// Requirements that must be satisfied for this Function to run successfully.
Requirements requirements = 5;
// Status conditions to be applied to the composite resource. Conditions may also
// optionally be applied to the composite resource's associated claim.
repeated Condition conditions = 6;
}
// RequestMeta contains metadata pertaining to a RunFunctionRequest.
message RequestMeta {
// An opaque string identifying a request. Requests with identical tags will
// be otherwise identical.
string tag = 1;
}
// Requirements that must be satisfied for a Function to run successfully.
message Requirements {
// Extra resources that this Function requires.
// The map key uniquely identifies the group of resources.
map<string, ResourceSelector> extra_resources = 1;
}
// ResourceSelector selects a group of resources, either by name or by label.
message ResourceSelector {
// API version of resources to select.
string api_version = 1;
// Kind of resources to select.
string kind = 2;
// Resources to match.
oneof match {
// Match the resource with this name.
string match_name = 3;
// Match all resources with these labels.
MatchLabels match_labels = 4;
}
// Match resources in this namespace.
// Omit namespace to match cluster scoped resources, or to match namespaced
// resources by labels across all namespaces.
optional string namespace = 5;
}
// MatchLabels defines a set of labels to match resources against.
message MatchLabels {
map<string, string> labels = 1;
}
// ResponseMeta contains metadata pertaining to a RunFunctionResponse.
message ResponseMeta {
// An opaque string identifying the content of the request. Must match the
// meta.tag of the corresponding RunFunctionRequest.
string tag = 1;
// Time-to-live of this response. Crossplane will call the function again when
// the TTL expires. Crossplane may cache the response to avoid calling the
// function again until the TTL expires.
optional google.protobuf.Duration ttl = 2;
}
// State of the composite resource (XR) and any composed resources.
message State {
// The state of the composite resource (XR).
Resource composite = 1;
// The state of any composed resources.
map<string, Resource> resources = 2;
}
// A Resource represents the state of a composite or composed resource.
message Resource {
// The JSON representation of the resource.
//
// * Crossplane will set this field in a RunFunctionRequest to the entire
// observed state of a resource - including its metadata, spec, and status.
//
// * A Function should set this field in a RunFunctionRequest to communicate
// the desired state of a composite or composed resource.
//
// * A Function may only specify the desired status of a composite resource -
// not its metadata or spec. A Function should not return desired metadata
// or spec for a composite resource. This will be ignored.
//
// * A Function may not specify the desired status of a composed resource -
// only its metadata and spec. A Function should not return desired status
// for a composed resource. This will be ignored.
google.protobuf.Struct resource = 1;
// The resource's connection details.
//
// * Crossplane will set this field in a RunFunctionRequest to communicate the
// the observed connection details of a composite or composed resource.
//
// * A Function should set this field in a RunFunctionResponse to indicate the
// desired connection details of the composite resource.
//
// * A Function should not set this field in a RunFunctionResponse to indicate
// the desired connection details of a composed resource. This will be
// ignored.
map<string, bytes> connection_details = 2;
// Ready indicates whether the resource should be considered ready.
//
// * Crossplane will never set this field in a RunFunctionRequest.
//
// * A Function should set this field to READY_TRUE in a RunFunctionResponse
// to indicate that a desired composed resource is ready.
//
// * A Function should set this field to READY_TRUE in a RunFunctionResponse
// to indicate that a desired composite resource is ready.
// This overwrites the standard readiness detection that determines the
// ready state of the composite by the ready state of the the
// composed resources.
Ready ready = 3;
}
// Ready indicates whether a composed resource should be considered ready.
enum Ready {
READY_UNSPECIFIED = 0;
// True means the composed resource has been observed to be ready.
READY_TRUE = 1;
// False means the composed resource has not been observed to be ready.
READY_FALSE = 2;
}
// A Result of running a Function.
message Result {
// Severity of this result.
Severity severity = 1;
// Human-readable details about the result.
string message = 2;
// Optional PascalCase, machine-readable reason for this result. If omitted,
// the value will be ComposeResources.
optional string reason = 3;
// The resources this result targets.
optional Target target = 4;
}
// Severity of Function results.
enum Severity {
SEVERITY_UNSPECIFIED = 0;
// Fatal results are fatal; subsequent Composition Functions may run, but
// the Composition Function pipeline run will be considered a failure and
// the first fatal result will be returned as an error.
SEVERITY_FATAL = 1;
// Warning results are non-fatal; the entire Composition will run to
// completion but warning events and debug logs associated with the
// composite resource will be emitted.
SEVERITY_WARNING = 2;
// Normal results are emitted as normal events and debug logs associated
// with the composite resource.
SEVERITY_NORMAL = 3;
}
// Target of Function results and conditions.
enum Target {
// If the target is unspecified, the result targets the composite resource.
TARGET_UNSPECIFIED = 0;
// Target the composite resource. Results that target the composite resource
// should include detailed, advanced information.
TARGET_COMPOSITE = 1;
// Target the composite and the claim. Results that target the composite and
// the claim should include only end-user friendly information.
TARGET_COMPOSITE_AND_CLAIM = 2;
}
// Status condition to be applied to the composite resource. Condition may also
// optionally be applied to the composite resource's associated claim. For
// detailed information on proper usage of status conditions, please see
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties.
message Condition {
// Type of condition in PascalCase.
string type = 1;
// Status of the condition.
Status status = 2;
// Reason contains a programmatic identifier indicating the reason for the
// condition's last transition. Producers of specific condition types may
// define expected values and meanings for this field, and whether the values
// are considered a guaranteed API. The value should be a PascalCase string.
// This field may not be empty.
string reason = 3;
// Message is a human readable message indicating details about the
// transition. This may be an empty string.
optional string message = 4;
// The resources this condition targets.
optional Target target = 5;
}
enum Status {
STATUS_CONDITION_UNSPECIFIED = 0;
STATUS_CONDITION_UNKNOWN = 1;
STATUS_CONDITION_TRUE = 2;
STATUS_CONDITION_FALSE = 3;
}

View File

@ -1,144 +0,0 @@
//
//Copyright 2022 The Crossplane Authors.
//
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: v1/run_function.proto
//buf:lint:ignore PACKAGE_DIRECTORY_MATCH // Too late to fix this now.
package v1
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
FunctionRunnerService_RunFunction_FullMethodName = "/apiextensions.fn.proto.v1.FunctionRunnerService/RunFunction"
)
// FunctionRunnerServiceClient is the client API for FunctionRunnerService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// A FunctionRunnerService is a Composition Function.
type FunctionRunnerServiceClient interface {
// RunFunction runs the Composition Function.
RunFunction(ctx context.Context, in *RunFunctionRequest, opts ...grpc.CallOption) (*RunFunctionResponse, error)
}
type functionRunnerServiceClient struct {
cc grpc.ClientConnInterface
}
func NewFunctionRunnerServiceClient(cc grpc.ClientConnInterface) FunctionRunnerServiceClient {
return &functionRunnerServiceClient{cc}
}
func (c *functionRunnerServiceClient) RunFunction(ctx context.Context, in *RunFunctionRequest, opts ...grpc.CallOption) (*RunFunctionResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RunFunctionResponse)
err := c.cc.Invoke(ctx, FunctionRunnerService_RunFunction_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// FunctionRunnerServiceServer is the server API for FunctionRunnerService service.
// All implementations must embed UnimplementedFunctionRunnerServiceServer
// for forward compatibility.
//
// A FunctionRunnerService is a Composition Function.
type FunctionRunnerServiceServer interface {
// RunFunction runs the Composition Function.
RunFunction(context.Context, *RunFunctionRequest) (*RunFunctionResponse, error)
mustEmbedUnimplementedFunctionRunnerServiceServer()
}
// UnimplementedFunctionRunnerServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedFunctionRunnerServiceServer struct{}
func (UnimplementedFunctionRunnerServiceServer) RunFunction(context.Context, *RunFunctionRequest) (*RunFunctionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RunFunction not implemented")
}
func (UnimplementedFunctionRunnerServiceServer) mustEmbedUnimplementedFunctionRunnerServiceServer() {}
func (UnimplementedFunctionRunnerServiceServer) testEmbeddedByValue() {}
// UnsafeFunctionRunnerServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to FunctionRunnerServiceServer will
// result in compilation errors.
type UnsafeFunctionRunnerServiceServer interface {
mustEmbedUnimplementedFunctionRunnerServiceServer()
}
func RegisterFunctionRunnerServiceServer(s grpc.ServiceRegistrar, srv FunctionRunnerServiceServer) {
// If the following call pancis, it indicates UnimplementedFunctionRunnerServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&FunctionRunnerService_ServiceDesc, srv)
}
func _FunctionRunnerService_RunFunction_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RunFunctionRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(FunctionRunnerServiceServer).RunFunction(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: FunctionRunnerService_RunFunction_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(FunctionRunnerServiceServer).RunFunction(ctx, req.(*RunFunctionRequest))
}
return interceptor(ctx, in, info, handler)
}
// FunctionRunnerService_ServiceDesc is the grpc.ServiceDesc for FunctionRunnerService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var FunctionRunnerService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "apiextensions.fn.proto.v1.FunctionRunnerService",
HandlerType: (*FunctionRunnerServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "RunFunction",
Handler: _FunctionRunnerService_RunFunction_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "v1/run_function.proto",
}

File diff suppressed because it is too large Load Diff

View File

@ -19,11 +19,12 @@ syntax = "proto3";
import "google/protobuf/struct.proto";
import "google/protobuf/duration.proto";
// Generated from apiextensions/fn/proto/v1/run_function.proto by ../hack/duplicate_proto_type.sh. DO NOT EDIT.
// Note that the authoritative Composition Functions protobuf definition lives
// at the below URL. Each SDK maintains and manually syncs its own copy.
// https://github.com/crossplane/crossplane/tree/master/apis/apiextensions/fn/proto
package apiextensions.fn.proto.v1beta1;
option go_package = "github.com/crossplane/crossplane/apis/apiextensions/fn/proto/v1beta1";
option go_package = "github.com/crossplane/function-sdk-go/proto/v1beta1";
// A FunctionRunnerService is a Composition Function.
service FunctionRunnerService {
@ -62,37 +63,6 @@ message RunFunctionRequest {
// and that context will be passed to subsequent Functions. Crossplane
// discards all context returned by the last Function in the pipeline.
optional google.protobuf.Struct context = 5;
// Optional extra resources that the Function required.
// Note that extra resources is a map to Resources, plural.
// The map key corresponds to the key in a RunFunctionResponse's
// extra_resources field. If a Function requested extra resources that
// did not exist, Crossplane sets the map key to an empty Resources message to
// indicate that it attempted to satisfy the request.
map<string, Resources> extra_resources = 6;
// Optional credentials that this Function may use to communicate with an
// external system.
map <string, Credentials> credentials = 7;
}
// Credentials that a Function may use to communicate with an external system.
message Credentials {
// Source of the credentials.
oneof source {
// Credential data loaded by Crossplane, for example from a Secret.
CredentialData credential_data = 1;
}
}
// CredentialData loaded by Crossplane, for example from a Secret.
message CredentialData {
map<string, bytes> data = 1;
}
// Resources represents the state of several Crossplane resources.
message Resources {
repeated Resource items = 1;
}
// A RunFunctionResponse contains the result of a Composition Function run.
@ -120,13 +90,6 @@ message RunFunctionResponse {
// Optional context to be passed to the next Function in the pipeline as part
// of the RunFunctionRequest. Dropped on the last function in the pipeline.
optional google.protobuf.Struct context = 4;
// Requirements that must be satisfied for this Function to run successfully.
Requirements requirements = 5;
// Status conditions to be applied to the composite resource. Conditions may also
// optionally be applied to the composite resource's associated claim.
repeated Condition conditions = 6;
}
// RequestMeta contains metadata pertaining to a RunFunctionRequest.
@ -136,36 +99,6 @@ message RequestMeta {
string tag = 1;
}
// Requirements that must be satisfied for a Function to run successfully.
message Requirements {
// Extra resources that this Function requires.
// The map key uniquely identifies the group of resources.
map<string, ResourceSelector> extra_resources = 1;
}
// ResourceSelector selects a group of resources, either by name or by label.
message ResourceSelector {
// API version of resources to select.
string api_version = 1;
// Kind of resources to select.
string kind = 2;
// Resources to match.
oneof match {
// Match the resource with this name.
string match_name = 3;
// Match all resources with these labels.
MatchLabels match_labels = 4;
}
}
// MatchLabels defines a set of labels to match resources against.
message MatchLabels {
map<string, string> labels = 1;
}
// ResponseMeta contains metadata pertaining to a RunFunctionResponse.
message ResponseMeta {
// An opaque string identifying the content of the request. Must match the
@ -249,13 +182,6 @@ message Result {
// Human-readable details about the result.
string message = 2;
// Optional PascalCase, machine-readable reason for this result. If omitted,
// the value will be ComposeResources.
optional string reason = 3;
// The resources this result targets.
optional Target target = 4;
}
// Severity of Function results.
@ -275,54 +201,4 @@ enum Severity {
// Normal results are emitted as normal events and debug logs associated
// with the composite resource.
SEVERITY_NORMAL = 3;
}
// Target of Function results and conditions.
enum Target {
// If the target is unspecified, the result targets the composite resource.
TARGET_UNSPECIFIED = 0;
// Target the composite resource. Results that target the composite resource
// should include detailed, advanced information.
TARGET_COMPOSITE = 1;
// Target the composite and the claim. Results that target the composite and
// the claim should include only end-user friendly information.
TARGET_COMPOSITE_AND_CLAIM = 2;
}
// Status condition to be applied to the composite resource. Condition may also
// optionally be applied to the composite resource's associated claim. For
// detailed information on proper usage of status conditions, please see
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties.
message Condition {
// Type of condition in PascalCase.
string type = 1;
// Status of the condition.
Status status = 2;
// Reason contains a programmatic identifier indicating the reason for the
// condition's last transition. Producers of specific condition types may
// define expected values and meanings for this field, and whether the values
// are considered a guaranteed API. The value should be a PascalCase string.
// This field may not be empty.
string reason = 3;
// Message is a human readable message indicating details about the
// transition. This may be an empty string.
optional string message = 4;
// The resources this condition targets.
optional Target target = 5;
}
enum Status {
STATUS_CONDITION_UNSPECIFIED = 0;
STATUS_CONDITION_UNKNOWN = 1;
STATUS_CONDITION_TRUE = 2;
STATUS_CONDITION_FALSE = 3;
}
}

View File

@ -15,11 +15,13 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc-gen-go-grpc v1.3.0
// - protoc (unknown)
// source: v1beta1/run_function.proto
// Generated from apiextensions/fn/proto/v1/run_function.proto by ../hack/duplicate_proto_type.sh. DO NOT EDIT.
// Note that the authoritative Composition Functions protobuf definition lives
// at the below URL. Each SDK maintains and manually syncs its own copy.
// https://github.com/crossplane/crossplane/tree/master/apis/apiextensions/fn/proto
package v1beta1
@ -32,8 +34,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
const (
FunctionRunnerService_RunFunction_FullMethodName = "/apiextensions.fn.proto.v1beta1.FunctionRunnerService/RunFunction"
@ -42,8 +44,6 @@ const (
// FunctionRunnerServiceClient is the client API for FunctionRunnerService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// A FunctionRunnerService is a Composition Function.
type FunctionRunnerServiceClient interface {
// RunFunction runs the Composition Function.
RunFunction(ctx context.Context, in *RunFunctionRequest, opts ...grpc.CallOption) (*RunFunctionResponse, error)
@ -58,9 +58,8 @@ func NewFunctionRunnerServiceClient(cc grpc.ClientConnInterface) FunctionRunnerS
}
func (c *functionRunnerServiceClient) RunFunction(ctx context.Context, in *RunFunctionRequest, opts ...grpc.CallOption) (*RunFunctionResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RunFunctionResponse)
err := c.cc.Invoke(ctx, FunctionRunnerService_RunFunction_FullMethodName, in, out, cOpts...)
err := c.cc.Invoke(ctx, FunctionRunnerService_RunFunction_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
@ -69,27 +68,21 @@ func (c *functionRunnerServiceClient) RunFunction(ctx context.Context, in *RunFu
// FunctionRunnerServiceServer is the server API for FunctionRunnerService service.
// All implementations must embed UnimplementedFunctionRunnerServiceServer
// for forward compatibility.
//
// A FunctionRunnerService is a Composition Function.
// for forward compatibility
type FunctionRunnerServiceServer interface {
// RunFunction runs the Composition Function.
RunFunction(context.Context, *RunFunctionRequest) (*RunFunctionResponse, error)
mustEmbedUnimplementedFunctionRunnerServiceServer()
}
// UnimplementedFunctionRunnerServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedFunctionRunnerServiceServer struct{}
// UnimplementedFunctionRunnerServiceServer must be embedded to have forward compatible implementations.
type UnimplementedFunctionRunnerServiceServer struct {
}
func (UnimplementedFunctionRunnerServiceServer) RunFunction(context.Context, *RunFunctionRequest) (*RunFunctionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RunFunction not implemented")
}
func (UnimplementedFunctionRunnerServiceServer) mustEmbedUnimplementedFunctionRunnerServiceServer() {}
func (UnimplementedFunctionRunnerServiceServer) testEmbeddedByValue() {}
// UnsafeFunctionRunnerServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to FunctionRunnerServiceServer will
@ -99,13 +92,6 @@ type UnsafeFunctionRunnerServiceServer interface {
}
func RegisterFunctionRunnerServiceServer(s grpc.ServiceRegistrar, srv FunctionRunnerServiceServer) {
// If the following call pancis, it indicates UnimplementedFunctionRunnerServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&FunctionRunnerService_ServiceDesc, srv)
}

View File

@ -19,23 +19,22 @@ package request
import (
"google.golang.org/protobuf/types/known/structpb"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"github.com/crossplane/function-sdk-go/errors"
v1 "github.com/crossplane/function-sdk-go/proto/v1"
"github.com/crossplane/function-sdk-go/proto/v1beta1"
"github.com/crossplane/function-sdk-go/resource"
"github.com/crossplane/function-sdk-go/resource/composed"
"github.com/crossplane/function-sdk-go/resource/composite"
)
// GetInput from the supplied request. Input is loaded into the supplied object.
func GetInput(req *v1.RunFunctionRequest, into runtime.Object) error {
return errors.Wrapf(resource.AsObject(req.GetInput(), into), "cannot get function input %T from %T", into, req)
func GetInput(req *v1beta1.RunFunctionRequest, into runtime.Object) error {
return errors.Wrap(resource.AsObject(req.GetInput(), into), "cannot get Function input %T from %T, into, req")
}
// GetContextKey gets context from the supplied key.
func GetContextKey(req *v1.RunFunctionRequest, key string) (*structpb.Value, bool) {
func GetContextKey(req *v1beta1.RunFunctionRequest, key string) (*structpb.Value, bool) {
f := req.GetContext().GetFields()
if f == nil {
return nil, false
@ -45,7 +44,7 @@ func GetContextKey(req *v1.RunFunctionRequest, key string) (*structpb.Value, boo
}
// GetObservedCompositeResource from the supplied request.
func GetObservedCompositeResource(req *v1.RunFunctionRequest) (*resource.Composite, error) {
func GetObservedCompositeResource(req *v1beta1.RunFunctionRequest) (*resource.Composite, error) {
xr := &resource.Composite{
Resource: composite.New(),
ConnectionDetails: req.GetObserved().GetComposite().GetConnectionDetails(),
@ -60,7 +59,7 @@ func GetObservedCompositeResource(req *v1.RunFunctionRequest) (*resource.Composi
}
// GetObservedComposedResources from the supplied request.
func GetObservedComposedResources(req *v1.RunFunctionRequest) (map[resource.Name]resource.ObservedComposed, error) {
func GetObservedComposedResources(req *v1beta1.RunFunctionRequest) (map[resource.Name]resource.ObservedComposed, error) {
ocds := map[resource.Name]resource.ObservedComposed{}
for name, r := range req.GetObserved().GetResources() {
ocd := resource.ObservedComposed{Resource: composed.New(), ConnectionDetails: r.GetConnectionDetails()}
@ -78,7 +77,7 @@ func GetObservedComposedResources(req *v1.RunFunctionRequest) (map[resource.Name
}
// GetDesiredCompositeResource from the supplied request.
func GetDesiredCompositeResource(req *v1.RunFunctionRequest) (*resource.Composite, error) {
func GetDesiredCompositeResource(req *v1beta1.RunFunctionRequest) (*resource.Composite, error) {
xr := &resource.Composite{
Resource: composite.New(),
ConnectionDetails: req.GetDesired().GetComposite().GetConnectionDetails(),
@ -93,7 +92,7 @@ func GetDesiredCompositeResource(req *v1.RunFunctionRequest) (*resource.Composit
}
// GetDesiredComposedResources from the supplied request.
func GetDesiredComposedResources(req *v1.RunFunctionRequest) (map[resource.Name]*resource.DesiredComposed, error) {
func GetDesiredComposedResources(req *v1beta1.RunFunctionRequest) (map[resource.Name]*resource.DesiredComposed, error) {
dcds := map[resource.Name]*resource.DesiredComposed{}
for name, r := range req.GetDesired().GetResources() {
dcd := &resource.DesiredComposed{Resource: composed.New()}
@ -101,45 +100,14 @@ func GetDesiredComposedResources(req *v1.RunFunctionRequest) (map[resource.Name]
return nil, err
}
switch r.GetReady() {
case v1.Ready_READY_UNSPECIFIED:
case v1beta1.Ready_READY_UNSPECIFIED:
dcd.Ready = resource.ReadyUnspecified
case v1.Ready_READY_TRUE:
case v1beta1.Ready_READY_TRUE:
dcd.Ready = resource.ReadyTrue
case v1.Ready_READY_FALSE:
case v1beta1.Ready_READY_FALSE:
dcd.Ready = resource.ReadyFalse
}
dcds[resource.Name(name)] = dcd
}
return dcds, nil
}
// GetExtraResources from the supplied request.
func GetExtraResources(req *v1.RunFunctionRequest) (map[string][]resource.Extra, error) {
out := make(map[string][]resource.Extra, len(req.GetExtraResources()))
for name, ers := range req.GetExtraResources() {
out[name] = []resource.Extra{}
for _, i := range ers.GetItems() {
r := &resource.Extra{Resource: &unstructured.Unstructured{}}
if err := resource.AsObject(i.GetResource(), r.Resource); err != nil {
return nil, err
}
out[name] = append(out[name], *r)
}
}
return out, nil
}
// GetCredentials from the supplied request.
func GetCredentials(req *v1.RunFunctionRequest, name string) (resource.Credentials, error) {
cred, exists := req.GetCredentials()[name]
if !exists {
return resource.Credentials{}, errors.Errorf("%s: credential not found", name)
}
switch t := cred.GetSource().(type) {
case *v1.Credentials_CredentialData:
return resource.Credentials{Type: resource.CredentialsTypeData, Data: cred.GetCredentialData().GetData()}, nil
default:
return resource.Credentials{}, errors.Errorf("%s: not a supported credential source", t)
}
}

View File

@ -23,7 +23,7 @@ import (
"github.com/google/go-cmp/cmp"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
v1 "github.com/crossplane/function-sdk-go/proto/v1"
"github.com/crossplane/function-sdk-go/proto/v1beta1"
"github.com/crossplane/function-sdk-go/resource"
"github.com/crossplane/function-sdk-go/resource/composed"
"github.com/crossplane/function-sdk-go/resource/composite"
@ -37,12 +37,12 @@ func TestGetObservedCompositeResource(t *testing.T) {
cases := map[string]struct {
reason string
req *v1.RunFunctionRequest
req *v1beta1.RunFunctionRequest
want want
}{
"NoObservedXR": {
reason: "In the unlikely event the request has no observed XR we should return a usable, empty Composite.",
req: &v1.RunFunctionRequest{},
req: &v1beta1.RunFunctionRequest{},
want: want{
oxr: &resource.Composite{
Resource: composite.New(),
@ -52,9 +52,9 @@ func TestGetObservedCompositeResource(t *testing.T) {
},
"ObservedXR": {
reason: "We should return the XR read from the request.",
req: &v1.RunFunctionRequest{
Observed: &v1.State{
Composite: &v1.Resource{
req: &v1beta1.RunFunctionRequest{
Observed: &v1beta1.State{
Composite: &v1beta1.Resource{
Resource: resource.MustStructJSON(`{
"apiVersion": "test.crossplane.io/v1",
"kind": "XR"
@ -103,12 +103,12 @@ func TestGetDesiredCompositeResource(t *testing.T) {
cases := map[string]struct {
reason string
req *v1.RunFunctionRequest
req *v1beta1.RunFunctionRequest
want want
}{
"NoDesiredXR": {
reason: "If the request has no desired XR we should return a usable, empty Composite.",
req: &v1.RunFunctionRequest{},
req: &v1beta1.RunFunctionRequest{},
want: want{
oxr: &resource.Composite{
Resource: composite.New(),
@ -118,9 +118,9 @@ func TestGetDesiredCompositeResource(t *testing.T) {
},
"DesiredXR": {
reason: "We should return the XR read from the request.",
req: &v1.RunFunctionRequest{
Desired: &v1.State{
Composite: &v1.Resource{
req: &v1beta1.RunFunctionRequest{
Desired: &v1beta1.State{
Composite: &v1beta1.Resource{
Resource: resource.MustStructJSON(`{
"apiVersion": "test.crossplane.io/v1",
"kind": "XR"
@ -169,21 +169,21 @@ func TestGetObservedComposedResources(t *testing.T) {
cases := map[string]struct {
reason string
req *v1.RunFunctionRequest
req *v1beta1.RunFunctionRequest
want want
}{
"NoObservedComposedResources": {
reason: "If the request has no observed composed resources we should return an empty, non-nil map.",
req: &v1.RunFunctionRequest{},
req: &v1beta1.RunFunctionRequest{},
want: want{
ocds: map[resource.Name]resource.ObservedComposed{},
},
},
"ObservedComposedResources": {
reason: "If the request has observed composed resources we should return them.",
req: &v1.RunFunctionRequest{
Observed: &v1.State{
Resources: map[string]*v1.Resource{
req: &v1beta1.RunFunctionRequest{
Observed: &v1beta1.State{
Resources: map[string]*v1beta1.Resource{
"observed-composed-resource": {
Resource: resource.MustStructJSON(`{
"apiVersion": "test.crossplane.io/v1",
@ -236,27 +236,27 @@ func TestGetDesiredComposedResources(t *testing.T) {
cases := map[string]struct {
reason string
req *v1.RunFunctionRequest
req *v1beta1.RunFunctionRequest
want want
}{
"NoDesiredComposedResources": {
reason: "If the request has no desired composed resources we should return an empty, non-nil map.",
req: &v1.RunFunctionRequest{},
req: &v1beta1.RunFunctionRequest{},
want: want{
dcds: map[resource.Name]*resource.DesiredComposed{},
},
},
"DesiredComposedResources": {
reason: "If the request has desired composed resources we should return them.",
req: &v1.RunFunctionRequest{
Desired: &v1.State{
Resources: map[string]*v1.Resource{
req: &v1beta1.RunFunctionRequest{
Desired: &v1beta1.State{
Resources: map[string]*v1beta1.Resource{
"desired-composed-resource": {
Resource: resource.MustStructJSON(`{
"apiVersion": "test.crossplane.io/v1",
"kind": "Composed"
}`),
Ready: v1.Ready_READY_TRUE,
Ready: v1beta1.Ready_READY_TRUE,
},
},
},

View File

@ -284,18 +284,3 @@ func (cd *Unstructured) SetBool(path string, value bool) error {
func (cd *Unstructured) SetInteger(path string, value int64) error {
return cd.SetValue(path, value)
}
// SetObservedGeneration of this Composed resource.
func (cd *Unstructured) SetObservedGeneration(generation int64) {
status := &xpv1.ObservedStatus{}
_ = fieldpath.Pave(cd.Object).GetValueInto("status", status)
status.SetObservedGeneration(generation)
_ = fieldpath.Pave(cd.Object).SetValue("status.observedGeneration", status.ObservedGeneration)
}
// GetObservedGeneration of this Composed resource.
func (cd *Unstructured) GetObservedGeneration() int64 {
status := &xpv1.ObservedStatus{}
_ = fieldpath.Pave(cd.Object).GetValueInto("status", status)
return status.GetObservedGeneration()
}

View File

@ -22,7 +22,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/upbound/provider-aws/apis/s3/v1beta2"
"github.com/upbound/provider-aws/apis/s3/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
@ -62,25 +62,25 @@ func Example() {
}
func ExampleScheme() {
// Add all v1beta2 types to the scheme so that From can automatically
// Add all v1beta1 types to the scheme so that From can automatically
// determine their apiVersion and kind.
v1beta2.AddToScheme(Scheme)
v1beta1.AddToScheme(Scheme)
}
func ExampleFrom() {
// Add all v1beta2 types to the scheme so that From can automatically
// Add all v1beta1 types to the scheme so that From can automatically
// determine their apiVersion and kind.
v1beta2.AddToScheme(Scheme)
v1beta1.AddToScheme(Scheme)
// Create a strongly typed runtime.Object, imported from a provider.
b := &v1beta2.Bucket{
b := &v1beta1.Bucket{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"coolness": "high",
},
},
Spec: v1beta2.BucketSpec{
ForProvider: v1beta2.BucketParameters{
Spec: v1beta1.BucketSpec{
ForProvider: v1beta1.BucketParameters{
Region: ptr.To[string]("us-east-2"),
},
},
@ -100,7 +100,7 @@ func ExampleFrom() {
fmt.Println(string(y))
// Output:
// apiVersion: s3.aws.upbound.io/v1beta2
// apiVersion: s3.aws.upbound.io/v1beta1
// kind: Bucket
// metadata:
// labels:
@ -108,12 +108,10 @@ func ExampleFrom() {
// spec:
// forProvider:
// region: us-east-2
// status:
// observedGeneration: 0
}
func TestFrom(t *testing.T) {
v1beta2.AddToScheme(Scheme)
v1beta1.AddToScheme(Scheme)
type args struct {
o runtime.Object
@ -130,12 +128,12 @@ func TestFrom(t *testing.T) {
"WithMetadata": {
reason: "A resource with metadata should not grow any extra metadata fields during conversion",
args: args{
o: &v1beta2.Bucket{
o: &v1beta1.Bucket{
ObjectMeta: metav1.ObjectMeta{
Name: "cool-bucket",
},
Spec: v1beta2.BucketSpec{
ForProvider: v1beta2.BucketParameters{
Spec: v1beta1.BucketSpec{
ForProvider: v1beta1.BucketParameters{
Region: ptr.To[string]("us-east-2"),
},
},
@ -143,8 +141,8 @@ func TestFrom(t *testing.T) {
},
want: want{
cd: &Unstructured{Unstructured: unstructured.Unstructured{Object: map[string]any{
"apiVersion": v1beta2.CRDGroupVersion.String(),
"kind": v1beta2.Bucket_Kind,
"apiVersion": v1beta1.CRDGroupVersion.String(),
"kind": v1beta1.Bucket_Kind,
"metadata": map[string]any{
"name": "cool-bucket",
},
@ -153,18 +151,15 @@ func TestFrom(t *testing.T) {
"region": "us-east-2",
},
},
"status": map[string]any{
"observedGeneration": float64(0),
},
}}},
},
},
"WithoutMetadata": {
reason: "A resource with no metadata should not grow a metadata object during conversion",
args: args{
o: &v1beta2.Bucket{
Spec: v1beta2.BucketSpec{
ForProvider: v1beta2.BucketParameters{
o: &v1beta1.Bucket{
Spec: v1beta1.BucketSpec{
ForProvider: v1beta1.BucketParameters{
Region: ptr.To[string]("us-east-2"),
},
},
@ -172,16 +167,13 @@ func TestFrom(t *testing.T) {
},
want: want{
cd: &Unstructured{Unstructured: unstructured.Unstructured{Object: map[string]any{
"apiVersion": v1beta2.CRDGroupVersion.String(),
"kind": v1beta2.Bucket_Kind,
"apiVersion": v1beta1.CRDGroupVersion.String(),
"kind": v1beta1.Bucket_Kind,
"spec": map[string]any{
"forProvider": map[string]any{
"region": "us-east-2",
},
},
"status": map[string]any{
"observedGeneration": float64(0),
},
}}},
},
},

View File

@ -30,7 +30,7 @@ import (
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/reference"
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/claim"
)
// New returns a new unstructured composite resource (XR).
@ -104,8 +104,8 @@ func (xr *Unstructured) SetCompositionReference(ref *corev1.ObjectReference) {
}
// GetCompositionRevisionReference of this composite resource.
func (xr *Unstructured) GetCompositionRevisionReference() *corev1.LocalObjectReference {
out := &corev1.LocalObjectReference{}
func (xr *Unstructured) GetCompositionRevisionReference() *corev1.ObjectReference {
out := &corev1.ObjectReference{}
if err := fieldpath.Pave(xr.Object).GetValueInto("spec.compositionRevisionRef", out); err != nil {
return nil
}
@ -113,7 +113,7 @@ func (xr *Unstructured) GetCompositionRevisionReference() *corev1.LocalObjectRef
}
// SetCompositionRevisionReference of this composite resource.
func (xr *Unstructured) SetCompositionRevisionReference(ref *corev1.LocalObjectReference) {
func (xr *Unstructured) SetCompositionRevisionReference(ref *corev1.ObjectReference) {
_ = fieldpath.Pave(xr.Object).SetValue("spec.compositionRevisionRef", ref)
}
@ -147,8 +147,8 @@ func (xr *Unstructured) GetCompositionUpdatePolicy() *xpv1.UpdatePolicy {
}
// GetClaimReference of this composite resource.
func (xr *Unstructured) GetClaimReference() *reference.Claim {
out := &reference.Claim{}
func (xr *Unstructured) GetClaimReference() *claim.Reference {
out := &claim.Reference{}
if err := fieldpath.Pave(xr.Object).GetValueInto("spec.claimRef", out); err != nil {
return nil
}
@ -156,7 +156,7 @@ func (xr *Unstructured) GetClaimReference() *reference.Claim {
}
// SetClaimReference of this composite resource.
func (xr *Unstructured) SetClaimReference(ref *reference.Claim) {
func (xr *Unstructured) SetClaimReference(ref *claim.Reference) {
_ = fieldpath.Pave(xr.Object).SetValue("spec.claimRef", ref)
}
@ -350,18 +350,3 @@ func (xr *Unstructured) SetBool(path string, value bool) error {
func (xr *Unstructured) SetInteger(path string, value int64) error {
return xr.SetValue(path, value)
}
// SetObservedGeneration of this Composite resource.
func (xr *Unstructured) SetObservedGeneration(generation int64) {
status := &xpv1.ObservedStatus{}
_ = fieldpath.Pave(xr.Object).GetValueInto("status", status)
status.SetObservedGeneration(generation)
_ = fieldpath.Pave(xr.Object).SetValue("status.observedGeneration", status.ObservedGeneration)
}
// GetObservedGeneration of this Composite resource.
func (xr *Unstructured) GetObservedGeneration() int64 {
status := &xpv1.ObservedStatus{}
_ = fieldpath.Pave(xr.Object).GetValueInto("status", status)
return status.GetObservedGeneration()
}

View File

@ -23,7 +23,6 @@ import (
"github.com/go-json-experiment/json"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/structpb"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"github.com/crossplane/function-sdk-go/errors"
@ -52,29 +51,6 @@ type DesiredComposed struct {
Ready Ready
}
// Extra is a resource requested by a Function.
type Extra struct {
Resource *unstructured.Unstructured
}
// CredentialsType is the type of credentials.
type CredentialsType string
const (
// CredentialsTypeData is a Credential of type Data.
CredentialsTypeData = "Data"
)
// Credentials is a secret requested by a Function.
type Credentials struct {
// Type represents the type of credentials.
Type CredentialsType
// Data is a map of key-value pairs where the keys are strings, and the values are byte slices
// containing sensitive data.
Data map[string][]byte
}
// Ready indicates whether a composed resource should be considered ready.
type Ready string
@ -109,7 +85,7 @@ func AsObject(s *structpb.Struct, o runtime.Object) error {
if err != nil {
return errors.Wrapf(err, "cannot marshal %T to JSON", s)
}
return errors.Wrapf(json.Unmarshal(b, o, json.RejectUnknownMembers(true)), "cannot unmarshal JSON from %T into %T", s, o)
return errors.Wrapf(json.Unmarshal(b, o), "cannot unmarshal JSON from %T into %T", s, o)
}
// AsStruct gets the supplied struct from the supplied Kubernetes object.

View File

@ -1,77 +0,0 @@
/*
Copyright 2024 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package response
import (
v1 "github.com/crossplane/function-sdk-go/proto/v1"
)
// ConditionOption allows further customization of the condition.
type ConditionOption struct {
condition *v1.Condition
}
// ConditionTrue will create a condition with the status of true and add the
// condition to the supplied RunFunctionResponse.
func ConditionTrue(rsp *v1.RunFunctionResponse, typ, reason string) *ConditionOption {
return newCondition(rsp, typ, reason, v1.Status_STATUS_CONDITION_TRUE)
}
// ConditionFalse will create a condition with the status of false and add the
// condition to the supplied RunFunctionResponse.
func ConditionFalse(rsp *v1.RunFunctionResponse, typ, reason string) *ConditionOption {
return newCondition(rsp, typ, reason, v1.Status_STATUS_CONDITION_FALSE)
}
// ConditionUnknown will create a condition with the status of unknown and add
// the condition to the supplied RunFunctionResponse.
func ConditionUnknown(rsp *v1.RunFunctionResponse, typ, reason string) *ConditionOption {
return newCondition(rsp, typ, reason, v1.Status_STATUS_CONDITION_UNKNOWN)
}
func newCondition(rsp *v1.RunFunctionResponse, typ, reason string, s v1.Status) *ConditionOption {
if rsp.GetConditions() == nil {
rsp.Conditions = make([]*v1.Condition, 0, 1)
}
c := &v1.Condition{
Type: typ,
Status: s,
Reason: reason,
Target: v1.Target_TARGET_COMPOSITE.Enum(),
}
rsp.Conditions = append(rsp.GetConditions(), c)
return &ConditionOption{condition: c}
}
// TargetComposite updates the condition to target the composite resource.
func (c *ConditionOption) TargetComposite() *ConditionOption {
c.condition.Target = v1.Target_TARGET_COMPOSITE.Enum()
return c
}
// TargetCompositeAndClaim updates the condition to target both the composite
// resource and claim.
func (c *ConditionOption) TargetCompositeAndClaim() *ConditionOption {
c.condition.Target = v1.Target_TARGET_COMPOSITE_AND_CLAIM.Enum()
return c
}
// WithMessage adds the message to the condition.
func (c *ConditionOption) WithMessage(message string) *ConditionOption {
c.condition.Message = &message
return c
}

View File

@ -1,193 +0,0 @@
/*
Copyright 2024 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package response_test
import (
"testing"
"github.com/google/go-cmp/cmp"
"google.golang.org/protobuf/testing/protocmp"
"k8s.io/utils/ptr"
v1 "github.com/crossplane/function-sdk-go/proto/v1"
"github.com/crossplane/function-sdk-go/response"
)
// Condition types.
const (
typeDatabaseReady = "DatabaseReady"
)
// Condition reasons.
const (
reasonAvailable = "ReasonAvailable"
reasonCreating = "ReasonCreating"
reasonPriorFailure = "ReasonPriorFailure"
reasonUnauthorized = "ReasonUnauthorized"
)
func TestCondition(t *testing.T) {
type testFn func(*v1.RunFunctionResponse)
type args struct {
fns []testFn
}
type want struct {
conditions []*v1.Condition
}
cases := map[string]struct {
reason string
args args
want want
}{
"CreateBasicRecords": {
reason: "Correctly adds conditions to the response.",
args: args{
fns: []testFn{
func(rsp *v1.RunFunctionResponse) {
response.ConditionTrue(rsp, typeDatabaseReady, reasonAvailable)
},
func(rsp *v1.RunFunctionResponse) {
response.ConditionFalse(rsp, typeDatabaseReady, reasonCreating)
},
func(rsp *v1.RunFunctionResponse) {
response.ConditionUnknown(rsp, typeDatabaseReady, reasonPriorFailure)
},
},
},
want: want{
conditions: []*v1.Condition{
{
Type: typeDatabaseReady,
Status: v1.Status_STATUS_CONDITION_TRUE,
Reason: reasonAvailable,
Target: v1.Target_TARGET_COMPOSITE.Enum(),
},
{
Type: typeDatabaseReady,
Status: v1.Status_STATUS_CONDITION_FALSE,
Reason: reasonCreating,
Target: v1.Target_TARGET_COMPOSITE.Enum(),
},
{
Type: typeDatabaseReady,
Status: v1.Status_STATUS_CONDITION_UNKNOWN,
Reason: reasonPriorFailure,
Target: v1.Target_TARGET_COMPOSITE.Enum(),
},
},
},
},
"SetTargets": {
reason: "Correctly sets targets on condition and adds it to the response.",
args: args{
fns: []testFn{
func(rsp *v1.RunFunctionResponse) {
response.ConditionTrue(rsp, typeDatabaseReady, reasonAvailable).TargetComposite()
},
func(rsp *v1.RunFunctionResponse) {
response.ConditionTrue(rsp, typeDatabaseReady, reasonAvailable).TargetCompositeAndClaim()
},
},
},
want: want{
conditions: []*v1.Condition{
{
Type: typeDatabaseReady,
Status: v1.Status_STATUS_CONDITION_TRUE,
Reason: reasonAvailable,
Target: v1.Target_TARGET_COMPOSITE.Enum(),
},
{
Type: typeDatabaseReady,
Status: v1.Status_STATUS_CONDITION_TRUE,
Reason: reasonAvailable,
Target: v1.Target_TARGET_COMPOSITE_AND_CLAIM.Enum(),
},
},
},
},
"SetMessage": {
reason: "Correctly sets message on condition and adds it to the response.",
args: args{
fns: []testFn{
func(rsp *v1.RunFunctionResponse) {
response.ConditionTrue(rsp, typeDatabaseReady, reasonAvailable).WithMessage("a test message")
},
},
},
want: want{
conditions: []*v1.Condition{
{
Type: typeDatabaseReady,
Status: v1.Status_STATUS_CONDITION_TRUE,
Reason: reasonAvailable,
Target: v1.Target_TARGET_COMPOSITE.Enum(),
Message: ptr.To("a test message"),
},
},
},
},
"ChainOptions": {
reason: "Can chain condition options together.",
args: args{
fns: []testFn{
func(rsp *v1.RunFunctionResponse) {
response.ConditionTrue(rsp, typeDatabaseReady, reasonAvailable).
WithMessage("a test message").
TargetCompositeAndClaim()
},
func(rsp *v1.RunFunctionResponse) {
response.ConditionTrue(rsp, typeDatabaseReady, reasonAvailable).
TargetCompositeAndClaim().
WithMessage("a test message")
},
},
},
want: want{
conditions: []*v1.Condition{
{
Type: typeDatabaseReady,
Status: v1.Status_STATUS_CONDITION_TRUE,
Reason: reasonAvailable,
Target: v1.Target_TARGET_COMPOSITE_AND_CLAIM.Enum(),
Message: ptr.To("a test message"),
},
{
Type: typeDatabaseReady,
Status: v1.Status_STATUS_CONDITION_TRUE,
Reason: reasonAvailable,
Target: v1.Target_TARGET_COMPOSITE_AND_CLAIM.Enum(),
Message: ptr.To("a test message"),
},
},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
rsp := &v1.RunFunctionResponse{}
for _, f := range tc.args.fns {
f(rsp)
}
if diff := cmp.Diff(tc.want.conditions, rsp.GetConditions(), protocmp.Transform()); diff != "" {
t.Errorf("\n%s\nFrom(...): -want, +got:\n%s", tc.reason, diff)
}
})
}
}

View File

@ -18,13 +18,14 @@ limitations under the License.
package response
import (
"fmt"
"time"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/structpb"
"github.com/crossplane/function-sdk-go/errors"
v1 "github.com/crossplane/function-sdk-go/proto/v1"
"github.com/crossplane/function-sdk-go/proto/v1beta1"
"github.com/crossplane/function-sdk-go/resource"
)
@ -33,9 +34,9 @@ const DefaultTTL = 1 * time.Minute
// To bootstraps a response to the supplied request. It automatically copies the
// desired state from the request.
func To(req *v1.RunFunctionRequest, ttl time.Duration) *v1.RunFunctionResponse {
return &v1.RunFunctionResponse{
Meta: &v1.ResponseMeta{
func To(req *v1beta1.RunFunctionRequest, ttl time.Duration) *v1beta1.RunFunctionResponse {
return &v1beta1.RunFunctionResponse{
Meta: &v1beta1.ResponseMeta{
Tag: req.GetMeta().GetTag(),
Ttl: durationpb.New(ttl),
},
@ -45,7 +46,7 @@ func To(req *v1.RunFunctionRequest, ttl time.Duration) *v1.RunFunctionResponse {
}
// SetContextKey sets context to the supplied key.
func SetContextKey(rsp *v1.RunFunctionResponse, key string, v *structpb.Value) {
func SetContextKey(rsp *v1beta1.RunFunctionResponse, key string, v *structpb.Value) {
if rsp.GetContext().GetFields() == nil {
rsp.Context = &structpb.Struct{Fields: make(map[string]*structpb.Value)}
}
@ -56,12 +57,12 @@ func SetContextKey(rsp *v1.RunFunctionResponse, key string, v *structpb.Value) {
// supplied response. The caller must be sure to avoid overwriting the desired
// state that may have been accumulated by previous Functions in the pipeline,
// unless they intend to.
func SetDesiredCompositeResource(rsp *v1.RunFunctionResponse, xr *resource.Composite) error {
func SetDesiredCompositeResource(rsp *v1beta1.RunFunctionResponse, xr *resource.Composite) error {
if rsp.GetDesired() == nil {
rsp.Desired = &v1.State{}
rsp.Desired = &v1beta1.State{}
}
s, err := resource.AsStruct(xr.Resource)
rsp.Desired.Composite = &v1.Resource{Resource: s, ConnectionDetails: xr.ConnectionDetails}
rsp.Desired.Composite = &v1beta1.Resource{Resource: s, ConnectionDetails: xr.ConnectionDetails}
return errors.Wrapf(err, "cannot convert %T to desired composite resource", xr.Resource)
}
@ -69,28 +70,66 @@ func SetDesiredCompositeResource(rsp *v1.RunFunctionResponse, xr *resource.Compo
// supplied response. The caller must be sure to avoid overwriting the desired
// state that may have been accumulated by previous Functions in the pipeline,
// unless they intend to.
func SetDesiredComposedResources(rsp *v1.RunFunctionResponse, dcds map[resource.Name]*resource.DesiredComposed) error {
func SetDesiredComposedResources(rsp *v1beta1.RunFunctionResponse, dcds map[resource.Name]*resource.DesiredComposed) error {
if rsp.GetDesired() == nil {
rsp.Desired = &v1.State{}
rsp.Desired = &v1beta1.State{}
}
if rsp.GetDesired().GetResources() == nil {
rsp.Desired.Resources = map[string]*v1.Resource{}
rsp.Desired.Resources = map[string]*v1beta1.Resource{}
}
for name, dcd := range dcds {
s, err := resource.AsStruct(dcd.Resource)
if err != nil {
return err
}
r := &v1.Resource{Resource: s}
r := &v1beta1.Resource{Resource: s}
switch dcd.Ready {
case resource.ReadyUnspecified:
r.Ready = v1.Ready_READY_UNSPECIFIED
r.Ready = v1beta1.Ready_READY_UNSPECIFIED
case resource.ReadyFalse:
r.Ready = v1.Ready_READY_FALSE
r.Ready = v1beta1.Ready_READY_FALSE
case resource.ReadyTrue:
r.Ready = v1.Ready_READY_TRUE
r.Ready = v1beta1.Ready_READY_TRUE
}
rsp.Desired.Resources[string(name)] = r
}
return nil
}
// Fatal adds a fatal result to the supplied RunFunctionResponse.
func Fatal(rsp *v1beta1.RunFunctionResponse, err error) {
if rsp.GetResults() == nil {
rsp.Results = make([]*v1beta1.Result, 0, 1)
}
rsp.Results = append(rsp.GetResults(), &v1beta1.Result{
Severity: v1beta1.Severity_SEVERITY_FATAL,
Message: err.Error(),
})
}
// Warning adds a warning result to the supplied RunFunctionResponse.
func Warning(rsp *v1beta1.RunFunctionResponse, err error) {
if rsp.GetResults() == nil {
rsp.Results = make([]*v1beta1.Result, 0, 1)
}
rsp.Results = append(rsp.GetResults(), &v1beta1.Result{
Severity: v1beta1.Severity_SEVERITY_WARNING,
Message: err.Error(),
})
}
// Normal adds a normal result to the supplied RunFunctionResponse.
func Normal(rsp *v1beta1.RunFunctionResponse, message string) {
if rsp.GetResults() == nil {
rsp.Results = make([]*v1beta1.Result, 0, 1)
}
rsp.Results = append(rsp.GetResults(), &v1beta1.Result{
Severity: v1beta1.Severity_SEVERITY_NORMAL,
Message: message,
})
}
// Normalf adds a normal result to the supplied RunFunctionResponse.
func Normalf(rsp *v1beta1.RunFunctionResponse, format string, a ...any) {
Normal(rsp, fmt.Sprintf(format, a...))
}

View File

@ -1,89 +0,0 @@
/*
Copyright 2024 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package response contains utilities for working with RunFunctionResponses.
package response
import (
"fmt"
v1 "github.com/crossplane/function-sdk-go/proto/v1"
)
// ResultOption allows further customization of the result.
type ResultOption struct {
result *v1.Result
}
// Fatal adds a fatal result to the supplied RunFunctionResponse.
// An event will be created for the Composite Resource.
// A fatal result cannot target the claim.
func Fatal(rsp *v1.RunFunctionResponse, err error) {
newResult(rsp, v1.Severity_SEVERITY_FATAL, err.Error())
}
// Warning adds a warning result to the supplied RunFunctionResponse.
// An event will be created for the Composite Resource.
func Warning(rsp *v1.RunFunctionResponse, err error) *ResultOption {
return newResult(rsp, v1.Severity_SEVERITY_WARNING, err.Error())
}
// Normal adds a normal result to the supplied RunFunctionResponse.
// An event will be created for the Composite Resource.
func Normal(rsp *v1.RunFunctionResponse, message string) *ResultOption {
return newResult(rsp, v1.Severity_SEVERITY_NORMAL, message)
}
// Normalf adds a normal result to the supplied RunFunctionResponse.
// An event will be created for the Composite Resource.
func Normalf(rsp *v1.RunFunctionResponse, format string, a ...any) *ResultOption {
return Normal(rsp, fmt.Sprintf(format, a...))
}
func newResult(rsp *v1.RunFunctionResponse, s v1.Severity, message string) *ResultOption {
if rsp.GetResults() == nil {
rsp.Results = make([]*v1.Result, 0, 1)
}
r := &v1.Result{
Severity: s,
Message: message,
Target: v1.Target_TARGET_COMPOSITE.Enum(),
}
rsp.Results = append(rsp.GetResults(), r)
return &ResultOption{result: r}
}
// TargetComposite updates the result and its event to target the composite
// resource.
func (o *ResultOption) TargetComposite() *ResultOption {
o.result.Target = v1.Target_TARGET_COMPOSITE.Enum()
return o
}
// TargetCompositeAndClaim updates the result and its event to target both the
// composite resource and claim.
func (o *ResultOption) TargetCompositeAndClaim() *ResultOption {
o.result.Target = v1.Target_TARGET_COMPOSITE_AND_CLAIM.Enum()
return o
}
// WithReason sets the reason field on the result and its event.
func (o *ResultOption) WithReason(reason string) *ResultOption {
o.result.Reason = &reason
return o
}

View File

@ -1,190 +0,0 @@
/*
Copyright 2023 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package response_test
import (
"errors"
"testing"
"github.com/google/go-cmp/cmp"
"google.golang.org/protobuf/testing/protocmp"
"k8s.io/utils/ptr"
v1 "github.com/crossplane/function-sdk-go/proto/v1"
"github.com/crossplane/function-sdk-go/response"
)
func TestResult(t *testing.T) {
type testFn func(*v1.RunFunctionResponse)
type args struct {
fns []testFn
}
type want struct {
results []*v1.Result
}
cases := map[string]struct {
reason string
args args
want want
}{
"CreateBasicRecords": {
reason: "Correctly adds results to the response.",
args: args{
fns: []testFn{
func(rsp *v1.RunFunctionResponse) {
response.Normal(rsp, "this is a test normal result")
},
func(rsp *v1.RunFunctionResponse) {
response.Normalf(rsp, "this is a test normal %s result", "formatted")
},
func(rsp *v1.RunFunctionResponse) {
response.Warning(rsp, errors.New("this is a test warning result"))
},
func(rsp *v1.RunFunctionResponse) {
response.Fatal(rsp, errors.New("this is a test fatal result"))
},
},
},
want: want{
results: []*v1.Result{
{
Severity: v1.Severity_SEVERITY_NORMAL,
Message: "this is a test normal result",
Target: v1.Target_TARGET_COMPOSITE.Enum(),
},
{
Severity: v1.Severity_SEVERITY_NORMAL,
Message: "this is a test normal formatted result",
Target: v1.Target_TARGET_COMPOSITE.Enum(),
},
{
Severity: v1.Severity_SEVERITY_WARNING,
Message: "this is a test warning result",
Target: v1.Target_TARGET_COMPOSITE.Enum(),
},
{
Severity: v1.Severity_SEVERITY_FATAL,
Message: "this is a test fatal result",
Target: v1.Target_TARGET_COMPOSITE.Enum(),
},
},
},
},
"SetTargets": {
reason: "Correctly sets targets on result and adds it to the response.",
args: args{
fns: []testFn{
func(rsp *v1.RunFunctionResponse) {
response.Warning(rsp, errors.New("this is a test warning result targeting the composite")).TargetComposite()
},
func(rsp *v1.RunFunctionResponse) {
response.Warning(rsp, errors.New("this is a test fatal result targeting both")).TargetCompositeAndClaim()
},
},
},
want: want{
results: []*v1.Result{
{
Severity: v1.Severity_SEVERITY_WARNING,
Message: "this is a test warning result targeting the composite",
Target: v1.Target_TARGET_COMPOSITE.Enum(),
},
{
Severity: v1.Severity_SEVERITY_WARNING,
Message: "this is a test fatal result targeting both",
Target: v1.Target_TARGET_COMPOSITE_AND_CLAIM.Enum(),
},
},
},
},
"SetReason": {
reason: "Correctly sets reason on result and adds it to the response.",
args: args{
fns: []testFn{
func(rsp *v1.RunFunctionResponse) {
response.Normal(rsp, "this is a test normal result targeting the composite").WithReason("TestReason")
},
func(rsp *v1.RunFunctionResponse) {
response.Warning(rsp, errors.New("this is a test warning result targeting the composite")).WithReason("TestReason")
},
},
},
want: want{
results: []*v1.Result{
{
Severity: v1.Severity_SEVERITY_NORMAL,
Message: "this is a test normal result targeting the composite",
Target: v1.Target_TARGET_COMPOSITE.Enum(),
Reason: ptr.To("TestReason"),
},
{
Severity: v1.Severity_SEVERITY_WARNING,
Message: "this is a test warning result targeting the composite",
Target: v1.Target_TARGET_COMPOSITE.Enum(),
Reason: ptr.To("TestReason"),
},
},
},
},
"ChainOptions": {
reason: "Can chain result options together.",
args: args{
fns: []testFn{
func(rsp *v1.RunFunctionResponse) {
response.Normal(rsp, "this is a test normal result targeting the composite and claim").
WithReason("TestReason").
TargetCompositeAndClaim()
},
func(rsp *v1.RunFunctionResponse) {
response.Warning(rsp, errors.New("this is a test warning result targeting the composite and claim")).
TargetCompositeAndClaim().
WithReason("TestReason")
},
},
},
want: want{
results: []*v1.Result{
{
Severity: v1.Severity_SEVERITY_NORMAL,
Message: "this is a test normal result targeting the composite and claim",
Target: v1.Target_TARGET_COMPOSITE_AND_CLAIM.Enum(),
Reason: ptr.To("TestReason"),
},
{
Severity: v1.Severity_SEVERITY_WARNING,
Message: "this is a test warning result targeting the composite and claim",
Target: v1.Target_TARGET_COMPOSITE_AND_CLAIM.Enum(),
Reason: ptr.To("TestReason"),
},
},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
rsp := &v1.RunFunctionResponse{}
for _, f := range tc.args.fns {
f(rsp)
}
if diff := cmp.Diff(tc.want.results, rsp.GetResults(), protocmp.Transform()); diff != "" {
t.Errorf("\n%s\nFrom(...): -want, +got:\n%s", tc.reason, diff)
}
})
}
}

90
sdk.go
View File

@ -18,7 +18,6 @@ limitations under the License.
package function
import (
"context"
"crypto/tls"
"crypto/x509"
"net"
@ -30,32 +29,28 @@ import (
"google.golang.org/grpc/credentials"
ginsecure "google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/reflection"
"google.golang.org/protobuf/proto"
"github.com/crossplane/function-sdk-go/logging"
v1 "github.com/crossplane/function-sdk-go/proto/v1"
"github.com/crossplane/function-sdk-go/proto/v1beta1"
)
// Default ServeOptions.
const (
DefaultNetwork = "tcp"
DefaultAddress = ":9443"
DefaultMaxRecvMsgSize = 1024 * 1024 * 4
DefaultNetwork = "tcp"
DefaultAddress = ":9443"
)
// ServeOptions configure how a Function is served.
type ServeOptions struct {
Network string
Address string
MaxRecvMsgSize int
Credentials credentials.TransportCredentials
Network string
Address string
Credentials credentials.TransportCredentials
}
// A ServeOption configures how a Function is served.
type ServeOption func(o *ServeOptions) error
// Listen configures the network, address and maximum message size on which the Function will
// Listen configures the network and address on which the Function will
// listen for RunFunctionRequests.
func Listen(network, address string) ServeOption {
return func(o *ServeOptions) error {
@ -119,22 +114,12 @@ func Insecure(insecure bool) ServeOption {
}
}
// MaxRecvMessageSize returns a ServeOption to set the max message size in bytes the server can receive.
// If this is not set, gRPC uses the default limit.
func MaxRecvMessageSize(sz int) ServeOption {
return func(o *ServeOptions) error {
o.MaxRecvMsgSize = sz
return nil
}
}
// Serve the supplied Function by creating a gRPC server and listening for
// RunFunctionRequests. Blocks until the server returns an error.
func Serve(fn v1.FunctionRunnerServiceServer, o ...ServeOption) error {
func Serve(fn v1beta1.FunctionRunnerServiceServer, o ...ServeOption) error {
so := &ServeOptions{
Network: DefaultNetwork,
Address: DefaultAddress,
MaxRecvMsgSize: DefaultMaxRecvMsgSize,
Network: DefaultNetwork,
Address: DefaultAddress,
}
for _, fn := range o {
@ -152,10 +137,9 @@ func Serve(fn v1.FunctionRunnerServiceServer, o ...ServeOption) error {
return errors.Wrapf(err, "cannot listen for %s connections at address %q", so.Network, so.Address)
}
srv := grpc.NewServer(grpc.MaxRecvMsgSize(so.MaxRecvMsgSize), grpc.Creds(so.Credentials))
srv := grpc.NewServer(grpc.Creds(so.Credentials))
reflection.Register(srv)
v1.RegisterFunctionRunnerServiceServer(srv, fn)
v1beta1.RegisterFunctionRunnerServiceServer(srv, ServeBeta(fn))
v1beta1.RegisterFunctionRunnerServiceServer(srv, fn)
return errors.Wrap(srv.Serve(lis), "cannot serve mTLS gRPC connections")
}
@ -163,55 +147,3 @@ func Serve(fn v1.FunctionRunnerServiceServer, o ...ServeOption) error {
func NewLogger(debug bool) (logging.Logger, error) {
return logging.NewLogger(debug)
}
// A BetaServer is a v1beta1 FunctionRunnerServiceServer that wraps an identical
// v1 FunctionRunnerServiceServer. This requires the v1 and v1beta1 protos to be
// identical.
//
// Functions were promoted from v1beta1 to v1 in Crossplane v1.17. Crossplane
// v1.16 and earlier only sends v1beta1 RunFunctionRequests. Functions should
// use the BetaServer for backward compatibility, to support Crossplane v1.16
// and earlier.
type BetaServer struct {
wrapped v1.FunctionRunnerServiceServer
v1beta1.UnimplementedFunctionRunnerServiceServer
}
// ServeBeta returns a v1beta1.FunctionRunnerServiceServer that wraps the
// suppled v1.FunctionRunnerServiceServer.
func ServeBeta(s v1.FunctionRunnerServiceServer) *BetaServer {
return &BetaServer{wrapped: s}
}
// RunFunction calls the RunFunction method of the wrapped
// v1.FunctionRunnerServiceServer. It converts from v1beta1 to v1 and back by
// round-tripping through protobuf marshaling.
func (s *BetaServer) RunFunction(ctx context.Context, req *v1beta1.RunFunctionRequest) (*v1beta1.RunFunctionResponse, error) {
gareq := &v1.RunFunctionRequest{}
b, err := proto.Marshal(req)
if err != nil {
return nil, errors.Wrap(err, "cannot marshal v1beta1 RunFunctionRequest to protobuf bytes")
}
if err := proto.Unmarshal(b, gareq); err != nil {
return nil, errors.Wrap(err, "cannot unmarshal v1 RunFunctionRequest from v1beta1 protobuf bytes")
}
garsp, err := s.wrapped.RunFunction(ctx, gareq)
if err != nil {
// This error is intentionally not wrapped. This middleware is just
// calling an underlying RunFunction.
return nil, err
}
b, err = proto.Marshal(garsp)
if err != nil {
return nil, errors.Wrap(err, "cannot marshal v1beta1 RunFunctionResponse to protobuf bytes")
}
rsp := &v1beta1.RunFunctionResponse{}
err = proto.Unmarshal(b, rsp)
return rsp, errors.Wrap(err, "cannot unmarshal v1 RunFunctionResponse from v1beta1 protobuf bytes")
}

View File

@ -17,17 +17,10 @@ limitations under the License.
package function
import (
"context"
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/testing/protocmp"
"github.com/crossplane/function-sdk-go/errors"
v1 "github.com/crossplane/function-sdk-go/proto/v1"
"github.com/crossplane/function-sdk-go/proto/v1beta1"
"github.com/crossplane/function-sdk-go/request"
"github.com/crossplane/function-sdk-go/resource"
@ -35,11 +28,9 @@ import (
"github.com/crossplane/function-sdk-go/response"
)
var _ v1beta1.FunctionRunnerServiceServer = &BetaServer{}
var req = &v1.RunFunctionRequest{
Observed: &v1.State{
Composite: &v1.Resource{
var req = &v1beta1.RunFunctionRequest{
Observed: &v1beta1.State{
Composite: &v1beta1.Resource{
Resource: resource.MustStructJSON(`{"spec":{"widgets":9001}}`),
},
},
@ -70,7 +61,7 @@ func Example() {
desired["new"].Resource.SetInteger("spec.widgets", widgets)
// Create a desired composed resource using structured data.
// db, _ := composed.From(&v1.Instance{})
// db, _ := composed.From(&v1beta1.Instance{})
// desired["database"] = &resource.DesiredComposed{Resource: db}
// Add a label to our new desired resource, and any other.
@ -79,112 +70,11 @@ func Example() {
}
// Set our updated desired composed resource in the response we'll return.
if err := response.SetDesiredComposedResources(rsp, desired); err != nil {
// You can set a custom status condition on the claim. This allows you to
// communicate with the user. See the link below for status condition
// guidance.
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties
response.ConditionFalse(rsp, "FunctionSuccess", "InternalError").
WithMessage("Something went wrong.").
TargetCompositeAndClaim()
// You can emit an event regarding the claim. This allows you to communicate
// with the user. Note that events should be used sparingly and are subject
// to throttling; see the issue below for more information.
// https://github.com/crossplane/crossplane/issues/5802
response.Warning(rsp, errors.New("something went wrong")).
TargetCompositeAndClaim()
} else {
response.ConditionTrue(rsp, "FunctionSuccess", "Success").
TargetCompositeAndClaim()
}
_ = response.SetDesiredComposedResources(rsp, desired)
j, _ := protojson.Marshal(rsp)
fmt.Println(string(j))
// Output:
// {"meta":{"ttl":"60s"},"desired":{"resources":{"new":{"resource":{"apiVersion":"example.org/v1","kind":"CoolResource","metadata":{"labels":{"coolness":"high"}},"spec":{"widgets":9001}}}}},"conditions":[{"type":"FunctionSuccess","status":"STATUS_CONDITION_TRUE","reason":"Success","target":"TARGET_COMPOSITE_AND_CLAIM"}]}
}
func TestBetaServer(t *testing.T) {
type args struct {
ctx context.Context
req *v1beta1.RunFunctionRequest
}
type want struct {
rsp *v1beta1.RunFunctionResponse
err error
}
cases := map[string]struct {
reason string
wrapped v1.FunctionRunnerServiceServer
args args
want want
}{
"RunFunctionError": {
reason: "We should return any error the wrapped server encounters",
wrapped: &MockFunctionServer{err: errors.New("boom")},
args: args{
req: &v1beta1.RunFunctionRequest{
Meta: &v1beta1.RequestMeta{
Tag: "hi",
},
},
},
want: want{
err: cmpopts.AnyError,
},
},
"Success": {
reason: "We should return the response the wrapped server returns",
wrapped: &MockFunctionServer{
rsp: &v1.RunFunctionResponse{
Meta: &v1.ResponseMeta{
Tag: "hello",
},
},
},
args: args{
req: &v1beta1.RunFunctionRequest{
Meta: &v1beta1.RequestMeta{
Tag: "hi",
},
},
},
want: want{
rsp: &v1beta1.RunFunctionResponse{
Meta: &v1beta1.ResponseMeta{
Tag: "hello",
},
},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
s := ServeBeta(tc.wrapped)
rsp, err := s.RunFunction(tc.args.ctx, tc.args.req)
if diff := cmp.Diff(tc.want.rsp, rsp, protocmp.Transform()); diff != "" {
t.Errorf("\n%s\ns.RunFunction(...): -want rsp, +got rsp:\n%s", tc.reason, diff)
}
if diff := cmp.Diff(tc.want.err, err, cmpopts.EquateErrors()); diff != "" {
t.Errorf("\n%s\ns.RunFunction(...): -want err, +got err:\n%s", tc.reason, diff)
}
})
}
}
type MockFunctionServer struct {
v1.UnimplementedFunctionRunnerServiceServer
rsp *v1.RunFunctionResponse
err error
}
func (s *MockFunctionServer) RunFunction(context.Context, *v1.RunFunctionRequest) (*v1.RunFunctionResponse, error) {
return s.rsp, s.err
// {"meta":{"ttl":"60s"},"desired":{"resources":{"new":{"resource":{"apiVersion":"example.org/v1","kind":"CoolResource","metadata":{"labels":{"coolness":"high"}},"spec":{"widgets":9001}}}}}}
}