-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5130 from KnVerey/fn-framework-example
Add a rich example of fn framework for abstraction
- Loading branch information
Showing
28 changed files
with
1,469 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
export KUSTOMIZE_ROOT ?= $(shell pwd | sed -E 's|(.*\/kustomize)/(.*)|\1|') | ||
include $(KUSTOMIZE_ROOT)/Makefile-modules.mk | ||
|
||
CONTROLLER_GEN_VERSION=v0.11.3 | ||
|
||
generate: $(MYGOBIN)/controller-gen $(MYGOBIN)/embedmd | ||
go generate ./... | ||
embedmd -w README.md | ||
|
||
build: generate | ||
go build -v -o $(MYGOBIN)/app-fn cmd/main.go | ||
|
||
$(MYGOBIN)/controller-gen: | ||
go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_GEN_VERSION) | ||
|
||
$(MYGOBIN)/embedmd: | ||
go install github.com/campoy/embedmd@v1.0.0 | ||
|
||
.PHONY: example | ||
example: build | ||
$(MYGOBIN)/app-fn pkg/exampleapp/testdata/success/basic/config.yaml | ||
|
||
|
||
test: generate | ||
go test -v -timeout 45m -cover ./... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
## Kyaml Functions Framework Example: Application Custom Resource | ||
|
||
This is a moderate-complexity example of a KRM function built using the [KRM Functions Framework package](https://pkg.go.dev/sigs.k8s.io/kustomize/kyaml/fn/framework). It demonstrates how to write a function that implements a custom resource (CR) representing an abstract application. | ||
|
||
[embedmd]:# (pkg/exampleapp/v1alpha1/testdata/success/basic/config.yaml) | ||
```yaml | ||
apiVersion: platform.example.com/v1alpha1 | ||
kind: ExampleApp | ||
metadata: | ||
name: simple-app-sample | ||
env: production | ||
workloads: | ||
webWorkers: | ||
- name: web-worker | ||
domains: | ||
- example.com | ||
jobWorkers: | ||
- name: job-worker | ||
replicas: 10 | ||
resources: medium | ||
queues: | ||
- high | ||
- medium | ||
- low | ||
- name: job-worker-2 | ||
replicas: 5 | ||
queues: | ||
- bg2 | ||
datastores: | ||
postgresInstance: simple-app-sample-postgres | ||
``` | ||
|
||
It also demonstrates the pattern of having the CR accept patches, allowing the user to customize the final result beyond the fields the CR exposes. | ||
|
||
[embedmd]:# (pkg/exampleapp/v1alpha1/testdata/success/overrides/config.yaml) | ||
```yaml | ||
apiVersion: platform.example.com/v1alpha1 | ||
kind: ExampleApp | ||
metadata: | ||
name: simple-app-sample | ||
env: production | ||
workloads: | ||
webWorkers: | ||
- name: web-worker | ||
domains: | ||
- first.example.com | ||
- name: web-worker-no-sidecar | ||
domains: | ||
- second.example.com | ||
|
||
overrides: | ||
additionalResources: | ||
- custom-configmap.yaml | ||
resourcePatches: | ||
- web-worker-sidecar.yaml | ||
containerPatches: | ||
- custom-app-env.yaml | ||
``` | ||
|
||
## Implementation walkthrough | ||
|
||
The entrypoint for the function is [cmd/main.go](cmd/main.go), which invokes a ["dispatcher"](pkg/dispatcher/dispatcher.go) that determines which `Filter` implements the resource passed in. The dispatcher pattern allows a single function binary to handle multiple CRs, and is also useful for evolving a single CR over time (e.g. handle `ExampleApp` API versions `example.com/v1beta1` and `example.com/v1`). | ||
|
||
[embedmd]:# (pkg/dispatcher/dispatcher.go go /.*VersionedAPIProcessor.*/ /}}/) | ||
```go | ||
p := framework.VersionedAPIProcessor{FilterProvider: framework.GVKFilterMap{ | ||
"ExampleApp": map[string]kio.Filter{ | ||
"platform.example.com/v1alpha1": &v1alpha1.ExampleApp{}, | ||
}, | ||
}} | ||
``` | ||
|
||
|
||
The ExampleApp type is defined in [pkg/exampleapp/v1alpha1/types.go](pkg/exampleapp/v1alpha1/types.go). It is responsible for implementing the logic of the CR, most of which is done by implementing the `kyaml.Filter` interface in [pkg/exampleapp/v1alpha1/processing.go](pkg/exampleapp/v1alpha1/processing.go). Internally, the filter function mostly builds up and executes a `framework.TemplateProcessor`. | ||
|
||
The ExampleApp type is annotated with [kubebuilder markers](https://book.kubebuilder.io/reference/markers/crd-validation.html), and a Go generator uses those to create the CRD YAML in [pkg/exampleapp/v1alpha1/platform.example.com_exampleapps.yaml](pkg/exampleapp/v1alpha1/platform.example.com_exampleapps.yaml). The CR then implements `framework.ValidationSchemaProvider`, which causes the CRD to be used for validation. It also implements `framework.Validator` to add custom validations and `framework.Defaulter` to add defaulting. | ||
|
||
[embedmd]:# (pkg/exampleapp/v1alpha1/types.go go /.*type ExampleApp.*/ /}/) | ||
```go | ||
type ExampleApp struct { | ||
// Embedding these structs is required to use controller-gen to produce the CRD | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ObjectMeta `json:"metadata"` | ||
|
||
// +kubebuilder:validation:Enum=production;staging;development | ||
Env string `json:"env" yaml:"env"` | ||
|
||
// +optional | ||
AppImage string `json:"appImage" yaml:"appImage"` | ||
|
||
Workloads Workloads `json:"workloads" yaml:"workloads"` | ||
|
||
// +optional | ||
Datastores Datastores `json:"datastores,omitempty" yaml:"datastores,omitempty"` | ||
|
||
// +optional | ||
Overrides Overrides `json:"overrides,omitempty" yaml:"overrides,omitempty"` | ||
} | ||
``` | ||
|
||
[embedmd]:# (pkg/exampleapp/v1alpha1/processing.go go /.*Filter.*/ /error\) {/) | ||
```go | ||
func (a ExampleApp) Filter(items []*yaml.RNode) ([]*yaml.RNode, error) { | ||
``` | ||
[embedmd]:# (pkg/exampleapp/v1alpha1/processing.go go /.*Schema.*/ /error\) {/) | ||
```go | ||
func (a *ExampleApp) Schema() (*spec.Schema, error) { | ||
``` | ||
[embedmd]:# (pkg/exampleapp/v1alpha1/processing.go go /.*Validate.*/ /error {/) | ||
```go | ||
func (a *ExampleApp) Validate() error { | ||
``` | ||
[embedmd]:# (pkg/exampleapp/v1alpha1/processing.go go /.*Default.*/ /error {/) | ||
```go | ||
func (a *ExampleApp) Default() error { | ||
``` | ||
## Running the Example | ||
There are three ways to try this out: | ||
A. Run `make example` in the root of the example to run the function with the test data in [pkg/exampleapp/v1alpha1/testdata/success/basic](pkg/exampleapp/v1alpha1/testdata/success/basic). | ||
B. Run `go run cmd/main.go [FILE]` in the root of the example. Try it with the test input from one of the cases in [pkg/exampleapp/v1alpha1/testdata/success](pkg/exampleapp/v1alpha1/testdata/success). For example: `go run cmd/main.go pkg/exampleapp/v1alpha1/testdata/success/basic/config.yaml`. | ||
C. Build the binary with `make build`, then run it with `app-fn [FILE]`. Try it with the test input from one of the cases in [pkg/exampleapp/v1alpha1/testdata/success](pkg/exampleapp/v1alpha1/testdata/success). For example: `app-fn pkg/exampleapp/v1alpha1/testdata/success/basic/config.yaml`. | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// Copyright 2023 The Kubernetes Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package main | ||
|
||
import "sigs.k8s.io/kustomize/functions/examples/fn-framework-application/pkg/dispatcher" | ||
|
||
func main() { | ||
_ = dispatcher.NewCommand().Execute() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
module sigs.k8s.io/kustomize/functions/examples/fn-framework-application | ||
|
||
go 1.19 | ||
|
||
require ( | ||
github.com/spf13/cobra v1.4.0 | ||
k8s.io/apimachinery v0.27.0 | ||
k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a | ||
sigs.k8s.io/kustomize/kyaml v0.14.1 | ||
) | ||
|
||
require ( | ||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/go-errors/errors v1.4.2 // indirect | ||
github.com/go-logr/logr v1.2.3 // indirect | ||
github.com/go-openapi/jsonpointer v0.19.6 // indirect | ||
github.com/go-openapi/jsonreference v0.20.1 // indirect | ||
github.com/go-openapi/swag v0.22.3 // indirect | ||
github.com/gogo/protobuf v1.3.2 // indirect | ||
github.com/golang/protobuf v1.5.3 // indirect | ||
github.com/google/gnostic v0.5.7-v3refs // indirect | ||
github.com/google/go-cmp v0.5.9 // indirect | ||
github.com/google/gofuzz v1.1.0 // indirect | ||
github.com/inconshreveable/mousetrap v1.0.0 // indirect | ||
github.com/josharian/intern v1.0.0 // indirect | ||
github.com/json-iterator/go v1.1.12 // indirect | ||
github.com/mailru/easyjson v0.7.7 // indirect | ||
github.com/mitchellh/mapstructure v1.4.1 // indirect | ||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||
github.com/modern-go/reflect2 v1.0.2 // indirect | ||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
github.com/rogpeppe/go-internal v1.10.0 // indirect | ||
github.com/spf13/pflag v1.0.5 // indirect | ||
github.com/stretchr/testify v1.8.1 // indirect | ||
github.com/xlab/treeprint v1.1.0 // indirect | ||
golang.org/x/net v0.8.0 // indirect | ||
golang.org/x/sys v0.6.0 // indirect | ||
golang.org/x/text v0.8.0 // indirect | ||
google.golang.org/protobuf v1.28.1 // 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/klog/v2 v2.90.1 // indirect | ||
k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect | ||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect | ||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect | ||
sigs.k8s.io/yaml v1.3.0 // indirect | ||
) |
Oops, something went wrong.