example

package module
v0.0.0-...-275ae58 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 12, 2025 License: Apache-2.0 Imports: 1 Imported by: 0

Documentation

Overview

Example (AReadField)
package main

import (
	"os"

	"github.com/kptdev/krm-functions-sdk/go/fn"
)

// In this example, we read a field from the input object and print it to the log.

func main() {
	if err := fn.AsMain(fn.ResourceListProcessorFunc(readField)); err != nil {
		os.Exit(1)
	}
}

func readField(rl *fn.ResourceList) (bool, error) {
	for _, obj := range rl.Items.Where(fn.IsGVK("apps", "v1", "Deployment")) {
		// Style 1: like using unstrucuted.Unstructured, get/set the value from field paths*
		replicas, _, _ := obj.NestedInt64("spec", "replicas")
		fn.Logf("replicas is %v\n", replicas)
		paused, _, _ := obj.NestedBool("spec", "paused")
		fn.Logf("paused is %v\n", paused)
		// Update strategy from Recreate to RollingUpdate.
		if strategy, _, _ := obj.NestedString("spec", "strategy", "type"); strategy == "Recreate" {
			if err := obj.SetNestedString("RollingUpdate", "spec", "strategy", "type"); err != nil {
				return false, err
			}
		}

		// Style 2: operate each resource layer via `GetMap`
		spec := obj.GetMap("spec")
		replicas = spec.GetInt("replicas")
		fn.Logf("replicas is %v\n", replicas)
		nodeSelector := spec.GetMap("template").GetMap("spec").GetMap("nodeSelector")
		if nodeSelector.GetString("disktype") != "ssd" {
			if err := nodeSelector.SetNestedString("ssd", "disktype"); err != nil {
				return false, err
			}
		}
	}
	return true, nil
}
Example (AsMain)

This example uses a SetLabels object, which implements `Runner.Run` methods.

The input from ./data/setlabels-resourcelist.yaml: apiVersion: config.kubernetes.io/v1 kind: ResourceList items:

  • apiVersion: v1 kind: Service metadata: name: example

functionConfig:

apiVersion: fn.kpt.dev/v1alpha1
kind: SetLabels
metadata:
  name: setlabel-fn-config
package main

import (
	"context"
	"os"

	"github.com/kptdev/krm-functions-sdk/go/fn"
)

var _ fn.Runner = &SetLabels{}

type SetLabels struct {
	Labels map[string]string `json:"labels,omitempty"`
}

// Run is the main function logic.
// `ctx` provides easy methods to add info, error or warning result to `ResourceList.Results`.
// `items` is parsed from the STDIN "ResourceList.Items".
// `functionConfig` is from the STDIN "ResourceList.FunctionConfig". The value has been assigned to the r.Labels
//
//	the functionConfig is validated to have kind "SetLabels" and apiVersion "fn.kpt.dev/v1alpha1"
func (r *SetLabels) Run(ctx *fn.Context, functionConfig *fn.KubeObject, items fn.KubeObjects, results *fn.Results) bool {
	for _, o := range items {
		for k, newLabel := range r.Labels {
			err := o.SetLabel(k, newLabel)
			if err != nil {
				results.ErrorE(err)
				// continue even if error occurs, we want the final results to show all errors.
			}
		}
	}
	if results.ExitCode() != 1 {
		results.Infof("updated labels")
	}
	return true
}

// This example uses a SetLabels object, which implements `Runner.Run` methods.
//
// The input from ./data/setlabels-resourcelist.yaml:
// apiVersion: config.kubernetes.io/v1
// kind: ResourceList
// items:
//   - apiVersion: v1
//     kind: Service
//     metadata:
//     name: example
//
// functionConfig:
//
//	apiVersion: fn.kpt.dev/v1alpha1
//	kind: SetLabels
//	metadata:
//	  name: setlabel-fn-config
func main() {
	file, _ := os.Open("./data/setlabels-resourcelist.yaml")
	defer file.Close()
	os.Stdin = file
	ctx := context.TODO()
	if err := fn.AsMain(fn.WithContext(ctx, &SetLabels{})); err != nil {
		os.Exit(1)
	}
}
Output:

apiVersion: config.kubernetes.io/v1
kind: ResourceList
items:
- apiVersion: v1
  kind: Service
  metadata:
    name: example
