diff --git a/content/knowledge-base/guides/write-a-composition-function-in-go.md b/content/knowledge-base/guides/write-a-composition-function-in-go.md
index 28b2b495..cc0e6cec 100644
--- a/content/knowledge-base/guides/write-a-composition-function-in-go.md
+++ b/content/knowledge-base/guides/write-a-composition-function-in-go.md
@@ -53,13 +53,13 @@ An `XBuckets` XR has a region and an array of bucket names. The function will
create an Amazon Web Services (AWS) S3 bucket for each entry in the names array.
-To write a function in Go you:
+To write a function in Go:
-1. Install the tools you need to write the function
-1. Initialize the function from a template
-1. Edit the template to add the function's logic
-1. Test the function end-to-end
-1. Build and push the function to a package repository
+1. [Install the tools you need to write the function](#install-the-tools-you-need-to-write-the-function)
+1. [Initialize the function from a template](#initialize-the-function-from-a-template)
+1. [Edit the template to add the function's logic](#edit-the-template-to-add-the-functions-logic)
+1. [Test the function end-to-end](#test-the-function-end-to-end)
+1. [Build and push the function to a package repository](#build-and-push-the-function-to-a-package-registry)
This guide covers each of these steps in detail.
@@ -72,14 +72,16 @@ To write a function in Go you need:
* The [Crossplane CLI](https://docs.crossplane.io/latest/cli) v1.14 or newer. This guide uses Crossplane
CLI v1.14.
+{{}}
You don't need access to a Kubernetes cluster or a Crossplane control plane to
build or test a composition function.
+{{}}
## Initialize the function from a template
Use the `crossplane beta xpkg init` command to initialize a new function. When
you run this command it initializes your function using
-[this GitHub repository](https://github.com/crossplane/function-template-go)
+[a GitHub repository](https://github.com/crossplane/function-template-go)
as a template.
```shell {copy-lines=1}
@@ -110,9 +112,9 @@ some other files in the template:
This tip talks about future plans for Crossplane.
-->
In v1.14 of the Crossplane CLI `crossplane beta xpkg init` just clones a
-template GitHub repository. In a future release the command will automate tasks
-like replacing the template name with the new function's name. See Crossplane
-issue [#4941](https://github.com/crossplane/crossplane/issues/4941) for details.
+template GitHub repository. A future CLI release will automate tasks like
+replacing the template name with the new function's name. See Crossplane issue
+[#4941](https://github.com/crossplane/crossplane/issues/4941) for details.
{{}}
@@ -138,14 +140,12 @@ documentation explains how to pass an input to a composition function.
The `package/input` directory contains an OpenAPI schema generated from the
structs in the `input` directory.
-{{}}
-If you're writing a function that does use an input type, don't delete the
-`input` and `package/input` directories.
+{{}}
+If you're writing a function that uses an input, edit the input to meet your
+function's requirements.
-Instead rename the type from `Input` to something more specific to your
-function. For example Function Patch and Transform names its input type
-`Resources`. Rename the API version too by updating the `// +groupName` comment
-at the top of `input.go`.
+Change the input's kind and API group. Don't use `Input` and
+`template.fn.crossplane.io`. Instead use something meaningful to your function.
When you edit files under the `input` directory you must update some generated
files by running `go generate`. See `input/generate.go` for details.
@@ -182,6 +182,7 @@ func (f *Function) RunFunction(_ context.Context, req *fnv1beta1.RunFunctionRequ
All Go composition functions have a `RunFunction` method. Crossplane passes
everything the function needs to run in a
{{}}RunFunctionRequest{{}} struct.
+
The function tells Crossplane what resources it should compose by returning a
{{}}RunFunctionResponse{{}} struct.
@@ -194,7 +195,7 @@ using [Protocol Buffers](http://protobuf.dev). You can find detailed schemas for
Edit the `RunFunction` method to replace it with this code.
-```go
+```go {hl_lines="4-56"}
func (f *Function) RunFunction(_ context.Context, req *fnv1beta1.RunFunctionRequest) (*fnv1beta1.RunFunctionResponse, error) {
rsp := response.To(req, response.DefaultTTL)
@@ -427,8 +428,8 @@ for the SDK.
## Test the function end-to-end
-You can test your function by adding unit tests, and by using the `crossplane
-beta render` command. It's a good idea to do both.
+Test your function by adding unit tests, and by using the `crossplane beta
+render` command.
Go has rich support for unit testing. When you initialize a function from the
template it adds some unit tests to `fn_test.go`. These tests follow Go's
@@ -574,8 +575,10 @@ ok github.com/negz/function-xbuckets 0.016s coverage: 52.6% of state
You can preview the output of a Composition that uses this function using
the Crossplane CLI. You don't need a Crossplane control plane to do this.
-Create a directory under `function-xbuckets` named `example`, and add the
-three files `xr.yaml`, `composition.yaml`, and `functions.yaml`.
+Create a directory under `function-xbuckets` named `example` and create
+Composite Resource, Composition and Function YAML files.
+
+Expand the following block to see example files.
{{}}
@@ -597,6 +600,8 @@ spec:
- crossplane-functions-example-c
```
+
+
The `composition.yaml` file contains the Composition to use to render the
composite resource:
@@ -616,6 +621,8 @@ spec:
name: function-xbuckets
```
+
+
The `functions.yaml` file contains the Functions the Composition references in
its pipeline steps:
@@ -633,7 +640,7 @@ spec:
```
{{}}
-Note that the Function in `functions.yaml` uses the
+The Function in `functions.yaml` uses the
{{}}Development{{}}
runtime. This tells `crossplane beta render` that your function is running
locally. It connects to your locally running function instead of using Docker to
@@ -648,17 +655,18 @@ metadata:
render.crossplane.io/runtime: Development
```
-Use `go run` to run your function locally. The
-{{}}--insecure{{}}
-flag tells the function to run without encryption or authentication. You should
-only use it during testing and development. The
-{{}}--debug{{}} flag tells the function to
-print debug log statements.
+Use `go run` to run your function locally.
```shell {label="run"}
go run . --insecure --debug
```
+{{}}
+The {{}}insecure{{}} flag tells the function
+to run without encryption or authentication. Only use it during testing and
+development.
+{{}}
+
In a separate terminal, run `crossplane beta render`.
```shell
diff --git a/content/knowledge-base/guides/write-a-composition-function-in-python.md b/content/knowledge-base/guides/write-a-composition-function-in-python.md
index dba2e75a..db10b7c6 100644
--- a/content/knowledge-base/guides/write-a-composition-function-in-python.md
+++ b/content/knowledge-base/guides/write-a-composition-function-in-python.md
@@ -53,13 +53,13 @@ An `XBuckets` XR has a region and an array of bucket names. The function will
create an Amazon Web Services (AWS) S3 bucket for each entry in the names array.
-To write a function in Python you:
+To write a function in Python:
-1. Install the tools you need to write the function
-1. Initialize the function from a template
-1. Edit the template to add the function's logic
-1. Test the function end-to-end
-1. Build and push the function to a package repository
+1. [Install the tools you need to write the function](#install-the-tools-you-need-to-write-the-function)
+1. [Initialize the function from a template](#initialize-the-function-from-a-template)
+1. [Edit the template to add the function's logic](#edit-the-template-to-add-the-functions-logic)
+1. [Test the function end-to-end](#test-the-function-end-to-end)
+1. [Build and push the function to a package repository](#build-and-push-the-function-to-a-package-registry)
This guide covers each of these steps in detail.
@@ -73,14 +73,16 @@ To write a function in Python you need:
* The [Crossplane CLI](https://docs.crossplane.io/latest/cli) v1.14 or newer. This guide uses Crossplane
CLI v1.14.
+{{}}
You don't need access to a Kubernetes cluster or a Crossplane control plane to
build or test a composition function.
+{{}}
## Initialize the function from a template
Use the `crossplane beta xpkg init` command to initialize a new function. When
you run this command it initializes your function using
-[this GitHub repository](https://github.com/crossplane/function-template-python)
+[a GitHub repository](https://github.com/crossplane/function-template-python)
as a template.
```shell {copy-lines=1}
@@ -117,33 +119,28 @@ know about some other files in the template:
This tip talks about future plans for Crossplane.
-->
In v1.14 of the Crossplane CLI `crossplane beta xpkg init` just clones a
-template GitHub repository. In a future release the command will automate tasks
-like replacing the template name with the new function's name. See Crossplane
-issue [#4941](https://github.com/crossplane/crossplane/issues/4941) for details.
+template GitHub repository. A future CLI release will automate tasks like
+replacing the template name with the new function's name. See Crossplane issue
+[#4941](https://github.com/crossplane/crossplane/issues/4941) for details.
{{}}
Edit `package/crossplane.yaml` to change the package's name before you start
adding code. Name your package `function-xbuckets`.
-Some functions accept a configuration input. You configure the input when you
-write a Composition that uses the function. The `package/input` directory
-defines the OpenAPI schema for the a function's input.
+The `package/input` directory defines the OpenAPI schema for the a function's
+input. The function in this guide doesn't accept an input. Delete the
+`package/input` directory.
-The function in this guide doesn't accept an input. For this function you
-should delete the `package/input` directory. The
-[composition functions](https://docs.crossplane.io/latest/concepts/composition-functions)
-documentation explains more the input to a composition function.
+The [composition functions](https://docs.crossplane.io/latest/concepts/composition-functions)
+documentation explains composition function inputs.
-{{}}
-If you're writing a function that does use an input type, don't delete the
-`package/input` directory. Instead edit the file to be specific to your
-function.
+{{}}
+If you're writing a function that uses an input, edit the input YAML file to
+meet your function's requirements.
-The kind `Input` is a placeholder value. The API group
-`template.fn.crossplane.io` is, too. Change the kind and API group to something
-meaningful to your function. Edit the `openAPIV3Schema` to represent your
-function's input schema.
+Change the input's kind and API group. Don't use `Input` and
+`template.fn.crossplane.io`. Instead use something meaningful to your function.
{{}}
## Edit the template to add the function's logic
@@ -173,20 +170,14 @@ async def RunFunction(self, req: fnv1beta1.RunFunctionRequest, _: grpc.aio.Servi
All Python composition functions have a `RunFunction` method. Crossplane passes
everything the function needs to run in a
-{{}}RunFunctionRequest{{}} object.
-The function tells Crossplane what resources it should compose by returning a
-{{}}RunFunctionResponse{{}} object.
+{{}}RunFunctionRequest{{}} object.
-{{}}
-Crossplane generates the `RunFunctionRequest` and `RunFunctionResponse` objects
-using [Protocol Buffers](https://protobuf.dev). You can find detailed schemas
-for `RunFunctionRequest` and `RunFunctionResponse` in the
-[Buf Schema Registry](https://buf.build/crossplane/crossplane/docs/main:apiextensions.fn.proto.v1beta1).
-{{}}
+The function tells Crossplane what resources it should compose by returning a
+{{}}RunFunctionResponse{{}} object.
Edit the `RunFunction` method to replace it with this code.
-```python
+```python {hl_lines="7-28"}
async def RunFunction(self, req: fnv1beta1.RunFunctionRequest, _: grpc.aio.ServicerContext) -> fnv1beta1.RunFunctionResponse:
log = self.log.bind(tag=req.meta.tag)
log.info("Running function")
@@ -304,14 +295,13 @@ This code:
1. Adds one desired S3 bucket for each bucket name.
1. Returns the desired S3 buckets in a `RunFunctionResponse`.
-
-
-{{}}
Crossplane provides a
[software development kit](https://github.com/crossplane/function-sdk-python)
(SDK) for writing composition functions in Python. This function uses utilities
-from the SDK. Read the
-[documentation](https://crossplane.github.io/function-sdk-python) for the SDK.
+from the SDK.
+
+{{}}
+Read [the Python Function SDK documentation](https://crossplane.github.io/function-sdk-python).
{{}}
{{}}
@@ -321,8 +311,7 @@ The Python SDK automatically generates the `RunFunctionRequest` and
[Buf Schema Registry](https://buf.build/crossplane/crossplane/docs/main:apiextensions.fn.proto.v1beta1).
The fields of the generated Python objects behave similarly to builtin Python
-types like dictionaries and lists. You should be aware that there are some
-differences.
+types like dictionaries and lists. Be aware that there are some differences.
Notably, you access the map of observed and desired resources like a dictionary
but you can't add a new desired resource by assigning to a map key. Instead,
@@ -349,8 +338,8 @@ for further details.
## Test the function end-to-end
-You can test your function by adding unit tests, and by using the `crossplane
-beta render` command. It's a good idea to do both.
+Test your function by adding unit tests, and by using the `crossplane beta
+render` command.
When you initialize a function from the
template it adds some unit tests to `tests/test_fn.py`. These tests use the
@@ -481,15 +470,15 @@ OK
artifacts like wheels. It also manages virtual environments, similar
to `virtualenv` or `venv`. The `hatch run` command creates a virtual environment
and runs a command in that environment.
-
-You configure Hatch using `pyproject.toml`.
{{}}
You can preview the output of a Composition that uses this function using
the Crossplane CLI. You don't need a Crossplane control plane to do this.
-Create a directory under `function-xbuckets` named `example`, and add the
-three files `xr.yaml`, `composition.yaml`, and `functions.yaml`.
+Create a directory under `function-xbuckets` named `example` and create
+Composite Resource, Composition and Function YAML files.
+
+Expand the following block to see example files.
{{}}
@@ -511,6 +500,8 @@ spec:
- crossplane-functions-example-c
```
+
+
The `composition.yaml` file contains the Composition to use to render the
composite resource:
@@ -530,6 +521,8 @@ spec:
name: function-xbuckets
```
+
+
The `functions.yaml` file contains the Functions the Composition references in
its pipeline steps:
@@ -547,7 +540,7 @@ spec:
```
{{}}
-Note that the Function in `functions.yaml` uses the
+The Function in `functions.yaml` uses the
{{}}Development{{}}
runtime. This tells `crossplane beta render` that your function is running
locally. It connects to your locally running function instead of using Docker to
@@ -562,14 +555,17 @@ metadata:
render.crossplane.io/runtime: Development
```
-Use `hatch run development` to run your function locally. This tells the
-function to run without encryption or authentication. You should only use it
-during testing and development.
+Use `hatch run development` to run your function locally.
```shell {label="run"}
hatch run development
```
+{{}}
+`hatch run development` runs the function without encryption or authentication.
+Only use it during testing and development.
+{{}}
+
In a separate terminal, run `crossplane beta render`.
```shell