Skip to content

Commit 315ed56

Browse files
authored
Merge pull request #5130 from KnVerey/fn-framework-example
Add a rich example of fn framework for abstraction
2 parents d3184da + 38d5bf8 commit 315ed56

28 files changed

+1469
-4
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,11 @@ test-unit-all: \
134134
# This target is used by our Github Actions CI to run unit tests for all non-plugin modules in multiple GOOS environments.
135135
.PHONY: test-unit-non-plugin
136136
test-unit-non-plugin:
137-
./hack/for-each-module.sh "make test" "./plugin/*" 15
137+
./hack/for-each-module.sh "make test" "./plugin/*" 16
138138

139139
.PHONY: build-non-plugin-all
140140
build-non-plugin-all:
141-
./hack/for-each-module.sh "make build" "./plugin/*" 15
141+
./hack/for-each-module.sh "make build" "./plugin/*" 16
142142

143143
.PHONY: test-unit-kustomize-plugins
144144
test-unit-kustomize-plugins:
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
export KUSTOMIZE_ROOT ?= $(shell pwd | sed -E 's|(.*\/kustomize)/(.*)|\1|')
2+
include $(KUSTOMIZE_ROOT)/Makefile-modules.mk
3+
4+
CONTROLLER_GEN_VERSION=v0.11.3
5+
6+
generate: $(MYGOBIN)/controller-gen $(MYGOBIN)/embedmd
7+
go generate ./...
8+
embedmd -w README.md
9+
10+
build: generate
11+
go build -v -o $(MYGOBIN)/app-fn cmd/main.go
12+
13+
$(MYGOBIN)/controller-gen:
14+
go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_GEN_VERSION)
15+
16+
$(MYGOBIN)/embedmd:
17+
go install github.com/campoy/[email protected]
18+
19+
.PHONY: example
20+
example: build
21+
$(MYGOBIN)/app-fn pkg/exampleapp/testdata/success/basic/config.yaml
22+
23+
24+
test: generate
25+
go test -v -timeout 45m -cover ./...
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
## Kyaml Functions Framework Example: Application Custom Resource
2+
3+
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.
4+
5+
[embedmd]:# (pkg/exampleapp/v1alpha1/testdata/success/basic/config.yaml)
6+
```yaml
7+
apiVersion: platform.example.com/v1alpha1
8+
kind: ExampleApp
9+
metadata:
10+
name: simple-app-sample
11+
env: production
12+
workloads:
13+
webWorkers:
14+
- name: web-worker
15+
domains:
16+
- example.com
17+
jobWorkers:
18+
- name: job-worker
19+
replicas: 10
20+
resources: medium
21+
queues:
22+
- high
23+
- medium
24+
- low
25+
- name: job-worker-2
26+
replicas: 5
27+
queues:
28+
- bg2
29+
datastores:
30+
postgresInstance: simple-app-sample-postgres
31+
```
32+
33+
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.
34+
35+
[embedmd]:# (pkg/exampleapp/v1alpha1/testdata/success/overrides/config.yaml)
36+
```yaml
37+
apiVersion: platform.example.com/v1alpha1
38+
kind: ExampleApp
39+
metadata:
40+
name: simple-app-sample
41+
env: production
42+
workloads:
43+
webWorkers:
44+
- name: web-worker
45+
domains:
46+
- first.example.com
47+
- name: web-worker-no-sidecar
48+
domains:
49+
- second.example.com
50+
51+
overrides:
52+
additionalResources:
53+
- custom-configmap.yaml
54+
resourcePatches:
55+
- web-worker-sidecar.yaml
56+
containerPatches:
57+
- custom-app-env.yaml
58+
```
59+
60+
## Implementation walkthrough
61+
62+
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`).
63+
64+
[embedmd]:# (pkg/dispatcher/dispatcher.go go /.*VersionedAPIProcessor.*/ /}}/)
65+
```go
66+
p := framework.VersionedAPIProcessor{FilterProvider: framework.GVKFilterMap{
67+
"ExampleApp": map[string]kio.Filter{
68+
"platform.example.com/v1alpha1": &v1alpha1.ExampleApp{},
69+
},
70+
}}
71+
```
72+
73+
74+
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`.
75+
76+
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.
77+
78+
[embedmd]:# (pkg/exampleapp/v1alpha1/types.go go /.*type ExampleApp.*/ /}/)
79+
```go
80+
type ExampleApp struct {
81+
// Embedding these structs is required to use controller-gen to produce the CRD
82+
metav1.TypeMeta `json:",inline"`
83+
metav1.ObjectMeta `json:"metadata"`
84+
85+
// +kubebuilder:validation:Enum=production;staging;development
86+
Env string `json:"env" yaml:"env"`
87+
88+
// +optional
89+
AppImage string `json:"appImage" yaml:"appImage"`
90+
91+
Workloads Workloads `json:"workloads" yaml:"workloads"`
92+
93+
// +optional
94+
Datastores Datastores `json:"datastores,omitempty" yaml:"datastores,omitempty"`
95+
96+
// +optional
97+
Overrides Overrides `json:"overrides,omitempty" yaml:"overrides,omitempty"`
98+
}
99+
```
100+
101+
[embedmd]:# (pkg/exampleapp/v1alpha1/processing.go go /.*Filter.*/ /error\) {/)
102+
```go
103+
func (a ExampleApp) Filter(items []*yaml.RNode) ([]*yaml.RNode, error) {
104+
```
105+
106+
[embedmd]:# (pkg/exampleapp/v1alpha1/processing.go go /.*Schema.*/ /error\) {/)
107+
```go
108+
func (a *ExampleApp) Schema() (*spec.Schema, error) {
109+
```
110+
111+
[embedmd]:# (pkg/exampleapp/v1alpha1/processing.go go /.*Validate.*/ /error {/)
112+
```go
113+
func (a *ExampleApp) Validate() error {
114+
```
115+
116+
[embedmd]:# (pkg/exampleapp/v1alpha1/processing.go go /.*Default.*/ /error {/)
117+
```go
118+
func (a *ExampleApp) Default() error {
119+
```
120+
121+
122+
## Running the Example
123+
124+
There are three ways to try this out:
125+
126+
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).
127+
128+
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`.
129+
130+
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`.
131+
132+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2023 The Kubernetes Authors.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package main
5+
6+
import "sigs.k8s.io/kustomize/functions/examples/fn-framework-application/pkg/dispatcher"
7+
8+
func main() {
9+
_ = dispatcher.NewCommand().Execute()
10+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
module sigs.k8s.io/kustomize/functions/examples/fn-framework-application
2+
3+
go 1.19
4+
5+
require (
6+
github.com/spf13/cobra v1.4.0
7+
k8s.io/apimachinery v0.27.0
8+
k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a
9+
sigs.k8s.io/kustomize/kyaml v0.14.1
10+
)
11+
12+
require (
13+
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect
14+
github.com/davecgh/go-spew v1.1.1 // indirect
15+
github.com/go-errors/errors v1.4.2 // indirect
16+
github.com/go-logr/logr v1.2.3 // indirect
17+
github.com/go-openapi/jsonpointer v0.19.6 // indirect
18+
github.com/go-openapi/jsonreference v0.20.1 // indirect
19+
github.com/go-openapi/swag v0.22.3 // indirect
20+
github.com/gogo/protobuf v1.3.2 // indirect
21+
github.com/golang/protobuf v1.5.3 // indirect
22+
github.com/google/gnostic v0.5.7-v3refs // indirect
23+
github.com/google/go-cmp v0.5.9 // indirect
24+
github.com/google/gofuzz v1.1.0 // indirect
25+
github.com/inconshreveable/mousetrap v1.0.0 // indirect
26+
github.com/josharian/intern v1.0.0 // indirect
27+
github.com/json-iterator/go v1.1.12 // indirect
28+
github.com/mailru/easyjson v0.7.7 // indirect
29+
github.com/mitchellh/mapstructure v1.4.1 // indirect
30+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
31+
github.com/modern-go/reflect2 v1.0.2 // indirect
32+
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
33+
github.com/pmezard/go-difflib v1.0.0 // indirect
34+
github.com/rogpeppe/go-internal v1.10.0 // indirect
35+
github.com/spf13/pflag v1.0.5 // indirect
36+
github.com/stretchr/testify v1.8.1 // indirect
37+
github.com/xlab/treeprint v1.1.0 // indirect
38+
golang.org/x/net v0.8.0 // indirect
39+
golang.org/x/sys v0.6.0 // indirect
40+
golang.org/x/text v0.8.0 // indirect
41+
google.golang.org/protobuf v1.28.1 // indirect
42+
gopkg.in/inf.v0 v0.9.1 // indirect
43+
gopkg.in/yaml.v2 v2.4.0 // indirect
44+
gopkg.in/yaml.v3 v3.0.1 // indirect
45+
k8s.io/klog/v2 v2.90.1 // indirect
46+
k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
47+
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
48+
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
49+
sigs.k8s.io/yaml v1.3.0 // indirect
50+
)

0 commit comments

Comments
 (0)