functionConfig:
  apiVersion: fn.kpt.dev/v1alpha1
  kind: SetLabels
  metadata:
    name: setlabel-fn-config
results:
- message: updated labels
  severity: info
Example (BReadFunctionConfig)
package main

import (
	"os"

	"github.com/kptdev/krm-functions-sdk/go/fn"
	yaml2 "sigs.k8s.io/kustomize/kyaml/yaml"
)

// In this example, we convert the functionConfig as strong typed object and then
// read a field from the functionConfig object.

func main() {
	if err := fn.AsMain(fn.ResourceListProcessorFunc(readFunctionConfig)); err != nil {
		os.Exit(1)
	}
}

func readFunctionConfig(rl *fn.ResourceList) (bool, error) {
	var sr SetReplicas
	if err := rl.FunctionConfig.As(&sr); err != nil {
		return false, err
	}
	fn.Logf("desired replicas is %v\n", sr.DesiredReplicas)
	return true, nil
}

// SetReplicas is the type definition of the functionConfig
type SetReplicas struct {
	yaml2.ResourceIdentifier `json:",inline" yaml:",inline"`
	DesiredReplicas          int `json:"desiredReplicas,omitempty" yaml:"desiredReplicas,omitempty"`
}
Example (BuiltinFunction)
package main

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/kptdev/krm-functions-sdk/go/fn"
)

// This example removes all resources as a builtin function by using fn.Execute.

type RemoveAllResources struct{}

func (*RemoveAllResources) Process(rl *fn.ResourceList) (bool, error) {
	rl.Items = nil
	return true, nil
}

func main() {
	reader := strings.NewReader(`
apiVersion: config.kubernetes.io/v1
kind: ResourceList
items:
- kind: Deployment
  metadata:
    name: my-deploy
- kind: Service
  metadata:
    name: my-service
functionConfig:
  apiVersion: fn.kpt.dev/v1alpha1
  kind: RemoveAllResources
  metadata:
    name: fn-config`)

	var writer bytes.Buffer
	err := fn.Execute(&RemoveAllResources{}, reader, &writer)
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Println(writer.String())

}
Output:

apiVersion: config.kubernetes.io/v1
kind: ResourceList
items: []
functionConfig:
  apiVersion: fn.kpt.dev/v1alpha1
  kind: RemoveAllResources
  metadata:
    name: fn-config
Example (CSetField)
package main

import (
	"os"

	"github.com/kptdev/krm-functions-sdk/go/fn"
)

// In this example, we read a field from the input object and print it to the log.

func main() {
	if err := fn.AsMain(fn.ResourceListProcessorFunc(setField)); err != nil {
		os.Exit(1)
	}
}

func setField(rl *fn.ResourceList) (bool, error) {
	for _, obj := range rl.Items {
		if obj.GetAPIVersion() == "apps/v1" && obj.GetKind() == "Deployment" {
			replicas := 10
			if err := obj.SetNestedField(&replicas, "spec", "replicas"); err != nil {
				return false, err
			}
		}
	}
	return true, nil
}
Example (DMutateComments)
package main

import (
	"os"
	"strings"

	"github.com/kptdev/krm-functions-sdk/go/fn"
)

// In this example, we mutate line comments for field metadata.name.
// Some function may want to store some information in the comments (e.g.
// apply-setters function: https://catalog.kpt.dev/apply-setters/v0.2/)

func main() {
	if err := fn.AsMain(fn.ResourceListProcessorFunc(mutateComments)); err != nil {
		os.Exit(1)
	}
}

func mutateComments(rl *fn.ResourceList) (bool, error) {
	for i := range rl.Items {
		lineComment, found, err := rl.Items[i].LineComment("metadata", "name")
		if err != nil {
			return false, err
		}
		if !found {
			return true, nil
		}

		if strings.TrimSpace(lineComment) == "" {
			lineComment = "bar-system"
		} else {
			lineComment = strings.Replace(lineComment, "foo", "bar", -1)
		}
		if err = rl.Items[i].SetLineComment(lineComment, "metadata", "name"); err != nil {
			return false, err
		}
	}
	return true, nil
}
Example (FilterGVK)
package main

import (
	"os"

	"github.com/kptdev/krm-functions-sdk/go/fn"
)

// This example implements a function that updates the replicas field for all deployments.

func main() {
	if err := fn.AsMain(fn.ResourceListProcessorFunc(updateReplicas)); err != nil {
		os.Exit(1)
	}
}

