Skip to content

Commit ec96a6d

Browse files
committed
Slim down the README
Signed-off-by: Nic Cope <nicc@rk0n.org>
1 parent 6016fe0 commit ec96a6d

File tree

4 files changed

+136
-182
lines changed

4 files changed

+136
-182
lines changed

README.md

Lines changed: 89 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,19 @@
11
# function-patch-and-transform
2+
[![CI](https://github.com/crossplane-contrib/function-patch-and-transform/actions/workflows/ci.yml/badge.svg)](https://github.com/crossplane-contrib/function-patch-and-transform/actions/workflows/ci.yml) ![GitHub release (latest SemVer)](https://img.shields.io/github/release/crossplane-contrib/function-patch-and-transform)
23

3-
A [Crossplane] Composition Function that implements P&T-style Composition.
4+
This [composition function][docs-functions] does everything Crossplane's
5+
built-in [patch & transform][docs-pandt] (P&T) composition does. Instead of
6+
specifying `spec.resources` in your Composition, you can use this function.
47

5-
```yaml
6-
apiVersion: pkg.crossplane.io/v1beta1
7-
kind: Function
8-
metadata:
9-
name: function-patch-and-transform
10-
spec:
11-
package: xpkg.upbound.io/crossplane-contrib/function-patch-and-transform:v0.1.4
12-
```
13-
14-
## What is this?
15-
16-
This [Composition Function][function-design] does everything Crossplane's built-in Patch &
17-
Transform Composition does. Instead of specifying `spec.resources` in your
18-
Composition, you can use this Function.
19-
20-
Note that this is a beta-style Function. It won't work with Crossplane v1.13 or
21-
earlier - it targets the [implementation of Functions][function-pr] coming with
22-
Crossplane v1.14 in late October.
23-
24-
Take [this example][docs-composition] from https://docs.crossplane.io. Using
25-
this Function, it would look like this:
8+
Using this function, P&T looks like this:
269

2710
```yaml
2811
apiVersion: apiextensions.crossplane.io/v1
2912
kind: Composition
3013
metadata:
31-
name: dynamo-with-bucket
14+
name: example
3215
spec:
33-
compositeTypeRef:
34-
apiVersion: database.example.com/v1alpha1
35-
kind: NoSQL
16+
# Omitted for brevity.
3617
mode: Pipeline
3718
pipeline:
3819
- step: patch-and-transform
@@ -42,40 +23,14 @@ spec:
4223
apiVersion: pt.fn.crossplane.io/v1beta1
4324
kind: Resources
4425
resources:
45-
- name: s3Bucket
26+
- name: bucket
4627
base:
4728
apiVersion: s3.aws.upbound.io/v1beta1
4829
kind: Bucket
49-
metadata:
50-
name: crossplane-quickstart-bucket
5130
spec:
5231
forProvider:
5332
region: us-east-2
5433
patches:
55-
- type: FromCompositeFieldPath
56-
fromFieldPath: "location"
57-
toFieldPath: "spec.forProvider.region"
58-
transforms:
59-
- type: map
60-
map:
61-
EU: "eu-north-1"
62-
US: "us-east-2"
63-
- name: dynamoDB
64-
base:
65-
apiVersion: dynamodb.aws.upbound.io/v1beta1
66-
kind: Table
67-
metadata:
68-
name: crossplane-quickstart-database
69-
spec:
70-
forProvider:
71-
region: "us-east-2"
72-
writeCapacity: 1
73-
readCapacity: 1
74-
attribute:
75-
- name: S3ID
76-
type: S
77-
hashKey: S3ID
78-
patches:
7934
- type: FromCompositeFieldPath
8035
fromFieldPath: "spec.location"
8136
toFieldPath: "spec.forProvider.region"
@@ -86,168 +41,120 @@ spec:
8641
US: "us-east-2"
8742
```
8843
89-
Notice that it looks pretty much identical to the example from the Crossplane
90-
documentation. The key difference is that everything that used to be under
91-
`spec.resources` is now nested a little deeper. Specifically, it's under
92-
`spec.pipeline[0].input.resources` (i.e. in the Function's input).
44+
Notice that it looks very similar to native P&T. The difference is that
45+
everything is under `spec.pipeline[0].input.resources`, not `spec.resources`.
46+
This is the Function's input.
9347

9448
## Okay, but why?
9549

96-
I think there are a _lot_ of good reasons to implement P&T Composition as a
97-
Function. In fact, I would go so far as to propose that once Functions are a GA
98-
feature we deprecate support for 'native' P&T (i.e. `spec.resources`). We can't
99-
remove it - that would be a breaking change - but we can freeze its API and
100-
suggest folks use the P&T Function instead.
50+
There are a lot of good reasons to use a function to use a function to do P&T
51+
composition. In fact, it's so compelling that the Crossplane maintainers are
52+
considering deprecating native P&T. See Crossplane issue [#4746] for details.
10153

102-
### Run P&T anywhere in your pipeline
54+
### Mix and match P&T with other functions
10355

104-
Native P&T can only run before the Composition Function pipeline. In the [draft
105-
beta implementation of Functions][function-pr] Crossplane does all the patching
106-
and transforming first, then sends the results through the Function pipeline.
56+
With this function you can use P&T with other functions. For example you can
57+
create a desired resource using the [Go Templating][fn-go-templating] function,
58+
then patch the result using this function.
10759

108-
This is handy, but what if you wanted to run another Function (like rendering
109-
some Go templates) first, then pass the result of that Function to be patched
110-
and transformed? With this Function you can do that:
111-
112-
```yaml
113-
apiVersion: apiextensions.crossplane.io/v1
114-
kind: Composition
115-
metadata:
116-
name: dynamo-with-bucket
117-
spec:
118-
compositeTypeRef:
119-
apiVersion: database.example.com/v1alpha1
120-
kind: NoSQL
121-
# This pipeliene renders some Go templates, then passes them to P&T
122-
pipeline:
123-
- step: render-go-templates
124-
functionRef:
125-
name: function-go-templates
126-
input: {} # Omitted for brevity :)
127-
- step: patch-and-transform
128-
functionRef:
129-
name: function-patch-and-transform
130-
input:
131-
apiVersion: pt.fn.crossplane.io/v1beta1
132-
kind: Resources
133-
resources:
134-
# Notice that my-cool-bucket doesn't have a base template. As long as
135-
# the render-go-templates step above rendered a composed resource with
136-
# this name, this Function will patch it.
137-
- name: my-cool-bucket
138-
patches:
139-
- type: FromCompositeFieldPath
140-
fromFieldPath: "location"
141-
toFieldPath: "spec.forProvider.region"
142-
transforms:
143-
- type: map
144-
map:
145-
EU: "eu-north-1"
146-
US: "us-east-2"
147-
```
148-
149-
It's not just patches either - you can use P&T to derive XR connection details
150-
from a resource produced by another Function too, or use it to determine whether
151-
a resource produced by another Function is ready
60+
It's not just patches either. You can use P&T to derive composite resource
61+
connection details from a resource produced by another function, or use it to
62+
determine whether a resource produced by another function is ready
15263

15364
### Decouple P&T development from Crossplane core
15465

155-
When P&T development happens in a Function, it's not coupled to the Crossplane
156-
release cycle. The Function developers could cut releases more frequently to add
157-
new features to P&T.
158-
159-
Plus, because it's just a Function, it becomes easier to fork. You could fork
160-
this Function, add a new kind of transform and try it out for a few weeks in
161-
your development environment before sending a PR upstream. Or, if your new
162-
feature is controversial, it's now a lot less work to maintain your own fork
163-
long term.
66+
When P&T development happens in a function, it's not coupled to the Crossplane
67+
release cycle. The maintainers of this function can cut releases more frequently
68+
to add new features to P&T.
16469

165-
### Makes P&T code more portable
70+
It also becomes easier to fork. You could fork this function, add a new kind of
71+
transform and try it out for a few weeks before sending a PR upstream. Or, if
72+
your new feature is controversial, it's now a lot less work to maintain your own
73+
fork long term.
16674

167-
A lot of building a better developer experience around Composition comes down to
168-
shifting left - letting you run and test your Compositions when you're
169-
developing them. Historically this has been tough. You need to spin up a `kind`
170-
cluster, install Crossplane, install providers, etc.
171-
172-
```bash
173-
$ xp composition render xr.yaml composition.yaml
174-
```
75+
### Test P&T locally using the Crossplane CLI
17576

176-
You could imagine a CLI tool like the above helping a lot. The problem with
177-
building tools like this in the past has been that they need to share
178-
Crossplane's Composition logic. We could make Composition a library, but then
179-
you'd need to make sure that the version of `xp` on your laptop used the same
180-
Composition library as your control planes, or you might see different results
181-
than you expected in production.
77+
You can use the Crossplane CLI to run any function locally and see what composed
78+
resources it would create. This only works with functions - not native P&T.
18279

183-
When all Composition logic is encapsulated in Functions - i.e. versioned OCI
184-
containers with a standard RPC - building a tool like this becomes much easier.
185-
Just tell the CLI what Function versions you're using in production and it can
186-
pull them down and use them to render your Composition.
80+
For example, using the files in the [example](example) directory:
18781

188-
### Makes Crossplane's Composition implementation simpler
189-
190-
If we can make the _native_ P&T implementation and Functions mutually exclusive,
191-
Crossplane's Composition implementation is dramatically less complex. This means
192-
it's easier to maintain and much less likely to be buggy.
82+
```shell
83+
$ crossplane beta render xr.yaml composition.yaml functions.yaml
84+
```
85+
Produces the following output, showing what resources Crossplane would compose:
19386

194-
Moving P&T inside a Function makes this possible - you can still use 'both' P&T
195-
and Functions, you'd just do it by... using Functions.
87+
```yaml
88+
---
89+
apiVersion: example.crossplane.io/v1
90+
kind: XR
91+
metadata:
92+
name: example-xr
93+
---
94+
apiVersion: s3.aws.upbound.io/v1beta1
95+
kind: Bucket
96+
metadata:
97+
annotations:
98+
crossplane.io/composition-resource-name: bucket
99+
generateName: example-xr-
100+
labels:
101+
crossplane.io/composite: example-xr
102+
ownerReferences:
103+
# Omitted for brevity
104+
spec:
105+
forProvider:
106+
region: us-east-2
107+
```
196108

197-
Eventually, if enough P&T users switch to this Function we may be able to remove
198-
native support for P&T altogether.
109+
See the [composition functions documentation][docs-functions] to learn how to
110+
use `crossplane beta render`.
199111

200112
## Differences from the native implementation
201113

202-
This Function has a few small, intentional breaking changes compared to the
203-
native implementation. Making the below fields required makes P&T configuration
204-
a lot more explicit and less ambiguous.
205-
206-
* `resources[i].name` is now a required field.
207-
* `resources[i].connectionDetails[i].name` is now a required field
208-
* `resources[i].connectionDetails[i].type` is now a required field
209-
* `resources[i].patches[i].transforms[i].string.type` is now a required field
210-
* `resources[i].patches[i].transforms[i].math.type` is now a required field
211-
* `resources[i].patches[i].policy.mergeOptions` is no longer supported
114+
This function has a few small, intentional breaking changes compared to the
115+
native implementation.
212116

213-
Functions use Kubernetes server-side apply, not `mergeOptions`, to intelligently
214-
merge arrays and objects. This requires merge configuration to be specified at
215-
the composed resource schema level (i.e. in CRDs) per [#4617].
117+
These fields are now required. This makes P&T configuration less ambiguous:
216118

217-
## Known issues
119+
* `resources[i].name`
120+
* `resources[i].connectionDetails[i].name`
121+
* `resources[i].connectionDetails[i].type`
122+
* `resources[i].patches[i].transforms[i].string.type`
123+
* `resources[i].patches[i].transforms[i].math.type`
218124

219-
The initial implementation has the following limitations:
125+
Also, the `resources[i].patches[i].policy.mergeOptions` field is no longer
126+
supported.
220127

221-
* `EnvironmentConfig` and its associated patches aren't supported yet. This is
222-
just because Crossplane doesn't yet send the `EnvironmentConfig` along with
223-
the `RunFunctionRequest`. Once we do, these should be easy to (re)implement.
224-
Adding support at the Functions level is tracked in [#4632].
128+
Composition functions use Kubernetes server-side apply to intelligently merge
129+
arrays and objects. This requires merge configuration to be specified at the
130+
composed resource schema level (i.e. in CRDs) per [#4617].
225131

226-
## Developing
132+
## Developing this function
227133

228-
This Function doesn't use the typical Crossplane build submodule and Makefile,
229-
since we'd like Functions to have a less heavyweight developer experience.
230-
It mostly relies on regular old Go tools:
134+
This function uses [Go][go], [Docker][docker], and the [Crossplane CLI][cli] to
135+
build functions.
231136

232137
```shell
233138
# Run code generation - see input/generate.go
234139
$ go generate ./...
235140
236-
# Run tests
237-
$ go test -cover ./...
238-
? github.com/crossplane-contrib/function-patch-and-transform/input/v1beta1 [no test files]
239-
ok github.com/crossplane-contrib/function-patch-and-transform 0.021s coverage: 76.1% of statements
141+
# Run tests - see fn_test.go
142+
$ go test ./...
240143
241-
# Lint the code
242-
$ docker run --rm -v $(pwd):/app -v ~/.cache/golangci-lint/v1.54.2:/root/.cache -w /app golangci/golangci-lint:v1.54.2 golangci-lint run
144+
# Build the function's runtime image - see Dockerfile
145+
$ docker build . --tag=runtime
243146
244-
# Build a Docker image - see Dockerfile
245-
$ docker build .
147+
# Build a function package - see package/crossplane.yaml
148+
$ crossplane xpkg build -f package --embed-runtime-image=runtime
246149
```
247150

248151
[Crossplane]: https://crossplane.io
249-
[function-design]: https://github.com/crossplane/crossplane/blob/3996f20/design/design-doc-composition-functions.md
250-
[function-pr]: https://github.com/crossplane/crossplane/pull/4500
251-
[docs-composition]: https://docs.crossplane.io/v1.13/getting-started/provider-aws-part-2/#create-a-deployment-template
152+
[docs-composition]: https://docs.crossplane.io/v1.14/getting-started/provider-aws-part-2/#create-a-deployment-template
153+
[docs-functions]: https://docs.crossplane.io/v1.14/concepts/composition-functions/
154+
[docs-pandt]: https://docs.crossplane.io/v1.14/concepts/patch-and-transform/
155+
[fn-go-templating]: https://github.com/crossplane-contrib/function-go-templating
252156
[#4617]: https://github.com/crossplane/crossplane/issues/4617
253-
[#4632]: https://github.com/crossplane/crossplane/pull/4632
157+
[#4746]: https://github.com/crossplane/crossplane/issues/4746
158+
[go]: https://go.dev
159+
[docker]: https://www.docker.com
160+
[cli]: https://docs.crossplane.io/latest/cli

example/composition.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
apiVersion: apiextensions.crossplane.io/v1
2+
kind: Composition
3+
metadata:
4+
name: function-patch-and-transform
5+
spec:
6+
compositeTypeRef:
7+
apiVersion: example.crossplane.io/v1
8+
kind: XR
9+
mode: Pipeline
10+
pipeline:
11+
- step: patch-and-transform
12+
functionRef:
13+
name: function-patch-and-transform
14+
input:
15+
apiVersion: pt.fn.crossplane.io/v1beta1
16+
kind: Resources
17+
resources:
18+
- name: bucket
19+
base:
20+
apiVersion: s3.aws.upbound.io/v1beta1
21+
kind: Bucket
22+
spec:
23+
forProvider:
24+
region: us-east-2
25+
patches:
26+
- type: FromCompositeFieldPath
27+
fromFieldPath: "location"
28+
toFieldPath: "spec.forProvider.region"
29+
transforms:
30+
- type: map
31+
map:
32+
EU: "eu-north-1"
33+
US: "us-east-2"

example/functions.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
apiVersion: pkg.crossplane.io/v1beta1
3+
kind: Function
4+
metadata:
5+
name: function-patch-and-transform
6+
spec:
7+
package: xpkg.upbound.io/crossplane-contrib/function-patch-and-transform:v0.1.4

example/xr.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Replace this with your XR!
2+
apiVersion: example.crossplane.io/v1
3+
kind: XR
4+
metadata:
5+
name: example-xr
6+
spec:
7+
location: US

0 commit comments

Comments
 (0)