Skip to content

Commit 79bcf2f

Browse files
committed
- Avoid *OrDie. Use type-aware functinos.
- Avoid panic style error handling
1 parent 4eefb51 commit 79bcf2f

15 files changed

+219
-404
lines changed

go/fn/doc.go

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@
1515
/*
1616
Package fn provides the SDK to write KRM functions.
1717
18-
Before you start
18+
# Before you start
1919
2020
This fn SDK requires some basic KRM function Specification knowledge. To make the best usage of your time, we recommend
2121
you to be familiar with "ResourceList" before moving forward.
2222
23-
The KRM Function Specification, or "ResourceList", defines the standards of the inter-process communication between
24-
the orchestrator (i.e. kpt CLI) and functions.
23+
The KRM Function Specification, or "ResourceList", defines the standards of the inter-process communication between
24+
the orchestrator (i.e. kpt CLI) and functions.
2525
2626
See KRM Function Specification reference in https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md
2727
28-
KRM Function
28+
# KRM Function
2929
3030
A KRM function can mutate and/or validate Kubernetes resources in a ResourceList.
3131
@@ -37,21 +37,21 @@ Read more about how to use KRM functions in https://kpt.dev/book/04-using-functi
3737
Read more about how to develop a KRM function in https://kpt.dev/book/05-developing-functions/
3838
3939
A general workflow is:
40-
1. Reads the "ResourceList" object from STDIN.
41-
2. Gets the function configs from the "ResourceList.FunctionConfig".
42-
3. Mutate or validate the Kubernetes YAML resources from the "ResourceList.Items" field with the function configs.
43-
4. Writes the modified "ResourceList" to STDOUT.
44-
5. Write function message to "ResourceList.Results" with severity "Info", "Warning" or "Error"
40+
1. Reads the "ResourceList" object from STDIN.
41+
2. Gets the function configs from the "ResourceList.FunctionConfig".
42+
3. Mutate or validate the Kubernetes YAML resources from the "ResourceList.Items" field with the function configs.
43+
4. Writes the modified "ResourceList" to STDOUT.
44+
5. Write function message to "ResourceList.Results" with severity "Info", "Warning" or "Error"
4545
46-
KubeObject
46+
# KubeObject
4747
4848
The KubeObject is the basic unit to perform operations on KRM resources.
4949
5050
In the "AsMain", both "Items" and "FunctionConfig"
5151
are converted to the KubeObject(s).
5252
5353
If you are familiar with unstructured.Unstructured, using KubeObject is as simple as using unstructured.Unstructured.
54-
You can call function like `NestedStringOrDie` `SetNestedStringMap`, etc.
54+
You can call function like `NestedString` `SetNestedStringMap`, etc.
5555
5656
Except that KubeObject will not have pass-in interface arguments, nor will return an interface.
5757
Instead, you shall treat each KubeObject field (slice, or non-string map)as SubObject.
@@ -65,14 +65,13 @@ SubObject.NestedInt64("replicas")
6565
Besides unstructured style, another way to use KubeObject is to purely work on the KubeObject/SubObject by calling
6666
"GetMap", "GetSlice", "UpsertMap" which expects the return to be SubObject(s) pointer.
6767
68-
AsMain
68+
# AsMain
6969
7070
"AsMain" is the main entrypoint. In most cases, you only need to provide the mutator or validation logic and have AsMain
7171
handles the ResourceList parsing, KRM resource field type detection, read from STDIN and write to STDOUT.
7272
7373
"AsMain" accepts a struct that either implement the ResourceListProcessor interface or Runner interface.
7474
7575
See github.com/GoogleContainerTools/kpt-functions-sdk/go/fn/examples for detailed usage.
76-
7776
*/
7877
package fn

go/fn/errors.go

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,45 +19,15 @@ import (
1919
"strings"
2020
)
2121

22+
const pathDelimitor = "."
23+
2224
// ErrMissingFnConfig raises error if a required functionConfig is missing.
2325
type ErrMissingFnConfig struct{}
2426

2527
func (ErrMissingFnConfig) Error() string {
2628
return "unable to find the functionConfig in the resourceList"
2729
}
2830

29-
// errKubeObjectFields raises if the KubeObject operation panics.
30-
type errKubeObjectFields struct {
31-
obj *KubeObject
32-
fields []string
33-
}
34-
35-
func (e *errKubeObjectFields) Error() string {
36-
return fmt.Sprintf("Resource(apiVersion=%v, kind=%v, Name=%v) has unmatched field type: `%v",
37-
e.obj.GetAPIVersion(), e.obj.GetKind(), e.obj.GetName(), strings.Join(e.fields, "/"))
38-
}
39-
40-
// errSubObjectFields raises if the SubObject operation panics.
41-
type errSubObjectFields struct {
42-
fields []string
43-
}
44-
45-
func (e *errSubObjectFields) Error() string {
46-
return fmt.Sprintf("SubObject has unmatched field type: `%v", strings.Join(e.fields, "/"))
47-
}
48-
49-
type errResultEnd struct {
50-
obj *KubeObject
51-
message string
52-
}
53-
54-
func (e *errResultEnd) Error() string {
55-
if e.obj != nil {
56-
return fmt.Sprintf("function is terminated by %v: %v", e.obj.ShortString(), e.message)
57-
}
58-
return fmt.Sprintf("function is terminated: %v", e.message)
59-
}
60-
6131
type ErrAttemptToTouchUpstreamIdentifier struct{}
6232

