diff --git a/pkg/api/v1alpha1/kclrun_types.go b/pkg/api/v1alpha1/kclrun_types.go index b453818..9374d06 100644 --- a/pkg/api/v1alpha1/kclrun_types.go +++ b/pkg/api/v1alpha1/kclrun_types.go @@ -18,4 +18,7 @@ const ( // ParamsKey is the key for the params field in the KCLRun resource. ParamsKey = "params" + + // MatchConstraintsKey is the key for the match constraints field in the KCLRun resource. + MatchConstraintsKey = "matchConstraints" ) diff --git a/pkg/config/config.go b/pkg/config/config.go index 212e347..04b8aae 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -3,6 +3,7 @@ package config import ( "fmt" "os" + "reflect" "strings" "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn" @@ -38,9 +39,23 @@ type KCLRun struct { Source string `json:"source" yaml:"source"` // Params are the parameters in key-value pairs format. Params map[string]interface{} `json:"params,omitempty" yaml:"params,omitempty"` + // MatchConstraints defines the resource matching rules. + MatchConstraints MatchConstraints `json:"matchConstraints,omitempty" yaml:"matchConstraints,omitempty"` } `json:"spec" yaml:"spec"` } +// MatchConstraints defines the resource matching rules. +type MatchConstraints struct { + ResourceRules []ResourceRule `json:"resourceRules,omitempty" yaml:"resourceRules,omitempty"` +} + +// ResourceRule defines a rule for matching resources. +type ResourceRule struct { + APIGroups []string `json:"apiGroups,omitempty" yaml:"apiGroups,omitempty"` + APIVersions []string `json:"apiVersions,omitempty" yaml:"apiVersions,omitempty"` + Resources []string `json:"resources,omitempty" yaml:"resources,omitempty"` +} + // Config is used to configure the KCLRun instance based on the given FunctionConfig. // It converts ConfigMap to KCLRun or assigns values directly from KCLRun. // If an error occurs during the configuration process, an error message will be returned. @@ -123,12 +138,36 @@ func (r *KCLRun) Transform(rl *fn.ResourceList) error { if err != nil { return err } - transformedObjects = append(transformedObjects, obj) + + // Check if the transformed object matches the resource rules + if r.MatchResourceRules(obj) { + transformedObjects = append(transformedObjects, obj) + } } rl.Items = transformedObjects return nil } +// MatchResourceRules checks if the given Kubernetes object matches the resource rules specified in KCLRun. +func (r *KCLRun) MatchResourceRules(obj *fn.KubeObject) bool { + + // if MatchConstraints.ResourceRules is not set (nil or empty), return true by default + if r == nil || isEmpty(r.Spec) || isEmpty(r.Spec.MatchConstraints) || isEmpty(r.Spec.MatchConstraints.ResourceRules) { + return true + } + // iterate through each resource rule + for _, rule := range r.Spec.MatchConstraints.ResourceRules { + if containsString(rule.APIGroups, obj.GroupKind().Group) && + containsString(rule.APIVersions, obj.GetAPIVersion()) && + containsString(rule.Resources, obj.GetKind()) { + return true + } + } + // if no match is found, return false + return false +} + +// DealAnnotations handles annotations, e.g., allow-insecure-source. func (r *KCLRun) DealAnnotations() { // Deal the allow-insecure-source annotation if v, ok := r.ObjectMeta.Annotations[AnnotationAllowInSecureSource]; ok && isOk(v) { @@ -136,6 +175,7 @@ func (r *KCLRun) DealAnnotations() { } } +// isOk checks if a given string is in the list of "OK" values. func isOk(value string) bool { okValues := []string{"ok", "yes", "true", "1", "on"} for _, v := range okValues { @@ -145,3 +185,25 @@ func isOk(value string) bool { } return false } + +// containsString checks if a slice contains a string. +func containsString(slice []string, str string) bool { + for _, s := range slice { + if s == str { + return true + } + } + return false +} + +// isEmpty checks if a struct is empty +func isEmpty(s interface{}) bool { + t := reflect.TypeOf(s) + + if t.Kind() != reflect.Struct { + panic("Input is not a struct") + } + zeroValue := reflect.New(t).Elem() + + return reflect.DeepEqual(s, zeroValue.Interface()) +} diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 4f2551a..19bf3e1 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -23,6 +23,8 @@ metadata: spec: source: | [item | {metadata.namespace = "baz"} for item in option("resource_list")] + matchConstraints: + resourceRules: `, }, { @@ -34,6 +36,19 @@ metadata: `, expectErrMsg: "`source` must not be empty", }, + { + name: "KCLRun missing matchConstraints", + config: `apiVersion: krm.kcl.dev/v1alpha1 +kind: KCLRun +metadata: + name: my-kcl-fn + namespace: foo +spec: + source: | + [item | {metadata.namespace = "baz"} for item in option("resource_list")] +`, + expectErrMsg: "", + }, { name: "valid ConfigMap", config: `apiVersion: v1 diff --git a/pkg/options/testdata/resource_list/kcl-run-code.yaml b/pkg/options/testdata/resource_list/kcl-run-code.yaml index 0a922cb..a0474a6 100644 --- a/pkg/options/testdata/resource_list/kcl-run-code.yaml +++ b/pkg/options/testdata/resource_list/kcl-run-code.yaml @@ -12,3 +12,8 @@ functionConfig: spec: source: | [resource | {if resource.kind == "Deployment": metadata.annotations: {"managed-by" = "krm-kcl"}} for resource in option("resource_list").items] + matchConstraints: + resourceRules: + - apiGroups: ["apps"] + apiVersions: ["v1"] + resources: ["deployments"] diff --git a/pkg/options/testdata/resource_list/kcl-run-git.yaml b/pkg/options/testdata/resource_list/kcl-run-git.yaml index 2fbe134..00b2f9e 100644 --- a/pkg/options/testdata/resource_list/kcl-run-git.yaml +++ b/pkg/options/testdata/resource_list/kcl-run-git.yaml @@ -14,3 +14,8 @@ functionConfig: annotations: config.kubernetes.io/local-config: "true" source: github.com/kcl-lang/krm-kcl/tests/mutation/set-annotations/ + matchConstraints: + resourceRules: + - apiGroups: ["apps"] + apiVersions: ["v1"] + resources: ["deployments"] diff --git a/pkg/options/testdata/resource_list/kcl-run-https.yaml b/pkg/options/testdata/resource_list/kcl-run-https.yaml index 5fce020..7ed3c94 100644 --- a/pkg/options/testdata/resource_list/kcl-run-https.yaml +++ b/pkg/options/testdata/resource_list/kcl-run-https.yaml @@ -14,3 +14,8 @@ functionConfig: annotations: config.kubernetes.io/local-config: "true" source: https://raw.githubusercontent.com/kcl-lang/krm-kcl/main/tests/mutation/set-annotations/main.k + matchConstraints: + resourceRules: + - apiGroups: ["apps"] + apiVersions: ["v1"] + resources: ["deployments"] diff --git a/pkg/options/testdata/resource_list/kcl-run-oci.yaml b/pkg/options/testdata/resource_list/kcl-run-oci.yaml index 7b45702..4ffa04e 100644 --- a/pkg/options/testdata/resource_list/kcl-run-oci.yaml +++ b/pkg/options/testdata/resource_list/kcl-run-oci.yaml @@ -14,3 +14,8 @@ functionConfig: annotations: config.kubernetes.io/local-config: "true" source: oci://ghcr.io/kcl-lang/set-annotation + matchConstraints: + resourceRules: + - apiGroups: ["apps"] + apiVersions: ["v1"] + resources: ["deployments"] diff --git a/pkg/options/testdata/yaml_stream/kcl-run-code.yaml b/pkg/options/testdata/yaml_stream/kcl-run-code.yaml index b97bd3a..9604f79 100644 --- a/pkg/options/testdata/yaml_stream/kcl-run-code.yaml +++ b/pkg/options/testdata/yaml_stream/kcl-run-code.yaml @@ -12,3 +12,8 @@ metadata: spec: source: | [resource | {if resource.kind == "Deployment": metadata.annotations: {"managed-by" = "krm-kcl"}} for resource in option("resource_list").items] + matchConstraints: + resourceRules: + - apiGroups: ["apps"] + apiVersions: ["v1"] + resources: ["deployments"] \ No newline at end of file diff --git a/pkg/options/testdata/yaml_stream/kcl-run-git.yaml b/pkg/options/testdata/yaml_stream/kcl-run-git.yaml index 8b282dc..46b32dc 100644 --- a/pkg/options/testdata/yaml_stream/kcl-run-git.yaml +++ b/pkg/options/testdata/yaml_stream/kcl-run-git.yaml @@ -12,3 +12,8 @@ spec: annotations: config.kubernetes.io/local-config: "true" source: github.com/kcl-lang/krm-kcl/tests/mutation/set-annotations/ + matchConstraints: + resourceRules: + - apiGroups: ["apps"] + apiVersions: ["v1"] + resources: ["deployments"] diff --git a/pkg/options/testdata/yaml_stream/kcl-run-https.yaml b/pkg/options/testdata/yaml_stream/kcl-run-https.yaml index 40abc41..892fb11 100644 --- a/pkg/options/testdata/yaml_stream/kcl-run-https.yaml +++ b/pkg/options/testdata/yaml_stream/kcl-run-https.yaml @@ -12,3 +12,8 @@ spec: annotations: config.kubernetes.io/local-config: "true" source: https://raw.githubusercontent.com/kcl-lang/krm-kcl/main/tests/mutation/set-annotations/main.k + matchConstraints: + resourceRules: + - apiGroups: ["apps"] + apiVersions: ["v1"] + resources: ["deployments"] \ No newline at end of file diff --git a/pkg/options/testdata/yaml_stream/kcl-run-oci.yaml b/pkg/options/testdata/yaml_stream/kcl-run-oci.yaml index 407aa45..39f99ce 100644 --- a/pkg/options/testdata/yaml_stream/kcl-run-oci.yaml +++ b/pkg/options/testdata/yaml_stream/kcl-run-oci.yaml @@ -12,3 +12,8 @@ spec: annotations: config.kubernetes.io/local-config: "true" source: oci://ghcr.io/kcl-lang/set-annotation + matchConstraints: + resourceRules: + - apiGroups: ["apps"] + apiVersions: ["v1"] + resources: ["deployments"]