Files
Davanum Srinivas 9a61304919 bump k8s.io/kube-openapi, drop github.com/mailru/easyjson
Updates k8s.io/kube-openapi from v0.0.0-20241212222426-2c72e554b1e7 to
v0.0.0-20260502001324-b7f5293f4787 across api, kyaml, and kustomize modules,
then propagates the change across all workspace modules via make workspace-sync.

The new kube-openapi release switched from the monolithic go-openapi/swag
(v0.23.0) to the refactored split sub-modules (go-openapi/swag/* v0.25.4).
The old swag carried github.com/mailru/easyjson as a dependency; the new
sub-modules do not. After go mod tidy, easyjson and josharian/intern are
fully removed from all three module graphs.

Also upgraded: github.com/google/gnostic-models v0.6.9 → v0.7.0 (pulled
in by the same kube-openapi update).

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
2026-05-02 10:47:24 -04:00
..

Kyaml Functions Framework Example: Application Custom Resource

This is a moderate-complexity example of a KRM function built using the KRM Functions Framework package. It demonstrates how to write a function that implements a custom resource (CR) representing an abstract application.

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.

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, which invokes a "dispatcher" 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).

	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. 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. Internally, the filter function mostly builds up and executes a framework.TemplateProcessor.

The ExampleApp type is annotated with kubebuilder markers, and a Go generator uses those to create the CRD YAML in 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.

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"`
}
func (a ExampleApp) Filter(items []*yaml.RNode) ([]*yaml.RNode, error) {
func (a *ExampleApp) Schema() (*spec.Schema, error) {
func (a *ExampleApp) Validate() error {
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.

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. 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. For example: app-fn pkg/exampleapp/v1alpha1/testdata/success/basic/config.yaml.