From fe33ea7233923c5406d8a84527d3ce7b7850fd94 Mon Sep 17 00:00:00 2001 From: Andrew Anderson Date: Thu, 18 Apr 2024 13:07:35 -0400 Subject: [PATCH] run plugin just once, only add resources to label if kubectl apply/create/replace is run - might be other cases, fixed issue with objectcreation for ocm manifestwork and bindingpolicy --- pkg/common/common.go | 89 ++++++++++---------- pkg/helpers/labeler-helpers.go | 21 ++--- pkg/plugin-bp-creator/plugin-bp-creator.go | 10 ++- pkg/plugin-ocm-creator/plugin-ocm-creator.go | 37 +++++--- test/alias-test.sh | 41 ++++++++- 5 files changed, 123 insertions(+), 75 deletions(-) diff --git a/pkg/common/common.go b/pkg/common/common.go index e8b6863..7fcdd0e 100644 --- a/pkg/common/common.go +++ b/pkg/common/common.go @@ -16,7 +16,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "gopkg.in/yaml.v2" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" @@ -25,7 +24,7 @@ import ( "k8s.io/client-go/rest" ) -var Version = "0.18.1" +var Version = "0.18.2" // Plugin interface type Plugin interface { @@ -165,22 +164,7 @@ func expandTilde(args []string) []string { return args } -func (p ParamsStruct) CreateObjForPlugin(gvk schema.GroupVersionKind, yamlData []byte, objName, objResource, namespace string) { - // Unmarshal YAML data into a map - var objMap map[string]interface{} - err := yaml.Unmarshal([]byte(yamlData), &objMap) - if err != nil { - fmt.Println("Error unmarshaling YAML:", err) - return - } - - // Marshal the map into JSON - objectJSON, err := json.Marshal(objMap) - if err != nil { - fmt.Println("Error marshaling JSON:", err) - return - } - +func (p ParamsStruct) CreateObjForPlugin(gvk schema.GroupVersionKind, yamlData []byte, objName, objResource, namespace string, objectJSON []byte) { gvr := schema.GroupVersionResource{ Group: gvk.Group, Version: gvk.Version, @@ -193,44 +177,54 @@ func (p ParamsStruct) CreateObjForPlugin(gvk schema.GroupVersionKind, yamlData [ Resource: "namespaces", } - if p.Flags["debug"] { + if p.Flags["l-debug"] { log.Printf(" ℹī¸ object info %v/%v/%v %v\n", nsgvr.Group, nsgvr.Version, nsgvr.Resource, namespace) } - _, err = p.GetObject(p.DynamicClient, "", nsgvr, namespace) + + _, err := p.createObject(p.DynamicClient, namespace, gvr, objectJSON) if err != nil { - log.Printf(" 🔴 failed to create %v %q, namespace %q does not exist. Is KubeStellar installed?\n", objResource, objName, namespace) - } else { - _, err = p.createObject(p.DynamicClient, namespace, gvr, objectJSON) - if err != nil { - log.Printf(" 🔴 failed to create %v object %q in namespace %v. Is KubeStellar installed?\n", objResource, objName, namespace) - } + log.Printf(" 🔴 failed to create %v object %q in namespace %q: %v. Check if %q CRD is missing from cluster.\n", objResource, objName, namespace, err, objResource) } } func (p ParamsStruct) createObject(ocDynamicClientCoreOrWds dynamic.Interface, namespace string, gvr schema.GroupVersionResource, objectJSON []byte) (string, error) { - objToCreate := &unstructured.Unstructured{} + var objMap map[string]interface{} + err := json.Unmarshal(objectJSON, &objMap) + if err != nil { + fmt.Println("Error unmarshaling JSON:", err) + return namespace, err + } - // printUnstructured(objToCreate) + // Create an unstructured.Unstructured object from the map + objToCreate := &unstructured.Unstructured{Object: objMap} - // unmarshal the JSON data into the Unstructured object - err := objToCreate.UnmarshalJSON(objectJSON) - if err != nil { - log.Printf("%v\n", err) - return namespace, nil + // Now objToCreate is an unstructured.Unstructured object representing the JSON data + // log.Printf("objToCreate: %v\n", objToCreate) + metadata, ok, _ := unstructured.NestedMap(objToCreate.Object, "Metadata") + if !ok { + fmt.Println("Metadata section not found") + return namespace, err } + name, ok, _ := unstructured.NestedString(metadata, "Name") + if !ok { + fmt.Println("Name not found") + return namespace, err + } + + // log.Printf("name: %v\n", name) - _, err = p.GetObject(ocDynamicClientCoreOrWds, namespace, gvr, objToCreate.GetName()) + _, err = p.GetObject(ocDynamicClientCoreOrWds, namespace, gvr, name) if err == nil { // object still exists, can't create - if p.Flags["debug"] { - log.Printf(" ℹī¸ object exists %v/%v/%v %v\n", gvr.Group, gvr.Version, gvr.Resource, objToCreate.GetName()) + if p.Flags["l-debug"] { + log.Printf(" ℹī¸ object exists %v/%v/%v %v\n", gvr.Group, gvr.Version, gvr.Resource, name) } return namespace, err } // log.Printf(" ℹī¸ object info %v/%v/%v %v\n", gvr.Group, gvr.Version, gvr.Resource, objToCreate.GetName()) if errors.IsNotFound(err) { - retryCount := 10 + retryCount := 3 for attempt := 1; attempt <= retryCount; attempt++ { if namespace == "" { _, err = ocDynamicClientCoreOrWds.Resource(gvr).Create(context.TODO(), objToCreate, metav1.CreateOptions{}) @@ -241,14 +235,14 @@ func (p ParamsStruct) createObject(ocDynamicClientCoreOrWds dynamic.Interface, n if err == nil { break } - if p.Flags["debug"] { + if p.Flags["l-debug"] { log.Printf(" ℹī¸ object %s is being created (if error, namespace might be missing from resource definition). Retrying in 5 seconds: %v/%v/%v: %v\n", objToCreate.GetName(), gvr.Group, gvr.Version, gvr.Resource, err) } time.Sleep(5 * time.Second) continue } - if p.Flags["debug"] { + if p.Flags["l-debug"] { if err != nil { if namespace == "" { log.Printf(" 🟡 error creating object %v/%v/%v %v: %v\n", gvr.Group, gvr.Version, gvr.Resource, objToCreate.GetName(), err) @@ -304,16 +298,19 @@ func (p ParamsStruct) GetObject(ocDynamicClientCoreOrWds dynamic.Interface, name return nil, errMarshal } - if p.Flags["debug"] { - if err != nil { - // log.Printf(" > object not found %v/%v/%v %v in %q: %v\n", gvr.Group, gvr.Version, gvr.Resource, objectName, namespace, err) - return nil, err + if err != nil { + if p.Flags["l-debug"] { + log.Printf(" > object not found %v/%v/%v %q in %q: %v\n", gvr.Group, gvr.Version, gvr.Resource, objectName, namespace, err) + } + return nil, err - } else { - // log.Printf(" > found object %v/%v/%v %v in %q\n", gvr.Group, gvr.Version, gvr.Resource, objectName, namespace) - return objectJSON, nil + } else { + if p.Flags["l-debug"] { + log.Printf(" > found object %v/%v/%v %q in %q\n", gvr.Group, gvr.Version, gvr.Resource, objectName, namespace) } + return objectJSON, nil } + if err != nil { return nil, err } diff --git a/pkg/helpers/labeler-helpers.go b/pkg/helpers/labeler-helpers.go index 7fe1f1c..486d0d7 100644 --- a/pkg/helpers/labeler-helpers.go +++ b/pkg/helpers/labeler-helpers.go @@ -109,17 +109,12 @@ func AliasRun(args []string, p c.ParamsStruct) error { strings.HasPrefix(arg, "expose") || strings.HasPrefix(arg, "autoscale") || strings.HasPrefix(arg, "attach") || - strings.HasPrefix(arg, "exec") || strings.HasPrefix(arg, "wait") || strings.HasPrefix(arg, "cp") || strings.HasPrefix(arg, "run") || strings.HasPrefix(arg, "label") || strings.HasPrefix(arg, "annotate") || - strings.HasPrefix(arg, "patch") || - strings.HasPrefix(arg, "delete") || - strings.HasPrefix(arg, "create") || - strings.HasPrefix(arg, "replace") || - strings.HasPrefix(arg, "edit") { + strings.HasPrefix(arg, "patch") { p.Flags[arg] = true } } @@ -239,14 +234,18 @@ func AliasRun(args []string, p c.ParamsStruct) error { fnArgs := []reflect.Value{reflect.ValueOf(p), reflect.ValueOf(false)} + runOnce := make(map[string]bool) for key := range combinedFlagsAndParams { for pkey, value := range p.PluginArgs { for _, vCSV := range value { v := strings.Split(vCSV, ",") if key == v[0] { if p.PluginPtrs[pkey].IsValid() { - log.Printf("\nlabeler plugin: %q:\n\n", pkey) - p.PluginPtrs[pkey].Call(fnArgs) + if !runOnce[pkey] { + log.Printf("\nlabeler plugin: %q:\n\n", pkey) + p.PluginPtrs[pkey].Call(fnArgs) + runOnce[pkey] = true + } } } } @@ -254,7 +253,7 @@ func AliasRun(args []string, p c.ParamsStruct) error { } if p.Flags["l-debug"] { for key, value := range p.Resources { - fmt.Printf("labeler.go: [debug] resources: Key: %s, Value: %s\n", key, value) + fmt.Printf("labeler.go: [debug] resources: Key: %s, Value: \n%s\n", key, value) } } } @@ -337,7 +336,9 @@ func traverseKubectlOutput(input []string, p c.ParamsStruct) { Namespace: namespace, ObjectName: objectName, } - addObjectsToResourcesAfterKubectlApply(resource, p) + if p.Flags["apply"] || p.Flags["create"] || p.Flags["replace"] { + addObjectsToResourcesAfterKubectlApply(resource, p) + } } } diff --git a/pkg/plugin-bp-creator/plugin-bp-creator.go b/pkg/plugin-bp-creator/plugin-bp-creator.go index f006530..08df165 100644 --- a/pkg/plugin-bp-creator/plugin-bp-creator.go +++ b/pkg/plugin-bp-creator/plugin-bp-creator.go @@ -1,6 +1,7 @@ package pluginBPcreator import ( + "encoding/json" "fmt" "log" "strings" @@ -113,8 +114,13 @@ func PluginCreateBP(p c.ParamsStruct, reflect bool) []string { } if p.Params["l-bp-wds"] != "" { - log.Printf(" 🚀 Attempting to create %v object %q in WDS namespace %q", k, n, p.Params[nsArg]) - p.CreateObjForPlugin(gvk, yamlData, n, r, p.Params["namespaceArg"]) + log.Printf(" 🚀 Attempting to create %v object %q in WDS %q", k, n, p.Params["l-bp-wds"]) + objectJSON, err := json.Marshal(bindingPolicy) + if err != nil { + fmt.Println("Error marshaling JSON:", err) + return []string{} + } + p.CreateObjForPlugin(gvk, yamlData, n, r, p.Params["l-bp-wds"], objectJSON) } else { fmt.Printf("%v", string(yamlData)) } diff --git a/pkg/plugin-ocm-creator/plugin-ocm-creator.go b/pkg/plugin-ocm-creator/plugin-ocm-creator.go index 8c64fee..f669bde 100644 --- a/pkg/plugin-ocm-creator/plugin-ocm-creator.go +++ b/pkg/plugin-ocm-creator/plugin-ocm-creator.go @@ -1,6 +1,7 @@ package pluginOCMcreator import ( + "encoding/json" "fmt" "log" @@ -17,30 +18,31 @@ type ManifestWork struct { Spec mwSpec `yaml:"spec"` } -type mwSpec struct { - Workload Workload `yaml:"workload"` -} - type mwMetadata struct { Name string `yaml:"name"` } +type mwSpec struct { + Workload Workload `yaml:"workload"` +} + type Workload struct { Manifests []map[string]interface{} `yaml:"manifests"` } -type Manifest struct { - YAML string `yaml:"-"` -} +// Remove the unused type declaration +// type manifest struct { +// YAML string `yaml:"-"` +// } func PluginCreateMW(p c.ParamsStruct, reflect bool) []string { // function must be exportable (capitalize first letter of function name) to be discovered by labeler if reflect { return []string{"l-mw-name,string,name for the manifestwork object", "l-mw-create,flag,create/apply the manifestwork object"} } - type PluginFunction struct { - pluginCreateMW string `triggerKey:"l-mw"` - } + // type PluginFunction struct { + // pluginCreateMW string `triggerKey:"l-mw"` + // } n := "change-me" nArg := "l-mw-name" g := "work.open-cluster-management.io" @@ -70,11 +72,10 @@ func PluginCreateMW(p c.ParamsStruct, reflect bool) []string { }, }, } - // need a loop to fill in the manifests with the objects from debug run of kubectl or helm - for _, yamlData := range p.Resources { + for _, workloadYamlData := range p.Resources { var obj map[string]interface{} - err := yaml.Unmarshal(yamlData, &obj) + err := yaml.Unmarshal(workloadYamlData, &obj) if err != nil { log.Printf("Error unmarshaling YAML: %v", err) continue @@ -87,10 +88,18 @@ func PluginCreateMW(p c.ParamsStruct, reflect bool) []string { fmt.Println("Error marshaling YAML:", err) return []string{} } + // log.Printf("yamlData: \n%v", string(yamlData)) if p.Flags["l-mw-create"] { log.Printf(" 🚀 Attempting to create %v object %q in namespace %q", k, n, p.Params["namespaceArg"]) - p.CreateObjForPlugin(gvk, yamlData, n, r, p.Params["namespaceArg"]) + // log.Printf("%v %v %v %v %v %v", gvk.Group, gvk.Version, gvk.Kind, n, r, p.Params["namespaceArg"]) + objectJSON, err := json.Marshal(manifestWork) + if err != nil { + fmt.Println("Error marshaling JSON:", err) + return []string{} + } + // log.Printf("objectJSON: \n%v", string(objectJSON)) + p.CreateObjForPlugin(gvk, yamlData, n, r, p.Params["namespaceArg"], objectJSON) } else { fmt.Printf("%v", string(yamlData)) } diff --git a/test/alias-test.sh b/test/alias-test.sh index fc8f00d..a4e660e 100755 --- a/test/alias-test.sh +++ b/test/alias-test.sh @@ -164,6 +164,41 @@ else fi ((test_number++)) + +echo +echo "---------------------------------------------" +echo "--- kustomize with KubeStellar bindingpolicy creation (should fail unless you have WDS1 for KubeStellar on context cluster) ---" +echo "k apply -k ../examples/kustomize -l app.kubernetes.io/part-of=sample --context=kind-kind --namespace=default --overwrite --l-bp-name=newbp --l-bp-wds=wds1" +if ! k apply -k ../examples/kustomize -l app.kubernetes.io/part-of=sample --context=kind-kind --namespace=default --overwrite --l-bp-name=newbp --l-bp-wds=wds1;then + print_error "test $test_number: ERROR" +else + print_success "test $test_number: SUCCESS" +fi +((test_number++)) + +echo +echo "---------------------------------------------" +echo "--- kustomize with OCM manifestwork output ---" +echo "k apply -f examples/kubectl/pass --label=app.kubernetes.io/part-of=sample --context=kind-kind --namespace=default --overwrite --l-mw-name=new" +if ! k apply -f ../examples/kubectl/pass --label=app.kubernetes.io/part-of=sample --context=kind-kind --namespace=default --overwrite --l-mw-name=new;then + print_error "test $test_number: ERROR" +else + print_success "test $test_number: SUCCESS" +fi +((test_number++)) + +echo +echo "---------------------------------------------" +echo "--- kustomize with OCM manifestwork creation (should fail unless you have OCM installed on context cluster) ---" +echo "k apply -f examples/kubectl/pass --label=app.kubernetes.io/part-of=sample --context=kind-kind --namespace=default --overwrite --l-mw-name=new --l-mw-create" +if ! k apply -f ../examples/kubectl/pass --label=app.kubernetes.io/part-of=sample --context=kind-kind --namespace=default --overwrite --l-mw-name=new --l-mw-create;then + print_error "test $test_number: ERROR" +else + print_success "test $test_number: SUCCESS" +fi +((test_number++)) + + echo echo "---------------------------------------------" echo "--- kustomize with debug output ---" @@ -188,9 +223,9 @@ fi echo echo "---------------------------------------------" -echo "--- kubectl log - works! ---" -echo "k logs deployment.apps/coredns -n kube-system" -if ! k logs deployment.apps/coredns -n kube-system;then +echo "--- kubectl log ---" +echo "k logs deployment.apps/my-app-deployment -n default --context=kind-kind" +if ! k logs deployment.apps/my-app-deployment -n default --context=kind-kind;then print_error "test $test_number: ERROR" else print_success "test $test_number: SUCCESS"