6333
func (ErrAttemptToTouchUpstreamIdentifier) Error() string {
@@ -71,3 +41,20 @@ type ErrInternalAnnotation struct {
7141
func (e *ErrInternalAnnotation) Error() string {
7242
return e.Message
7343
}
44+
func NewErrUnmatchedField(obj SubObject, fields []string, dt any) *ErrUnmatchedField {
45+
relativefields := strings.Join(fields, pathDelimitor)
46+
obj.fieldpath += pathDelimitor + relativefields
47+
return &ErrUnmatchedField{
48+
SubObject: &obj, DataType: fmt.Sprintf("%T", dt),
49+
}
50+
}
51+
52+
type ErrUnmatchedField struct {
53+
SubObject *SubObject
54+
DataType string
55+
}
56+
57+
func (e *ErrUnmatchedField) Error() string {
58+
return fmt.Sprintf("Resource(apiVersion=%v, kind=%v) has unmatched field type %q in fieldpath %v",
59+
e.SubObject.parentGVK.GroupVersion(), e.SubObject.parentGVK.Kind, e.DataType, e.SubObject.fieldpath)
60+
}

go/fn/examples/example_filter_GVK_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ func updateReplicas(rl *fn.ResourceList) (bool, error) {
3434
return false, fn.ErrMissingFnConfig{}
3535
}
3636
var replicas int
37-
rl.FunctionConfig.GetOrDie(&replicas, "replicas")
37+
rl.FunctionConfig.NestedResource(&replicas, "replicas")
3838
for i := range rl.Items.Where(fn.IsGVK("apps", "v1", "Deployment")) {
39-
rl.Items[i].SetOrDie(replicas, "spec", "replicas")
39+
rl.Items[i].SetNestedField(replicas, "spec", "replicas")
4040
}
4141
return true, nil
4242
}

go/fn/examples/example_generator_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ func generate(rl *fn.ResourceList) (bool, error) {
4141
return false, fn.ErrMissingFnConfig{}
4242
}
4343

44-
revision := rl.FunctionConfig.NestedStringOrDie("data", "revision")
45-
id := rl.FunctionConfig.NestedStringOrDie("data", "id")
44+
revision, _, _ := rl.FunctionConfig.NestedString("data", "revision")
45+
id, _, _ := rl.FunctionConfig.NestedString("data", "id")
4646
js, err := fetchDashboard(revision, id)
4747
if err != nil {
4848
return false, fmt.Errorf("fetch dashboard: %v", err)

go/fn/examples/example_kubeobject_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,18 @@ func Example_kubeObjectMutatePrimitiveField() {
2929
replicas := spec.GetInt("replicas")
3030
// mutate the replicas variable
3131

32-
spec.SetNestedIntOrDie(int(replicas))
32+
spec.SetNestedInt(int(replicas))
3333
}
3434

3535
func Example_kubeObjectMutatePrimitiveSlice() {
36-
finalizers := deployment.NestedStringSliceOrDie("metadata", "finalizers")
36+
finalizers, _, _ := deployment.NestedStringSlice("metadata", "finalizers")
3737
// mutate the finalizers slice
3838

39-
deployment.SetNestedStringSliceOrDie(finalizers, "metadata", "finalizers")
39+
deployment.SetNestedStringSlice(finalizers, "metadata", "finalizers")
4040
}
4141

4242
func Example_kubeObjectMutatePrimitiveMap() {
43-
data := configMap.NestedStringMapOrDie("data")
43+
data, _, _ := configMap.NestedStringMap("data")
4444
// mutate the data map
4545

4646
err := deployment.SetNestedStringMap(data, "data")
@@ -52,8 +52,8 @@ func Example_kubeObjectMutateStrongTypedField() {
5252
var newPodTemplate corev1.PodTemplate
5353
curPodTemplate := configMap.GetMap("spec").GetMap("template")
5454
// Assign the current PodTemplate value to newPodTemplate
55-
// Use AsOrDie to AsMain handles the errors.
56-
curPodTemplate.AsOrDie(&newPodTemplate)
55+
// Use As to AsMain handles the errors.
56+
curPodTemplate.As(&newPodTemplate)
5757
// mutate the newPodTemplate object
5858
err := deployment.SetNestedField(newPodTemplate, "spec", "template")
5959
if err != nil { /* do something */
@@ -62,7 +62,7 @@ func Example_kubeObjectMutateStrongTypedField() {
6262

6363
func Example_kubeObjectMutateStrongTypedSlice() {
6464
var containers []corev1.Container
65-
found, err := deployment.Get(&containers, "spec", "template", "spec", "containers")
65+
found, err := deployment.NestedResource(&containers, "spec", "template", "spec", "containers")
6666
if err != nil { /* do something */
6767
}
6868
if !found { /* do something */

go/fn/examples/example_logger_injector_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func injectLogger(rl *fn.ResourceList) (bool, error) {
4141
}
4242
for i, obj := range rl.Items.Where(hasDesiredGVK) {
4343
var containers []corev1.Container
44-
obj.GetOrDie(&containers, "spec", "template", "spec", "containers")
44+
obj.NestedResource(&containers, "spec", "template", "spec", "containers")
4545
foundTargetContainer := false
4646
for j, container := range containers {
4747
if container.Name == li.ContainerName {
@@ -57,7 +57,7 @@ func injectLogger(rl *fn.ResourceList) (bool, error) {
5757
}
5858
containers = append(containers, c)
5959
}
60-
rl.Items[i].SetOrDie(containers, "spec", "template", "spec", "containers")
60+
rl.Items[i].SetNestedField(containers, "spec", "template", "spec", "containers")
6161
}
6262
return true, nil
6363
}

go/fn/examples/example_read_field_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ func Example_aReadField() {
3131
func readField(rl *fn.ResourceList) (bool, error) {
3232
for _, obj := range rl.Items.Where(fn.IsGVK("apps", "v1", "Deployment")) {
3333
// Style 1: like using unstrucuted.Unstructured, get/set the value from field paths*
34-
replicas := obj.NestedInt64OrDie("spec", "replicas")
34+
replicas, _, _ := obj.NestedInt64("spec", "replicas")
3535
fn.Logf("replicas is %v\n", replicas)
36-
paused := obj.NestedBoolOrDie("spec", "paused")
36+
paused, _, _ := obj.NestedBool("spec", "paused")
3737
fn.Logf("paused is %v\n", paused)
3838
// Update strategy from Recreate to RollingUpdate.
39-
if strategy := obj.NestedStringOrDie("spec", "strategy", "type"); strategy == "Recreate" {
40-
obj.SetNestedStringOrDie("RollingUpdate", "spec", "strategy", "type")
39+
if strategy, _, _ := obj.NestedString("spec", "strategy", "type"); strategy == "Recreate" {
40+
obj.SetNestedString("RollingUpdate", "spec", "strategy", "type")
4141
}
4242

4343
// Style 2: operate each resource layer via `GetMap`
@@ -46,7 +46,7 @@ func readField(rl *fn.ResourceList) (bool, error) {
4646
fn.Logf("replicas is %v\n", replicas)
4747
nodeSelector := spec.GetMap("template").GetMap("spec").GetMap("nodeSelector")
4848
if nodeSelector.GetString("disktype") != "ssd" {
49-
nodeSelector.SetNestedStringOrDie("ssd", "disktype")
49+
nodeSelector.SetNestedString("ssd", "disktype")
5050
}
5151
}
5252
return true, nil

go/fn/examples/example_read_functionConfig_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func Example_bReadFunctionConfig() {
3232

3333
func readFunctionConfig(rl *fn.ResourceList) (bool, error) {
3434
var sr SetReplicas
35-
rl.FunctionConfig.AsOrDie(&sr)
35+
rl.FunctionConfig.As(&sr)
3636
fn.Logf("desired replicas is %v\n", sr.DesiredReplicas)
3737
return true, nil
3838
}

go/fn/examples/example_set_field_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func setField(rl *fn.ResourceList) (bool, error) {
3232
for _, obj := range rl.Items {
3333
if obj.GetAPIVersion() == "apps/v1" && obj.GetKind() == "Deployment" {
3434
replicas := 10
35-
obj.SetOrDie(&replicas, "spec", "replicas")
35+
obj.SetNestedField(&replicas, "spec", "replicas")
3636
}
3737
}
3838
return true, nil

go/fn/examples/example_validator_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func validator(rl *fn.ResourceList) (bool, error) {
3232
var results fn.Results
3333
for _, obj := range rl.Items.Where(hasDesiredGVK) {
3434
var runAsNonRoot bool
35-
obj.GetOrDie(&runAsNonRoot, "spec", "template", "spec", "securityContext", "runAsNonRoot")
35+
obj.NestedResource(&runAsNonRoot, "spec", "template", "spec", "securityContext", "runAsNonRoot")
3636
if !runAsNonRoot {
3737
results = append(results, fn.ConfigObjectResult("`spec.template.spec.securityContext.runAsNonRoot` must be set to true", obj, fn.Error))
3838
}

go/fn/internal/maphelpers.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,20 @@ package internal
1616

1717
import (
1818
"fmt"
19+
20+
"sigs.k8s.io/kustomize/kyaml/yaml"
1921
)
2022

23+
func (o *MapVariant) GetRNode(fields ...string) (*yaml.RNode, bool, error) {
24+
rn := &yaml.RNode{}
25+
val, found, err := o.GetNestedValue(fields...)
26+
if err != nil || !found {
27+
return nil, found, err
28+
}
29+
rn.SetYNode(val.Node())
30+
return rn, found, err
31+
}
32+
2133
func (o *MapVariant) GetNestedValue(fields ...string) (variant, bool, error) {
2234
current := o
2335
n := len(fields)

0 commit comments

Comments
 (0)