// updateReplicas sets a field in resources selecting by GVK.
func updateReplicas(rl *fn.ResourceList) (bool, error) {
	if rl.FunctionConfig == nil {
		return false, fn.ErrMissingFnConfig{}
	}
	var replicas int
	found, err := rl.FunctionConfig.NestedResource(&replicas, "replicas")
	if err != nil || !found {
		return found, err
	}
	for i := range rl.Items.Where(fn.IsGVK("apps", "v1", "Deployment")) {
		if err := rl.Items[i].SetNestedField(replicas, "spec", "replicas"); err != nil {
			return false, err
		}
	}
	return true, nil
}
Example (Generator)
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"

	"github.com/kptdev/krm-functions-sdk/go/fn"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// This function generates Graphana configuration in the form of ConfigMap. It
// accepts Revision and ID as input.

func main() {
	if err := fn.AsMain(fn.ResourceListProcessorFunc(generate)); err != nil {
		os.Exit(1)
	}
}

// generate generates a ConfigMap.
func generate(rl *fn.ResourceList) (bool, error) {
	if rl.FunctionConfig == nil {
		return false, fn.ErrMissingFnConfig{}
	}

	revision, _, _ := rl.FunctionConfig.NestedString("data", "revision")
	id, _, _ := rl.FunctionConfig.NestedString("data", "id")
	js, err := fetchDashboard(revision, id)
	if err != nil {
		return false, fmt.Errorf("fetch dashboard: %v", err)
	}

	cm := corev1.ConfigMap{
		TypeMeta: metav1.TypeMeta{
			APIVersion: "v1",
			Kind:       "ConfigMap",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      fmt.Sprintf("%v-gen", rl.FunctionConfig.GetName()),
			Namespace: rl.FunctionConfig.GetNamespace(),
			Labels: map[string]string{
				"grafana_dashboard": "true",
			},
		},
		Data: map[string]string{
			fmt.Sprintf("%v.json", rl.FunctionConfig.GetName()): fmt.Sprintf("%q", js),
		},
	}
	return true, rl.UpsertObjectToItems(cm, nil, false)
}

func fetchDashboard(revision, id string) (string, error) {
	url := fmt.Sprintf("https://grafana.com/api/dashboards/%s/revisions/%s/download", id, revision)
	resp, err := http.Get(url) // nolint:gosec
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	b, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(b), nil
}
Example (KubeObjectMutatePrimitiveField)
package main

import (
	"github.com/kptdev/krm-functions-sdk/go/fn"
)

var deployment fn.KubeObject

func main() {
	spec := deployment.GetMap("spec")
	replicas := spec.GetInt("replicas")
	// mutate the replicas variable

	err := spec.SetNestedInt(int(replicas))
	if err != nil {
		panic(err)
	}
}
Example (KubeObjectMutatePrimitiveMap)
package main

import (
	"github.com/kptdev/krm-functions-sdk/go/fn"
)

var (
	deployment fn.KubeObject
	configMap  fn.KubeObject
)

func main() {
	data, _, _ := configMap.NestedStringMap("data")
	// mutate the data map

	err := deployment.SetNestedStringMap(data, "data")
	if err != nil { /* do something */
	}
}
Example (KubeObjectMutatePrimitiveSlice)
package main

import (
	"github.com/kptdev/krm-functions-sdk/go/fn"
)

var deployment fn.KubeObject

func main() {
	finalizers, _, _ := deployment.NestedStringSlice("metadata", "finalizers")
	// mutate the finalizers slice

	err := deployment.SetNestedStringSlice(finalizers, "metadata", "finalizers")
	if err != nil {
		panic(err)
	}
}
Example (KubeObjectMutateStrongTypedField)
package main

import (
	"github.com/kptdev/krm-functions-sdk/go/fn"
	corev1 "k8s.io/api/core/v1"
)

var (
	deployment fn.KubeObject
	configMap  fn.KubeObject
)

func main() {
	var newPodTemplate corev1.PodTemplate
	curPodTemplate := configMap.GetMap("spec").GetMap("template")
	// Assign the current PodTemplate value to newPodTemplate
	// Use As to AsMain handles the errors.
	err := curPodTemplate.As(&newPodTemplate)
	if err != nil {
		panic(err)
	}
	// mutate the newPodTemplate object
	err = deployment.SetNestedField(newPodTemplate, "spec", "template")
	if err != nil { /* do something */
		panic(err)
	}
}
Example (KubeObjectMutateStrongTypedSlice)
package main

