Skip to content

Conversation

yuwenma
Copy link
Contributor

@yuwenma yuwenma commented Jul 13, 2022

This guide focuses on explaining the KRM resource editing patterns.

@yuwenma yuwenma changed the title Function SDK guide [WIP] Function SDK guide Jul 13, 2022
mkdir -p $GOPATH/src/${FUNCTION_PATH} && cd $GOPATH/src/${FUNCTION_PATH}

# Get the "get-started" package.
kpt pkg get https://github.com/GoogleContainerTools/kpt-functions-sdk.git/go/get-started-runner@master ${FUNCTION_NAME}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line didn't work for me ...

Error: path "/go/get-started-runner" does not exist in repo "https://github.com/GoogleContainerTools/kpt-functions-sdk"

Do I need to merge a PR in kpt-functions-sdk?

}
}
```
`FunctionX` implements the [`Runner`] interface that can process the input KRM resources as [`fn.ResourceList`], it initializes `fn.KubeObject` to hold the KRM resources,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to mention ResourceList?

}
```
`FunctionX` implements the [`Runner`] interface that can process the input KRM resources as [`fn.ResourceList`], it initializes `fn.KubeObject` to hold the KRM resources,
so that you can use [`fn.KubeObject` and `fn.SubObject`] methods directly. After `Run`, it will convert the modified `fn.KubeObjects` to KRM resources.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of "it initializes fn.KubeObject to hold the KRM resources, so that you can use [fn.KubeObject and fn.SubObject] methods directly", I would just say that something like "the objects passed to the function are available in items. These objects can be modified in place and are used as the output of the function".

`FunctionX` implements the [`Runner`] interface that can process the input KRM resources as [`fn.ResourceList`], it initializes `fn.KubeObject` to hold the KRM resources,
so that you can use [`fn.KubeObject` and `fn.SubObject`] methods directly. After `Run`, it will convert the modified `fn.KubeObjects` to KRM resources.

### Define configures
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe Define function parameters or Define function arguments?

Otherwise you can skip this step and move to next.
```go
type FunctionX struct {
FnConfigBool bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe SomeBoolValue, SomeIntValue, SomeStringValue instead of the FnConfig prefix - otherwise the question is whether the FnConfig prefix matters

## Write the main logic in `Run`

The SDK will initialize a slice of `*fn.KubeObject` to hold your KRM resources. You will need to pass the
KRM resources from the input in `items` fields.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we've said this already?

// clusterScoped contains only cluster scoped resources
clusterScoped := objects.Where(func(o *fn.KubeObject) bool { return o.IsClusterScoped() })
// customDeployment contains all resources of Kind "CustomDeployment", in Group "fn.kpt.dev" with any Versions.
customDeployment := objects.Where(fn.IsGVK("fn.kpt.dev", "", "CustomDeployment") })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I would support IsGK instead of IsGVK with a magic empty value

The `fn.KubeObjects` is a slice of `*fn.KubeObject`, that you can apply some select logic to easily choose
the target KRM resources. See below example on using `Where` and `WhereNot` to filter different types of resources.
```go
func (r *YourFunction) Run(context *fn.Context, functionConfig *fn.KubeObject, items fn.KubeObjects) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: if these are just examples, don't wrap them in a Run function ... otherwise users will think we're actually doing something here....

```go
func (r *YourFunction) Run(context *fn.Context, functionConfig *fn.KubeObject, items fn.KubeObjects) {
// Get first deployment object.
deployment := items.Where(fn.IsGVK("apps", "v1", "Deployment")).Where(func(o *fn.KubeObject) bool{return o.GetName() == "nginx"})[0]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably want HasName or IsName, but OTOH it is helpful to spell this out.

You could spell it out, and then say "you can also use the IsName helper method, which produces the same filter function"

### Copy `KubeObject` to a typed struct

If you already have some struct to define a KRM resource (like `corev1.ConfigMap`), you can switch the `KubeObject`
to the other type via `As`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add an explicit warning that this copies the object and thus you have to copy it back

Copy link
Contributor

@droot droot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for writing this. I have a few minor comments.

@@ -18,6 +18,8 @@ You can develop a KRM function in Go using [the kpt function SDK].
In this quickstart, we will write a function that adds an annotation
`config.kubernetes.io/managed-by=kpt` to all `Deployment` resources.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: config.kubernetes.io/managed-by:kpt ( s/=/:)

This guide gives tips to effectively write a KRM function.

This guide is for advanced kpt function users who find the [catalog.kpt.dev] cannot fulfil their needs
and want to design their own KRM functions.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think advanced kpt users can confuse users and might discourage users to read further.

Maybe we can combine this line and previous line with something along the lines of ...

This guide describes best practices and patterns on how to structure your Go KRM functions.

This guide is for advanced kpt function users who find the [catalog.kpt.dev] cannot fulfil their needs
and want to design their own KRM functions.

Suggest reading [Developing in Go] first
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit . missing at the end.

## Prerequisites

- [Install kpt]
- [Install Docker]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be good to specify minimum version of Go. I think Go 1.17+ is what we have been using for the toolset.

mkdir -p $GOPATH/src/${FUNCTION_PATH} && cd $GOPATH/src/${FUNCTION_PATH}

# Get the "get-started" package.
kpt pkg get https://github.com/GoogleContainerTools/kpt-functions-sdk.git/go/get-started-runner@master ${FUNCTION_NAME}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming myself as a Go developer, I will find this step to be awkward :) and I will also be surprised to see files such as Kptfile etc. It might get confusing. Can we just use git clone or something to fetch the skeleton code ?

```go
func (r *YourFunction) Run(context *fn.Context, functionConfig *fn.KubeObject, items fn.KubeObjects) {
// Get first deployment object.
deployment := items.Where(fn.IsGVK("apps", "v1", "Deployment")).Where(func(o *fn.KubeObject) bool{return o.GetName() == "nginx"})[0]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's okay to have this expression in multiple lines that helps with readability.

@@ -107,5 +107,6 @@
- [Namespace Provisioning UI](guides/namespace-provisioning-ui.md)
- [Variant Constructor Pattern](guides/variant-constructor-pattern.md)
- [Value Propagation Pattern](guides/value-propagation.md)
- [Effective Go KRM functions](guides/effective-go-krm-function.md)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it makes sense to have it alongside Getting Started instead of guides ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants