Style and copy feedback from review

Review was of the Python guide, but I've applied everything that was
also applicable to the Go guide.

Signed-off-by: Nic Cope <nicc@rk0n.org>
This commit is contained in:
Nic Cope 2024-01-12 15:04:06 -08:00
parent 42f17d9650
commit 87516d51ee
2 changed files with 85 additions and 81 deletions

View File

@ -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.
<!-- vale gitlab.FutureTense = YES -->
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.
{{<hint "note">}}
You don't need access to a Kubernetes cluster or a Crossplane control plane to
build or test a composition function.
{{</hint>}}
## 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.
<!-- vale gitlab.FutureTense = YES -->
{{</hint>}}
@ -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.
{{<hint "important">}}
If you're writing a function that does use an input type, don't delete the
`input` and `package/input` directories.
{{<hint "tip">}}
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
{{<hover label="hello-world" line="1">}}RunFunctionRequest{{</hover>}} struct.
The function tells Crossplane what resources it should compose by returning a
{{<hover label="hello-world" line="13">}}RunFunctionResponse{{</hover>}} 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.
{{<expand "The xr.yaml, composition.yaml and function.yaml files">}}
@ -597,6 +600,8 @@ spec:
- crossplane-functions-example-c
```
<br />
The `composition.yaml` file contains the Composition to use to render the
composite resource:
@ -616,6 +621,8 @@ spec:
name: function-xbuckets
```
<br />
The `functions.yaml` file contains the Functions the Composition references in
its pipeline steps:
@ -633,7 +640,7 @@ spec:
```
{{</expand>}}
Note that the Function in `functions.yaml` uses the
The Function in `functions.yaml` uses the
{{<hover label="development" line="6">}}Development{{</hover>}}
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
{{<hover label="run" line="1">}}--insecure{{</hover>}}
flag tells the function to run without encryption or authentication. You should
only use it during testing and development. The
{{<hover label="run" line="1">}}--debug{{</hover>}} flag tells the function to
print debug log statements.
Use `go run` to run your function locally.
```shell {label="run"}
go run . --insecure --debug
```
{{<hint "warning">}}
The {{<hover label="run" line="1">}}insecure{{</hover>}} flag tells the function
to run without encryption or authentication. Only use it during testing and
development.
{{</hint>}}
In a separate terminal, run `crossplane beta render`.
```shell

View File

@ -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.
<!-- vale gitlab.FutureTense = YES -->
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.
{{<hint "note">}}
You don't need access to a Kubernetes cluster or a Crossplane control plane to
build or test a composition function.
{{</hint>}}
## 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.
<!-- vale gitlab.FutureTense = YES -->
{{</hint>}}
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.
{{<hint "important">}}
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.
{{<hint "tip">}}
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.
{{</hint>}}
## 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
{{<hover label="hello-world" line="7">}}RunFunctionRequest{{</hover>}} object.
The function tells Crossplane what resources it should compose by returning a
{{<hover label="hello-world" line="22">}}RunFunctionResponse{{</hover>}} object.
{{<hover label="hello-world" line="1">}}RunFunctionRequest{{</hover>}} object.
{{<hint "tip">}}
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).
{{</hint>}}
The function tells Crossplane what resources it should compose by returning a
{{<hover label="hello-world" line="15">}}RunFunctionResponse{{</hover>}} 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`.
{{<hint "tip">}}
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.
{{<hint "tip">}}
Read [the Python Function SDK documentation](https://crossplane.github.io/function-sdk-python).
{{</hint>}}
{{<hint "important">}}
@ -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`.
{{</hint>}}
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.
{{<expand "The xr.yaml, composition.yaml and function.yaml files">}}
@ -511,6 +500,8 @@ spec:
- crossplane-functions-example-c
```
<br />
The `composition.yaml` file contains the Composition to use to render the
composite resource:
@ -530,6 +521,8 @@ spec:
name: function-xbuckets
```
<br />
The `functions.yaml` file contains the Functions the Composition references in
its pipeline steps:
@ -547,7 +540,7 @@ spec:
```
{{</expand>}}
Note that the Function in `functions.yaml` uses the
The Function in `functions.yaml` uses the
{{<hover label="development" line="6">}}Development{{</hover>}}
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
```
{{<hint "warning">}}
`hatch run development` runs the function without encryption or authentication.
Only use it during testing and development.
{{</hint>}}
In a separate terminal, run `crossplane beta render`.
```shell