import (
	"github.com/kptdev/krm-functions-sdk/go/fn"
	corev1 "k8s.io/api/core/v1"
)

var deployment fn.KubeObject

func main() {
	var containers []corev1.Container
	found, err := deployment.NestedResource(&containers, "spec", "template", "spec", "containers")
	if err != nil { /* do something */
	}
	if !found { /* do something */
	}

	// mutate the podTemplate object

	err = deployment.SetNestedField(containers, "spec", "template", "spec", "containers")
	if err != nil { /* do something */
	}
}
Example (LoggeInjector)
package main

import (
	"os"

	corev1 "k8s.io/api/core/v1"
	yaml2 "sigs.k8s.io/kustomize/kyaml/yaml"

	"github.com/kptdev/krm-functions-sdk/go/fn"
)

// In this example, we implement a function that injects a logger as a sidecar
// container in workload APIs.

func main() {
	if err := fn.AsMain(fn.ResourceListProcessorFunc(injectLogger)); err != nil {
		os.Exit(1)
	}
}

// injectLogger injects a logger container into the workload API resources.
// generate implements the gokrmfn.KRMFunction interface.
func injectLogger(rl *fn.ResourceList) (bool, error) {
	var li LoggerInjection
	if err := rl.FunctionConfig.As(&li); err != nil {
		return false, err
	}
	for i, obj := range rl.Items.Where(hasDesiredGVK) {
		var containers []corev1.Container
		found, err := obj.NestedResource(&containers, "spec", "template", "spec", "containers")
		if err != nil || !found {
			return found, err
		}
		foundTargetContainer := false
		for j, container := range containers {
			if container.Name == li.ContainerName {
				containers[j].Image = li.ImageName
				foundTargetContainer = true
				break
			}
		}
		if !foundTargetContainer {
			c := corev1.Container{
				Name:  li.ContainerName,
				Image: li.ImageName,
			}
			containers = append(containers, c)
		}
		if err = rl.Items[i].SetNestedField(containers, "spec", "template", "spec", "containers"); err != nil {
			return false, nil
		}
	}
	return true, nil
}

// LoggerInjection is type definition of the functionConfig.
type LoggerInjection struct {
	yaml2.ResourceMeta `json:",inline" yaml:",inline"`

	ContainerName string `json:"containerName" yaml:"containerName"`
	ImageName     string `json:"imageName" yaml:"imageName"`
}
Example (SelectExclude)
package main

import (
	"os"

	"github.com/kptdev/krm-functions-sdk/go/fn"
)

// This example implements a function that selectively includes or excludes some resources.

func main() {
	if err := fn.AsMain(fn.ResourceListProcessorFunc(selectResources)); err != nil {
		os.Exit(1)
	}
}

// selectResources keeps all resources with the GVK apps/v1 Deployment that do
// NOT have the label foo=bar, and removes the rest.
func selectResources(rl *fn.ResourceList) (bool, error) {
	rl.Items = rl.Items.Where(fn.IsGVK("apps", "v1", "Deployment")).
		WhereNot(fn.HasLabels(map[string]string{"foo": "bar"}))
	return true, nil
}
Example (Validator)

This example implements a function that validate resources to ensure spec.template.spec.securityContext.runAsNonRoot is set in workload APIs.

package main

import (
	"os"

	"github.com/kptdev/krm-functions-sdk/go/fn"
)

// This example implements a function that validate resources to ensure
// spec.template.spec.securityContext.runAsNonRoot is set in workload APIs.
func main() {
	if err := fn.AsMain(fn.ResourceListProcessorFunc(validator)); err != nil {
		os.Exit(1)
	}
}

func validator(rl *fn.ResourceList) (bool, error) {
	var results fn.Results
	for _, obj := range rl.Items.Where(hasDesiredGVK) {
		var runAsNonRoot bool
		if _, err := obj.NestedResource(&runAsNonRoot, "spec", "template", "spec", "securityContext", "runAsNonRoot"); err != nil {
			return false, err
		}
		if !runAsNonRoot {
			results = append(results, fn.ConfigObjectResult("`spec.template.spec.securityContext.runAsNonRoot` must be set to true", obj, fn.Error))
		}
	}
	return true, results
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL