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 }
Click to show internal directories.
Click to hide internal directories.