From 05f431033c36c54e7c47f18a5a0a6315475a093f Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Wed, 1 Nov 2023 14:17:00 +0530 Subject: [PATCH 01/19] Intial Seed Code --- .gitignore | 3 + README.md | 54 ++++- common/README.md | 1 + common/helm_to_yaml.go | 45 ++++ common/json_to_string.go | 380 ++++++++++++++++++++++++++++++ common/runtime_to_json.go | 253 ++++++++++++++++++++ common/string_to_gofile.go | 285 ++++++++++++++++++++++ common/unstruct_to_string.go | 95 ++++++++ common/util.go | 58 +++++ config/README.md | 1 + config/enum_module_mapping.json | 81 +++++++ config/struct_module_mapping.json | 379 +++++++++++++++++++++++++++++ go.mod | 32 +++ go.sum | 101 ++++++++ inputs/README.md | 1 + main.go | 187 +++++++++++++++ outputs/README.md | 1 + to-do.txt | 18 ++ 18 files changed, 1973 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 common/README.md create mode 100644 common/helm_to_yaml.go create mode 100644 common/json_to_string.go create mode 100644 common/runtime_to_json.go create mode 100644 common/string_to_gofile.go create mode 100644 common/unstruct_to_string.go create mode 100644 common/util.go create mode 100644 config/README.md create mode 100644 config/enum_module_mapping.json create mode 100644 config/struct_module_mapping.json create mode 100644 go.mod create mode 100644 go.sum create mode 100644 inputs/README.md create mode 100644 main.go create mode 100644 outputs/README.md create mode 100644 to-do.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..492149d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +testing_helpers +outputs/generated_code.go +temp \ No newline at end of file diff --git a/README.md b/README.md index dbcfc7e..0790820 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,52 @@ -# nephio-sdk -Nephio SDK repo +# helm-to-operator-codegen-sdk +### Prerequisties: +1. GoLang Version: 1.19 +``` +wget -c https://golang.org/dl/go1.19.8.linux-amd64.tar.gz +sudo tar -C /usr/local -xvzf go1.19.8.linux-amd64.tar.gz +# Create Folder for Go Packages: +mkdir go +cd go +mkdir pkg bin src + +# In order to add Go to environment: Append the following to "/etc/profile" file +export PATH=$PATH:/usr/local/go/bin +export GOPATH="$HOME/go" +export GOBIN="$GOPATH/bin" + +# Then, Apply the changes: +source /etc/profile +# Check The Installation +go version +``` + + +2. Helm : +``` +wget https://get.helm.sh/helm-v3.9.3-linux-amd64.tar.gz +tar xvf helm-v3.9.3-linux-amd64.tar.gz +sudo mv linux-amd64/helm /usr/local/bin +``` + +3. Go Packages: +``` +git clone https://github.sec.samsung.net/s-jaisawal/helm-operatort-sdk.git +cd helm-operatort-sdk/ +go mod tidy +``` + +### Running the Script +``` +go run main.go +e.g. go run main.go /home/ubuntu/free5gccharts/towards5gs-helm/charts/free5gc/charts/free5gc-amf free5gcns +``` +Note: +1. If is not provided, then by default it would take the helm_charts present in Input-folder. + +The generated Go-Code would be written in the "outputs/generated_code.go" file + +The Generated Go-Code would contain the following plugable function: +1. Create_All(): It when called, create all the k8s resources(services, deployment) in the kubernetes cluster. +2. Get_Resources(): would return the list of a particular resource. + 1. Get_Service() would return the list of all Services-Objects. + 2. Get_Deployment() would return the list of all Deployment-Objects. & so on diff --git a/common/README.md b/common/README.md new file mode 100644 index 0000000..42f4615 --- /dev/null +++ b/common/README.md @@ -0,0 +1 @@ +# helm-operatort-sdk \ No newline at end of file diff --git a/common/helm_to_yaml.go b/common/helm_to_yaml.go new file mode 100644 index 0000000..2512dd1 --- /dev/null +++ b/common/helm_to_yaml.go @@ -0,0 +1,45 @@ +package common + +import ( + "bufio" + "fmt" + "os/exec" + + "github.com/sirupsen/logrus" +) + +type HelmYamlConvertor struct { + Namespace string + Chartpath string +} + +/* +Converts the Helm-Chart to Yaml Template in temp folder, +Runs the bash command "helm template --namespace --output-dir temp/templated/" +Todo: Increase the functionality to handle remote helm charts, and support for using different values.yaml & so on +*/ +func (obj *HelmYamlConvertor) ConvertHelmToYaml() error { + logrus.Info(obj.Namespace, obj.Chartpath) + logrus.Info(" ----------------- Converting Helm to Yaml --------------------------") + createDirIfDontExist("temp") + if obj.Namespace == "" { + obj.Namespace = "default" + } + cmdStruct := exec.Command("helm", "template", obj.Chartpath, "--namespace", obj.Namespace, "--output-dir", "temp/templated/") + stderr, _ := cmdStruct.StderrPipe() // Intialising a Pipe to read error stream + if err := cmdStruct.Start(); err != nil { + logrus.Error(err) + return err + } + + scanner := bufio.NewScanner(stderr) + helmCmdErr := "" + for scanner.Scan() { + helmCmdErr += scanner.Text() + } + if len(helmCmdErr) > 0 { + logrus.Error("Error while running the command| helm template " + obj.Chartpath + " --namespace " + obj.Namespace + " --output-dir temp/templated/ ") + return fmt.Errorf(helmCmdErr) + } + return nil +} diff --git a/common/json_to_string.go b/common/json_to_string.go new file mode 100644 index 0000000..e889bf8 --- /dev/null +++ b/common/json_to_string.go @@ -0,0 +1,380 @@ +package common + +import ( + "encoding/json" + "fmt" + "log" + "os" + "reflect" + "regexp" + "strings" + + "github.com/liyue201/gostl/ds/set" + "github.com/liyue201/gostl/ds/stack" + "github.com/liyue201/gostl/utils/comparator" + "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +type JsonStringConverter struct { + globalStructMapping map[string]string // To be set by Calling Intialise (setModStructMapping) + globalEnumsSet set.Set[string] // To be set by Calling Intialise (setEnums) + generatedGoCode string // To be set by jsonToGoCode Function +} + +func (obj *JsonStringConverter) checkOpeningAndClosingBraces() bool { + s := stack.New[int]() + lineCount := 0 + opCount, closeCount := 0, 0 + stringDetected := false + for i := 0; i < len(obj.generatedGoCode); i++ { + if obj.generatedGoCode[i] == '\n' { + lineCount++ + } + if obj.generatedGoCode[i] == '"' { + // If Enters in double quotes (string) Don't Check For opening/Closing Braces + stringDetected = !stringDetected + } + if !stringDetected { + if obj.generatedGoCode[i] == '{' { + s.Push('{') + opCount++ + } else if obj.generatedGoCode[i] == '}' { + closeCount++ + if s.Empty() { + logrus.Error("Closing Brace has no Opening Brace| Linenumber ", lineCount) + return false + } else { + s.Pop() + } + } + } + + } + + logrus.Debugf("Test Summary| Opening Brace Count %d | Closing Brace Count %d| \n", opCount, closeCount) + for !s.Empty() { + logrus.Error("Extra Opening Braces Found at the End ") + return false + } + + return true +} + +/* +Checks for Generated-Go-Code +Check 1: Opening and Closing Braces should be consistent (as you see in day to day like) +More Checks can also be added here +*/ +func (obj *JsonStringConverter) checkGoCode() { + obj.checkOpeningAndClosingBraces() +} + +/* +Reads the module-structure mapping.json and populate globalStructMapping map +globalStructMapping Says: Give me any data-type (v1.Deployment) and I will tell you which module it belong (appsv1) +This is required, because runtime-Object type if you reflect, will say the datatype as v1.Service or v1.DeploymentSpec, +whereas in code, we want corev1.Service or appsv1.DeploymentSpec +*/ +func (obj *JsonStringConverter) setModStructMapping(filepath string) { + + out := map[string]string{} + plan, _ := os.ReadFile(filepath) + var data map[string]interface{} + err := json.Unmarshal(plan, &data) + if err != nil { + logrus.Fatal("Cannot unmarshal the json For Struct Mapping | ", filepath, " Error | ", err) + } + + // Validating Struct Mapping, Every Mapping value should contain unique values: pending + var validateSet = set.New[string](comparator.StringComparator, set.WithGoroutineSafe()) + for _, structsList := range data { + for _, structName := range structsList.([]interface{}) { + // ToDo: What to do when duplication is found, currently we are only logging + if validateSet.Contains(structName.(string)) { + logrus.Warn("Duplication Detected in Struct Mapping | For ", structName.(string)) + } + validateSet.Insert(structName.(string)) + } + + } + + // Saving to Global Map, so that it could be used by "formatTypeVal" function + for module, structs := range data { + for _, structName := range structs.([]interface{}) { + structNameStr := structName.(string) + out[structNameStr] = module + } + } + obj.globalStructMapping = out +} + +/* + Enums are needed to handle differently than structs, therefore the below set tells which data-types are enum (non-composite), + So, that it could be handled differently (Used by "formatTypeVal" function) +*/ +func (obj *JsonStringConverter) setEnums(filepath string) { + fp, _ := os.ReadFile(filepath) + var data map[string]interface{} + err := json.Unmarshal(fp, &data) + if err != nil { + logrus.Fatal("Cannot unmarshal the json For Enum-Mapping | ", filepath, " Error | ", err) + } + var tempSet = set.New[string](comparator.StringComparator, set.WithGoroutineSafe()) + // Saving to Global Enum Set, so that it could be used by "formatTypeVal" function + for _, enums := range data { + for _, val := range enums.([]interface{}) { + enum := val.(string) + if tempSet.Contains(enum) { + // ToDo: What to do when duplication is found, currently we are only logging + logrus.Warn("Duplication Detected in Enum Mapping | For ", enum) + } + tempSet.Insert(enum) + } + } + obj.globalEnumsSet = *tempSet +} + +/* +Based on different data-type, values are formated differently +Example objType objVal Format_Val(Out) + + String 5 "5" + Int32 5 5 + *int32 5 int32Ptr(5) +*/ +func (obj *JsonStringConverter) formatTypeVal(objType string, objVal string, tabCount int) string { + // Special Data-Types are Handled Here + if objType == "intstr.Type" { + /*intstr.Type need to be handled explictly: intstr.Type is a int enum*/ + objVal = objVal[1 : len(objVal)-1] // Removing the double quotes from the objVal (because we need int) + return fmt.Sprintf("%s(%s)", objType, objVal) + } else if objType == "[]uint8" || objType == "[]byte" { + return fmt.Sprintf("%s(%s)", objType, objVal) + } + // Special Data-Types area Ends + + pointerType := false + if objType[0] == '*' { + pointerType = true + } else if objType[0] == '&' { + log.Fatal(fmt.Errorf("& Types are not supported yet")) + } + + if pointerType { + switch objType[1:] { + // You can find the defination of func boolPtr, int32Ptr, int64Ptr, intPtr, int16Ptr in the string_to_gocode.go + case "int", "int16", "int32", "int64": + return fmt.Sprintf("%sPtr(%s)", objType[1:], objVal[1:len(objVal)-1]) + case "bool": + return fmt.Sprintf("boolPtr(%s)", objVal) + case "string": + return fmt.Sprintf("stringPtr(%s)", objVal) + } + } + + switch objType { + case "int32", "int64", "int", "int16": + return objVal[1 : len(objVal)-1] // Remove the double quotes and return + case "bool": + return objVal + case "string": + return objVal + } + + // It will reach here If It is a Composite Literal i.e. Struct OR a Enum + // Step-1: If type contains v1 --> Needs to change with corresponding modules using the globalStructMapping + if pointerType { + objType = "&" + objType[1:] //Converting pointer to address + } + re := regexp.MustCompile(`v1.`) + index := re.FindStringIndex(objType) + + if index != nil { + // v1 is present in objType + startIndex := index[0] + endIndex := index[1] + curStruct := objType[endIndex:] // Converts &v1.DeploymentSpec --> DeploymentSpec and assigns to curStruct + module := obj.globalStructMapping[curStruct] + if module == "" { + logrus.Error("Current Structure-Module Mapping is NOT KNOWN| Kindly add it in module_struct_mapping.json | ", objType) + } + objTypeWithModule := objType[:startIndex] + module + "." + curStruct // Converts &v1.DeploymentSpec --> &appsv1.DeploymentSpec + /* + The only difference between Enum and Structs is that: + Enum are intialised using () where Structs are intialised using {} + Therefore, Need to Handle Separatly + */ + if obj.globalEnumsSet.Contains(curStruct) && objType[:2] != "[]" { // List of enums([]enumtype) are also Intailised as Structs using {} + return fmt.Sprintf("%s(%s)", objTypeWithModule, objVal) // Replacing {} with (), For Enums + } else { + return fmt.Sprintf("%s{\n%s\n%s}", objTypeWithModule, objVal, repeat("\t", tabCount)) + } + } + + return fmt.Sprintf("%s{\n%s\n%s}", objType, objVal, repeat("\t", tabCount)) +} + +/* +Recursive Function (DFS Algorithm) to traverse json and convert to gocode +The DFS Algorithm would traverse all the nodes(represented by v) writes its corressponding gocode +*/ +func (obj *JsonStringConverter) traverseJson(v reflect.Value, curObjType string, tabs int) string { + for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { + v = v.Elem() // Dereference the Pointer + } + switch v.Kind() { + case reflect.Array, reflect.Slice: + inter_str := "\n" + objType := curObjType[2:] // Removing the [] + for i := 0; i < v.Len(); i++ { + // Run DFS Over each iterations of slice and capture the backtrack-values + backtrackVal := obj.traverseJson(v.Index(i), objType, tabs+1) + inter_str += repeat("\t", tabs) + obj.formatTypeVal(objType, backtrackVal, tabs) + inter_str += ",\n" // Add a Comma plus a New Line after every value + } + /* + Inter_str would like Something Like this, + "backtrackVal1", + "backtrackVal2", + "backtrackVal3", + + */ + + return inter_str + case reflect.Map: + out := "" + for _, key := range v.MapKeys() { + // Here key represents the struct Attribute Name/ Field Name + objMap, _ := v.MapIndex(key).Interface().(map[string]interface{}) + logrus.Debug(repeat("\t", tabs), key) + objType := objMap["type"].(string) // objType represents the type of i'th attribute + logrus.Debug(repeat("\t", tabs) + objType) + objVal := objMap["val"] // objVal represents the value of i'th attribute + if len(objType) > 5 && (objType[1:4] == "map" || objType[0:3] == "map") { + // If objType/ Attribute type is Map. Then it is handled here + mapStartIndex := 0 + if objType[1:4] == "map" { + mapStartIndex = 1 + } + // Assuming the Key in Map is always string, map[string] --> 11 Characters + mapValuesType := objType[mapStartIndex+11:] + curMap := objVal.(map[string]interface{}) + + backTrackValues := "" + for curKey, curVal := range curMap { + logrus.Debug(repeat("\t", tabs), curKey) + // Run DFS over the Values of the map that is contained by i'th attribute as its value + backtrackVal := obj.traverseJson(reflect.ValueOf(curVal), objType, tabs+1) + backTrackValues += fmt.Sprintf("%s\"%s\" : %s,\n", repeat("\t", tabs+1), curKey, obj.formatTypeVal(mapValuesType, backtrackVal, tabs)) + } + backTrackValues = backTrackValues[:len(backTrackValues)-1] // Removing the Last Extra Comma + out = out + fmt.Sprintf("%s%s : %s,\n", repeat("\t", tabs), key, obj.formatTypeVal(objType, backTrackValues, tabs)) + /* + out would look like + Attribute-1: map[string]some_datatype{ + "key1": "backtrackVal1", + "key2": "backtrackVal2" + } + */ + } else { + // If objType/ Attribute type is Not Map, It could be String, Any other Struct, Int, Slice etc + // Run DFS over the objVal which is the value of i'th attribute + backtrackVal := obj.traverseJson(reflect.ValueOf(objVal), objType, tabs+1) + // Special Case: If type is resourceList + if curObjType == "v1.ResourceList" { + // Need Extra Double-Quotes At Key (cpu, ephemoral-storage) + out = out + fmt.Sprintf("%s\"%s\" : %s, \n", repeat("\t", tabs), key, obj.formatTypeVal(objType, backtrackVal, tabs)) + } else { + out = out + fmt.Sprintf("%s%s : %s, \n", repeat("\t", tabs), key, obj.formatTypeVal(objType, backtrackVal, tabs)) + } + /* + out would look something Like: + Replicas: 32, + Containers: []corev1.Container{ + corev1.Container{ + Image: "hello-world" // These Values are a Result of Backtracking + }, + + } + */ + } + } + out = out[:len(out)-1] // Removing the last new line + return out + + case reflect.String: + logrus.Debug(repeat("\t", tabs), v.String()) + data := v.String() + if strings.Contains(data, "\n") { + // New Lines Are now handled fmt.Sprint + // Since now The string goes under ``, therefore we don't need to replace /" with ", + // So, reverting the change that is currently being done in runtimetojson + data = strings.ReplaceAll(data, "\\\"", "\"") // Replacing String containing /" with " + data = fmt.Sprintf("fmt.Sprint(`%s`)", data) + return data + } + return "\"" + data + "\"" // Need to return the output with double quotes, " --> /" + + case reflect.Bool: + logrus.Debug(repeat("\t", tabs), v.Bool()) + return fmt.Sprint(v.Bool()) // Return string version of bool + + default: + logrus.Fatal("Unsupported Kind For Json-String DFS Traversal| ", v.Kind()) + } + return "\nOops, This should be Never Returned\n" +} + +/* +Reads the temp.json created by runtime_to_json.go and traverse json(DFS Runner) and generates the go-code requried +*/ +func (obj *JsonStringConverter) jsonToGoCode(filepath string) { + + plan, _ := os.ReadFile(filepath) + var data map[string]interface{} + err := json.Unmarshal(plan, &data) + if err != nil { + fmt.Println("Cannot unmarshal the json ", err) + } + logrus.Debug("Json Data", data) + obj.generatedGoCode = obj.traverseJson(reflect.ValueOf(data), "", 2) + + logrus.Debug(" --------------Check-Your Go Code --------------------------") + logrus.Debug(obj.generatedGoCode) + logrus.Debug("Running GO-Code Checks") + obj.checkGoCode() +} + +/* +Intialises the Module-Struct Mapping and Enum-Struct Mapping (Used in Format-Val Function) +*/ +func (obj *JsonStringConverter) Intialise() { + // To Make Sure Intialise is only called once + if len(obj.globalStructMapping) == 0 { + obj.setModStructMapping("config/struct_module_mapping.json") + obj.setEnums("config/enum_module_mapping.json") + } +} + +/* +Reads the temp.json created by runtime_to_json.go and Builds gocode string based on the contents of temps.json +*/ +func (obj *JsonStringConverter) Convert(gvk schema.GroupVersionKind) (string, error) { + if gvk.Version != "v1" { + logrus.Error("Currently Only Api-Version v1 is supported") + return "", fmt.Errorf("currently Only Api-Version v1 is supported") + } + + module := obj.globalStructMapping[gvk.Kind] + if module == "" { + logrus.Warn("FATAL ERROR| Kind " + gvk.Kind + " Currently Not Supported") + return "", fmt.Errorf("FATAL ERROR| Kind " + gvk.Kind + " Currently Not Supported") + } + + objType := "&" + module + "." + gvk.Kind + + obj.jsonToGoCode("temp/temp.json") + gocode := fmt.Sprintf("%s{\n%s\n\t}", objType, obj.generatedGoCode) + return gocode, nil +} diff --git a/common/runtime_to_json.go b/common/runtime_to_json.go new file mode 100644 index 0000000..0fad966 --- /dev/null +++ b/common/runtime_to_json.go @@ -0,0 +1,253 @@ +package common + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "os" + "reflect" + "strings" + "time" + + "github.com/sirupsen/logrus" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + schedulingv1 "k8s.io/api/scheduling/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +type RuntimeJsonConverter struct { +} + +/* +Recursive Function (DFS Algorithm) to traverse the object structure and identify data-types of various attributes +If you see the Runtime-Object as a Hierachial Structure (Tree), then you say curObj would be the node of the tree/graph you are currently at +The DFS Algorithm would traverse all the nodes, but will not return empty fields +*/ +func (obj *RuntimeJsonConverter) runDfsJsonOmitEmpty(curObj interface{}, tabs int) interface{} { + + // Handling Special Cases, When We can't move further because of Private Attributes + if fmt.Sprint(reflect.TypeOf(curObj)) == "resource.Quantity" { + res := curObj.(resource.Quantity) + resourceVal := res.String() + if resourceVal == "0" { + return nil + } + // Hack: type is changed to int, because we don't want the value in double quote when converting it to string + return map[string]string{"type": "int", "val": fmt.Sprintf("resource.MustParse(\"%s\")", resourceVal)} + } else if fmt.Sprint(reflect.TypeOf(curObj)) == "v1.Time" { + /* + Since the attributes of v1.Time struct are private, Therefore we need to send back the value using GoString() method + */ + timeInter := curObj.(metav1.Time) + timeVar := timeInter.Time + defaultVal := time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC) + if timeVar == defaultVal { //If The LHS is the Default Value, then omit it + return nil + } else { + var out = make(map[string]interface{}) + timeVal := timeVar.GoString() + out["Time"] = map[string]string{"type": "int", "val": timeVal} // Hack: type is changed to int, because we don't want the value in double quote + return out + } + } + // Private Attributes Special Cases Handling End + + objRef := reflect.ValueOf(curObj) + if objRef.Kind() == reflect.Ptr { + objRef = objRef.Elem() // Dereferencing the Pointer + } + + switch objRef.Kind() { + case reflect.Struct: + var out = make(map[string]interface{}) + for i := 0; i < objRef.NumField(); i++ { + var inter = make(map[string]interface{}) + if !objRef.Field(i).CanInterface() { + logrus.Warn("Private Attributes are not visible to me ! Support Missing For || ", objRef.Type().Field(i).Name, objRef.Type().Field(i).Type) + continue + } + // Run DFS over the attributes (Fields) of current Struct + backtrackVal := obj.runDfsJsonOmitEmpty(objRef.Field(i).Interface(), tabs+1) + if backtrackVal != nil { + inter["type"] = fmt.Sprint(objRef.Type().Field(i).Type) // Type of i'th Field + inter["val"] = backtrackVal // Backtracked/Actual Value of i'th Field + out[objRef.Type().Field(i).Name] = inter // Save the (Type and Value of i'th Field) with key as i'th Field Name + } + } + if len(out) == 0 { + return nil + } + return out + + case reflect.Float32, reflect.Float64, reflect.Int, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int64: + data := fmt.Sprint(objRef) // Converts the Obj Val to String Val + if data == "0" { // 0 is considered to be default value, Therefore, Omitting it + return nil + } + return data + + case reflect.Bool: + data := objRef.Bool() + return data + + case reflect.String: + data := objRef.String() + if data == "" { // "" is considered to be default value, Therefore, Omitting it + return nil + } + + data = strings.ReplaceAll(data, "\"", "\\\"") // Replacing String containing " with /" + return data + + case reflect.Slice: + var out []interface{} + if objRef.Len() == 0 { + return nil + } + sliceElementType := objRef.Index(0).Type() + // Special Cases: + // Case 1: If The Slice represents a Byte, then its Element data-type would be uint8 + if fmt.Sprint(sliceElementType) == "uint8" { + // Assuming that the byte has come from Kind: Secret, So, we need to encode the string to base64, before writing in code + // Thought: You never write the actual value of secret in yaml, but the encoded versions of it, The same is happening below + // Todo: Finding out where []byte is used other than Secret, and if it also represent encoded version or actual string + byteVal := objRef.Interface() + encodedByteVal := base64.StdEncoding.EncodeToString(byteVal.([]byte)) + return encodedByteVal + } + for i := 0; i < objRef.Len(); i++ { + // Run DFS over the all the iterations of current slice and capture the backtrack value + backtrackVal := obj.runDfsJsonOmitEmpty(objRef.Index(i).Interface(), tabs+1) + if backtrackVal != nil { + out = append(out, backtrackVal) + } else { + // Sometimes, "" in a slice, cannot be considered as nil value, For Examples for writing api-groups in Kind:Role, We write "" in a slice + // Argument: If a person has written "" in a slice, then It might be written for a reason + // Pending: Type Checking, Todo: if sliceElementType is int, then append 0, if bool, then append false, if string then, "" (Required Or Not) + out = append(out, "") + } + + } + if len(out) == 0 { + return nil + } + return out + + case reflect.Map: + // Assumption : Key Value is Always String + var out = make(map[string]interface{}) + switch objRef.Type().Key().Kind() { + case reflect.String: + for _, key := range objRef.MapKeys() { + // Run DFS over all the Values of current Map and Capture the Output + backtrackVal := obj.runDfsJsonOmitEmpty(objRef.MapIndex(key).Interface(), tabs+1) + // Argument: Why would someone add "" value of a key, if it is not useful i.e (key1 : ""), + // Pending: Type Checking, Todo: if val_type is int, then add 0, if bool, then add false, if string then, "" (Required Or Not) + if backtrackVal != nil { + out[key.String()] = backtrackVal + } + + } + default: + logrus.Warn("Currently Map-keys with the following Kind ", objRef.Type().Key().Kind(), " Are not Supported") + } + if len(out) == 0 { + return nil + } + return out + + case reflect.Invalid: + return nil + + default: + logrus.Fatal("Unsupported Type-Kind Found| Runtime-Json.Go| ", objRef.Kind()) + } + return "It shouldn't have reached here" +} + +/* +Input: Runtime-Obj, Group-Version-Kind +Output: Writes Temp.json which represents the structure(Heirarchy) and corresponding data-types & values of the Runtime-Object +Example: + + { + "ApiVersion": { + "type": "string" + "val" : "v1" + }, + "Spec": { + "type": "v1.DeploymentSpec" + "val": { + "Replicas": { + "type" : "&int32", + "val" : 5 + } + } + } + } & so on +*/ +func (obj *RuntimeJsonConverter) Convert(runtimeObj runtime.Object, gvk schema.GroupVersionKind) error { + if gvk.Version != "v1" { + logrus.Error("Currently Only Api-Version v1 is supported (Skipping)") + return fmt.Errorf("currently only Api-version v1 is supported") + } + logrus.Debug("----------------------------------Your Runtime Object--------------------\n", runtimeObj) + logrus.Debug("----------------------------------Your Runtime Object Ends--------------------\n") + var objMap interface{} + switch gvk.Kind { + case "Service": + curObj := runtimeObj.(*corev1.Service) + objMap = obj.runDfsJsonOmitEmpty(curObj, 0) + case "Deployment": + curObj := runtimeObj.(*appsv1.Deployment) + objMap = obj.runDfsJsonOmitEmpty(curObj, 0) + case "ConfigMap": + curObj := runtimeObj.(*corev1.ConfigMap) + objMap = obj.runDfsJsonOmitEmpty(curObj, 0) + case "ServiceAccount": + curObj := runtimeObj.(*corev1.ServiceAccount) + objMap = obj.runDfsJsonOmitEmpty(curObj, 0) + case "PersistentVolumeClaim": + curObj := runtimeObj.(*corev1.PersistentVolumeClaim) + objMap = obj.runDfsJsonOmitEmpty(curObj, 0) + case "StatefulSet": + curObj := runtimeObj.(*appsv1.StatefulSet) + objMap = obj.runDfsJsonOmitEmpty(curObj, 0) + case "Secret": + curObj := runtimeObj.(*corev1.Secret) + objMap = obj.runDfsJsonOmitEmpty(curObj, 0) + case "PriorityClass": + curObj := runtimeObj.(*schedulingv1.PriorityClass) + objMap = obj.runDfsJsonOmitEmpty(curObj, 0) + case "Role": + curObj := runtimeObj.(*rbacv1.Role) + objMap = obj.runDfsJsonOmitEmpty(curObj, 0) + case "RoleBinding": + curObj := runtimeObj.(*rbacv1.RoleBinding) + objMap = obj.runDfsJsonOmitEmpty(curObj, 0) + case "ClusterRole": + curObj := runtimeObj.(*rbacv1.ClusterRole) + objMap = obj.runDfsJsonOmitEmpty(curObj, 0) + case "ClusterRoleBinding": + curObj := runtimeObj.(*rbacv1.ClusterRoleBinding) + objMap = obj.runDfsJsonOmitEmpty(curObj, 0) + default: + logrus.Warn("Kind Currently Not Supported | ", gvk.Kind) + return fmt.Errorf("kind Currently Not Supported | %s", gvk.Kind) + } + + logrus.Debug("----------------------------------Your JSON Map--------------------\n", objMap) + logrus.Debug("----------------------------------Your JSON Map Ends--------------------\n") + jsonString, jsonErr := json.MarshalIndent(objMap, "", " ") + if jsonErr != nil { + return jsonErr + } + createDirIfDontExist("temp") + err := os.WriteFile("temp/temp.json", jsonString, 0777) + return err +} diff --git a/common/string_to_gofile.go b/common/string_to_gofile.go new file mode 100644 index 0000000..b60939b --- /dev/null +++ b/common/string_to_gofile.go @@ -0,0 +1,285 @@ +package common + +import ( + "fmt" + "os" + "strings" + + "github.com/liyue201/gostl/ds/set" + "github.com/liyue201/gostl/utils/comparator" + "github.com/sirupsen/logrus" +) + +type GoFile struct { + Namespace string + FileContent string + runtimeSupportKindSet set.Set[string] // To be Set By Intialise +} + +/* +Input: + + resourceType: The type of Resource: (Service, Deployment etc) + resourceList: list of gocode of all the resources of the specified resource type + +Output: Converts the input into a runnable gofunction +Example: resourceType = Service, resourceList = ["Svc-1-Code", "Svc-2-Code"] +Returns: + + func Get_Service() []*corev1.Service{ + service_1 := svc-1-Code + service_2 := svc-2-Code + + return []*corev1.Service{service_1, service_2,} + } +*/ +func (obj *GoFile) getRunnableFunction(resourceType string, resourceList []string) string { + if len(resourceList) == 0 { + return "" + } + fxnReturnType := "" + firstResource := resourceList[0] + for i := 0; i < len(firstResource); i++ { + if firstResource[i] == '{' { + break + } else if firstResource[i] != '\t' { + fxnReturnType += string(firstResource[i]) + } + } + if fxnReturnType[0] == '&' { + fxnReturnType = "*" + fxnReturnType[1:] + } + + varList := "" + varNamePrefix := fmt.Sprintf("%s%s", strings.ToLower(string(resourceType[0])), resourceType[1:]) // Service --> service + createdVars := "" + for i := 0; i < len(resourceList); i++ { + curVarName := varNamePrefix + fmt.Sprint(i+1) + varList += fmt.Sprintf(` + %s := %s + + `, curVarName, resourceList[i]) + createdVars += fmt.Sprintf("%s, ", curVarName) + } + + fxn := fmt.Sprintf(` +func Get%s() []%s{ + %s + return []%s{%s} +} + + `, resourceType, fxnReturnType, varList, fxnReturnType, createdVars) + + logrus.Debug(fxn) + return fxn +} + +/* +It adds the imports as well as the helper fxns like int_ptr, string_ptr +Input: + + allFxn: Go-code for all the fxns (Get_Service(), Get_Deployment()) concatenated in a single string + fxnCreated: List of all the fxnNames that allFxn contains (Used in getMasterFxn) + debugging: For Testing (to be removed) + +Output: + + A Go Package, containing all the functions, helper functions, required imports, The output of this function is what you see in the generated_code.go +*/ +func (obj *GoFile) addFunctionsToGofile(allFxn string, fxnCreated []string, debugging bool) string { + packageName := "main" + if !debugging { + packageName = "controller" + } + fileText := fmt.Sprintf(` +package %s + +import ( + "context" + "fmt" + "time" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + rbacv1 "k8s.io/api/rbac/v1" + schedulingv1 "k8s.io/api/scheduling/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func deleteMeAfterDeletingUnusedImportedModules() { + /* + It is written to handle the error "Module Imported but not used", + The user can delete the non-required modules from import and then delete this function also + */ + _ = time.Now() + _ = &unstructured.Unstructured{} + _ = corev1.Service{} + _ = metav1.ObjectMeta{} + _ = appsv1.Deployment{} + _ = rbacv1.Role{} + _ = schedulingv1.PriorityClass{} + _ = intstr.FromInt(4) + _, _ = resource.ParseQuantity("") + _ = context.TODO() + _ = fmt.Sprintf("") +} + +func int32Ptr(val int) *int32 { + var a int32 + a = int32(val) + return &a +} + +func int64Ptr(val int) *int64 { + var a int64 + a = int64(val) + return &a +} + +func intPtr(val int) *int { + a := val + return &a +} + +func int16Ptr(val int) *int16 { + var a int16 + a = int16(val) + return &a +} + +func boolPtr(val bool) *bool { + a := val + return &a +} + +func stringPtr(val string) *string { + a := val + return &a +} +`, packageName) + obj.getMasterFxn(fxnCreated, true) + obj.getMasterFxn(fxnCreated, false) + allFxn + mainfxn := ` + func main(){ + fmt.Println("Only for Debbugging purpose") + fmt.Println(GetService()) + fmt.Println(GetDeployment()) + } + ` + if debugging { + return fileText + mainfxn + } else { + return fileText + } + +} + +/* +Input: + + fxnCreated: List all functions that has been created so far, Example: [Get_Service(), Get_Deployment()] + inCreatedState: Bool: true for Create_All, and false for Delete_All + +Output: + + Output the Go-Code of the function that can either create or delete all the resources +*/ +func (obj *GoFile) getMasterFxn(fxnCreated []string, inCreatedState bool) string { + usage := "Delete" + if inCreatedState { + usage = "Create" + } + fxnStatement := "" + if obj.Namespace == "" { + for _, fxnName := range fxnCreated { + fxnStatement += fmt.Sprintf(` + for _, resource := range %s{ + err = r.%s(context.TODO(), resource) + if err != nil { + fmt.Println("Erorr During %sing resource of %s| Error --> |", err) + } + } + `, fxnName, usage, usage[:len(usage)-1], fxnName) + } + } else { + for _, fxnName := range fxnCreated { + fxnResourceType := fxnName[3 : len(fxnName)-2] // ResourceType from GetService(), GetDeployment() + ifblock := + `if resource.ObjectMeta.Namespace == ""{ + resource.ObjectMeta.Namespace = namespaceProvided + }` + if !obj.runtimeSupportKindSet.Contains(fxnResourceType) { + // If the resource type is unstructured.Unstructured + ifblock = + `if resource.GetNamespace() == ""{ + resource.SetNamespace(namespaceProvided) + }` + } + + fxnStatement += fmt.Sprintf(` + for _, resource := range %s{ + %s + err = r.%s(context.TODO(), resource) + if err != nil { + fmt.Println("Erorr During %sing resource of %s| Error --> |", err) + } + } + `, fxnName, ifblock, usage, usage[:len(usage)-1], fxnName) + } + } + + outFxn := fmt.Sprintf(` +/* +// Before Uncommenting the following function, Make sure the data-type of r is same as of your Reconciler, +// Replace "YourKindReconciler" with the type of your Reconciler +func (r *YourKindReconciler)%sAll(){ + var err error + namespaceProvided := "%s" + %s +} +*/ + + `, usage, obj.Namespace, fxnStatement) + return outFxn +} + +func (obj *GoFile) Intialise(runtimeSupportKinds []string) { + var tempSet = set.New[string](comparator.StringComparator, set.WithGoroutineSafe()) + for _, val := range runtimeSupportKinds { + tempSet.Insert(val) + } + obj.runtimeSupportKindSet = *tempSet +} + +/* +Input: Map of Resource-Type as Key and the Value represents Go-Codes corresponding to the resource-type in slice +Example: "Service": ["GO-Code for Service-1", "GO-Code for Service-2"] + + "Deployment": ["GO-Code for Deployment-1", "GO-Code for Deployment-2", "GO-Code for Deployment-3"] + +Output: + + Generates the Go-file String Content containing all the functions and libray imports, so the gocode can be deployed/ pluged in +*/ +func (obj *GoFile) Generate(gocodes map[string][]string) { + allFxn := "" + functionsCreated := []string{} + for resourceType, resourceList := range gocodes { + allFxn += obj.getRunnableFunction(resourceType, resourceList) + functionsCreated = append(functionsCreated, fmt.Sprintf("Get%s()", resourceType)) + } + fileText := obj.addFunctionsToGofile(allFxn, functionsCreated, false) + obj.FileContent = fileText +} + +/* +Writes the "output_gocode/generated_code.go" file +*/ +func (obj *GoFile) WriteToFile() { + createDirIfDontExist("outputs") + err := os.WriteFile("outputs/generated_code.go", []byte(obj.FileContent), 0777) + if err != nil { + logrus.Fatal("Writing gocode to outputs/generated_code.go FAILED| Error --> | ", err) + } +} diff --git a/common/unstruct_to_string.go b/common/unstruct_to_string.go new file mode 100644 index 0000000..2de1011 --- /dev/null +++ b/common/unstruct_to_string.go @@ -0,0 +1,95 @@ +package common + +import ( + "fmt" + "log" + "reflect" + "strings" + + "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +type UnstructStringConverter struct { +} + +/* +Runs Dfs Algorithm over the Unstructured Object Tree Like Structure +Input: + + v: Current Node of Tree you are at + tabs: Depth of Current Node + +Output: + + Go-Code String Representing the Unstructured Object +*/ +func (obj *UnstructStringConverter) runDfsUnstruct(v reflect.Value, tabs int) string { + for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { + log.Fatal("Assumption: Pointer Can Never come in Unstructed |Feels Wrong") + } + + switch v.Kind() { + case reflect.Array, reflect.Slice: + curSlice := v.Interface().([]interface{}) + var outStr = "" + for _, sliceItem := range curSlice { + // Run DFS over all the iterations of Slice, and capture the backtrack value + backtackValues := obj.runDfsUnstruct(reflect.ValueOf(sliceItem), tabs+1) + outStr += repeat("\t", tabs) + backtackValues + ",\n" + logrus.Debugf("%s%s,\n", repeat("\t", tabs), backtackValues) + } + if len(outStr) > 1 { + outStr = outStr[:len(outStr)-1] //Removing the Last \n + } + return fmt.Sprintf("[]interface{}{\n%s\n%s}", outStr, repeat("\t", tabs)) + + case reflect.Map: + out := "" + curMap := v.Interface().(map[string]interface{}) + for key, val := range curMap { + // Run DFS over all the Values of Map, and capture the backtrack value + backtackValues := obj.runDfsUnstruct(reflect.ValueOf(val), tabs+1) + + logrus.Debugf("%s\"%s\": %s", repeat("\t", tabs), key, backtackValues) + out += fmt.Sprintf("%s\"%s\": %s,\n", repeat("\t", tabs), key, backtackValues) + } + if len(out) > 1 { + out = out[:len(out)-1] //Removing the Last \n + } + return fmt.Sprintf("map[string]interface{}{\n%s\n%s}", out, repeat("\t", tabs)) + case reflect.String: + data := v.String() + + if strings.Contains(data, "\n") { + // New Lines Are now handled fmt.Sprint + data = fmt.Sprintf("fmt.Sprint(`%s`)", data) + return data + } + data = strings.ReplaceAll(data, "\"", "\\\"") // Need to preserve double quotes, therefore preserving by adding a backslash (" --> /") + return "\"" + data + "\"" // Sending with double quotes + + case reflect.Bool: + return fmt.Sprint(v.Bool()) // Return the Bool value as String + case reflect.Float32, reflect.Float64, reflect.Int, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int64: + return fmt.Sprint(v) // Return the Int, Float value as String + default: + logrus.Error("Current Type is Not Supported in Unstruct-To-String| ", v.Kind()) + + } + return "" +} + +/* +Converts the Unstructured Object to a gocode (string) that can create the same Unstructured Object +*/ +func (obj *UnstructStringConverter) Convert(unstructObj unstructured.Unstructured) string { + outStr := obj.runDfsUnstruct(reflect.ValueOf(unstructObj.Object), 2) + gocodeStr := fmt.Sprintf( + `&unstructured.Unstructured{ + Object: %s, + }`, outStr) + logrus.Debug("\n ---------Your Generated Code -----------------\n") + logrus.Debug(gocodeStr) + return gocodeStr +} diff --git a/common/util.go b/common/util.go new file mode 100644 index 0000000..0f08386 --- /dev/null +++ b/common/util.go @@ -0,0 +1,58 @@ +package common + +import ( + "errors" + "log" + "os" +) + +/* +Python Equivalent of pattern*times: repeat("a", 3) --> "aaa" +*/ +func repeat(pattern string, times int) string { + + out := "" + for z := 0; z < times; z++ { + out += pattern + } + return out +} + +/* +Creates the directory if it doesn't exist intially +*/ +func createDirIfDontExist(path string) error { + if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { + err := os.Mkdir(path, 0777) + if err != nil { + log.Println(err) + return err + } + } + return nil +} + +/* +It outputs the list of filepaths of all the yaml-files present in a directory, recursively +*/ +func RecursiveListYamls(curFolder string) (yamlfiles []string) { + folderContent, _ := os.ReadDir(curFolder) + for _, files := range folderContent { + if files.Type().IsDir() { + if files.Name() == "tests" { // Don't list the test yamls (if any) + continue + } + returnedYamlFiles := RecursiveListYamls(curFolder + "/" + files.Name()) + yamlfiles = append(yamlfiles, returnedYamlFiles...) + } else { + fileName := files.Name() + if len(fileName) > 5 { + if fileName[len(fileName)-5:] == ".yaml" { + yamlfiles = append(yamlfiles, curFolder+"/"+fileName) + } + } + + } + } + return +} diff --git a/config/README.md b/config/README.md new file mode 100644 index 0000000..42f4615 --- /dev/null +++ b/config/README.md @@ -0,0 +1 @@ +# helm-operatort-sdk \ No newline at end of file diff --git a/config/enum_module_mapping.json b/config/enum_module_mapping.json new file mode 100644 index 0000000..c98dd4c --- /dev/null +++ b/config/enum_module_mapping.json @@ -0,0 +1,81 @@ +{ + "appsv1": [ + "PodManagementPolicyType", + "StatefulSetUpdateStrategyType", + "PersistentVolumeClaimRetentionPolicyType", + "StatefulSetConditionType", + "DeploymentStrategyType", + "DeploymentConditionType", + "DaemonSetUpdateStrategyType", + "DaemonSetConditionType", + "ReplicaSetConditionType" + ], + "corev1": [ + "PersistentVolumeReclaimPolicy", + "PersistentVolumeMode", + "PersistentVolumeClaimConditionType", + "PersistentVolumeClaimResizeStatus", + "PersistentVolumeAccessMode", + "PersistentVolumePhase", + "PersistentVolumeClaimPhase", + "HostPathType", + "StorageMedium", + "Protocol", + "AzureDataDiskCachingMode", + "AzureDataDiskKind", + "MountPropagationMode", + "URIScheme", + "PullPolicy", + "PreemptionPolicy", + "TerminationMessagePolicy", + "Capability", + "ConditionStatus", + "PodPhase", + "PodConditionType", + "RestartPolicy", + "DNSPolicy", + "NodeSelectorOperator", + "TaintEffect", + "TolerationOperator", + "OSName", + "UnsatisfiableConstraintAction", + "NodeInclusionPolicy", + "PodFSGroupChangePolicy", + "SeccompProfileType", + "PodQOSClass", + "ReplicationControllerConditionType", + "ServiceAffinity", + "ServiceType", + "ServiceInternalTrafficPolicyType", + "ServiceExternalTrafficPolicyType", + "IPFamily", + "IPFamilyPolicy", + "UniqueVolumeName", + "NodePhase", + "NodeConditionType", + "NodeAddressType", + "ResourceName", + "FinalizerName", + "NamespacePhase", + "NamespaceConditionType", + "LimitType", + "ResourceQuotaScope", + "ScopeSelectorOperator", + "SecretType", + "ComponentConditionType", + "ProcMountType" + ], + "metav1": [ + "ResourceVersionMatch", + "DeletionPropagation", + "StatusReason", + "CauseType", + "LabelSelectorOperator", + "ManagedFieldsOperationType", + "RowConditionType", + "ConditionStatus", + "IncludeObjectPolicy" + ], + "rbacv1": [], + "schedulingv1": [] +} \ No newline at end of file diff --git a/config/struct_module_mapping.json b/config/struct_module_mapping.json new file mode 100644 index 0000000..b7404f2 --- /dev/null +++ b/config/struct_module_mapping.json @@ -0,0 +1,379 @@ +{ + "appsv1": [ + "StatefulSet", + "StatefulSetUpdateStrategy", + "RollingUpdateStatefulSetStrategy", + "StatefulSetPersistentVolumeClaimRetentionPolicy", + "StatefulSetOrdinals", + "StatefulSetSpec", + "StatefulSetStatus", + "StatefulSetCondition", + "StatefulSetList", + "Deployment", + "DeploymentSpec", + "DeploymentStrategy", + "RollingUpdateDeployment", + "DeploymentStatus", + "DeploymentCondition", + "DeploymentList", + "DaemonSetUpdateStrategy", + "RollingUpdateDaemonSet", + "DaemonSetSpec", + "DaemonSetStatus", + "DaemonSetCondition", + "DaemonSet", + "DaemonSetList", + "ReplicaSet", + "ReplicaSetList", + "ReplicaSetSpec", + "ReplicaSetStatus", + "ReplicaSetCondition", + "ControllerRevision", + "ControllerRevisionList", + "PodManagementPolicyType", + "StatefulSetUpdateStrategyType", + "PersistentVolumeClaimRetentionPolicyType", + "StatefulSetConditionType", + "DeploymentStrategyType", + "DeploymentConditionType", + "DaemonSetUpdateStrategyType", + "DaemonSetConditionType", + "ReplicaSetConditionType" + ], + "corev1": [ + "Volume", + "VolumeSource", + "PersistentVolumeClaimVolumeSource", + "PersistentVolumeSource", + "PersistentVolume", + "PersistentVolumeSpec", + "VolumeNodeAffinity", + "PersistentVolumeStatus", + "PersistentVolumeList", + "PersistentVolumeClaim", + "PersistentVolumeClaimList", + "PersistentVolumeClaimSpec", + "TypedObjectReference", + "PersistentVolumeClaimCondition", + "PersistentVolumeClaimStatus", + "HostPathVolumeSource", + "EmptyDirVolumeSource", + "GlusterfsVolumeSource", + "GlusterfsPersistentVolumeSource", + "RBDVolumeSource", + "RBDPersistentVolumeSource", + "CinderVolumeSource", + "CinderPersistentVolumeSource", + "CephFSVolumeSource", + "SecretReference", + "CephFSPersistentVolumeSource", + "FlockerVolumeSource", + "GCEPersistentDiskVolumeSource", + "QuobyteVolumeSource", + "FlexPersistentVolumeSource", + "FlexVolumeSource", + "AWSElasticBlockStoreVolumeSource", + "GitRepoVolumeSource", + "SecretVolumeSource", + "SecretProjection", + "NFSVolumeSource", + "ISCSIVolumeSource", + "ISCSIPersistentVolumeSource", + "FCVolumeSource", + "AzureFileVolumeSource", + "AzureFilePersistentVolumeSource", + "VsphereVirtualDiskVolumeSource", + "PhotonPersistentDiskVolumeSource", + "AzureDiskVolumeSource", + "PortworxVolumeSource", + "ScaleIOVolumeSource", + "ScaleIOPersistentVolumeSource", + "StorageOSVolumeSource", + "StorageOSPersistentVolumeSource", + "ConfigMapVolumeSource", + "ConfigMapProjection", + "ServiceAccountTokenProjection", + "ProjectedVolumeSource", + "VolumeProjection", + "KeyToPath", + "LocalVolumeSource", + "CSIPersistentVolumeSource", + "CSIVolumeSource", + "EphemeralVolumeSource", + "PersistentVolumeClaimTemplate", + "ContainerPort", + "VolumeMount", + "VolumeDevice", + "EnvVar", + "EnvVarSource", + "ObjectFieldSelector", + "ResourceFieldSelector", + "ConfigMapKeySelector", + "SecretKeySelector", + "EnvFromSource", + "ConfigMapEnvSource", + "SecretEnvSource", + "HTTPHeader", + "HTTPGetAction", + "TCPSocketAction", + "GRPCAction", + "ExecAction", + "Probe", + "Capabilities", + "ResourceRequirements", + "ResourceClaim", + "Container", + "ProbeHandler", + "LifecycleHandler", + "Lifecycle", + "ContainerStateWaiting", + "ContainerStateRunning", + "ContainerStateTerminated", + "ContainerState", + "ContainerStatus", + "PodCondition", + "NodeSelector", + "NodeSelectorTerm", + "NodeSelectorRequirement", + "TopologySelectorTerm", + "TopologySelectorLabelRequirement", + "Affinity", + "PodAffinity", + "PodAntiAffinity", + "WeightedPodAffinityTerm", + "PodAffinityTerm", + "NodeAffinity", + "PreferredSchedulingTerm", + "Taint", + "Toleration", + "PodReadinessGate", + "PodSpec", + "PodResourceClaim", + "ClaimSource", + "PodOS", + "PodSchedulingGate", + "TopologySpreadConstraint", + "HostAlias", + "PodSecurityContext", + "SeccompProfile", + "PodDNSConfig", + "PodDNSConfigOption", + "PodIP", + "EphemeralContainerCommon", + "EphemeralContainer", + "PodStatus", + "PodStatusResult", + "Pod", + "PodList", + "PodTemplateSpec", + "PodTemplate", + "PodTemplateList", + "ReplicationControllerSpec", + "ReplicationControllerStatus", + "ReplicationControllerCondition", + "ReplicationController", + "ReplicationControllerList", + "SessionAffinityConfig", + "ClientIPConfig", + "ServiceStatus", + "LoadBalancerStatus", + "LoadBalancerIngress", + "ServiceSpec", + "ServicePort", + "Service", + "ServiceList", + "ServiceAccount", + "ServiceAccountList", + "Endpoints", + "EndpointSubset", + "EndpointAddress", + "EndpointPort", + "EndpointsList", + "NodeSpec", + "NodeConfigSource", + "ConfigMapNodeConfigSource", + "DaemonEndpoint", + "NodeDaemonEndpoints", + "NodeSystemInfo", + "NodeConfigStatus", + "NodeStatus", + "AttachedVolume", + "AvoidPods", + "PreferAvoidPodsEntry", + "PodSignature", + "ContainerImage", + "NodeCondition", + "NodeAddress", + "Node", + "NodeList", + "NamespaceSpec", + "NamespaceStatus", + "NamespaceCondition", + "Namespace", + "NamespaceList", + "Binding", + "Preconditions", + "PodLogOptions", + "PodAttachOptions", + "PodExecOptions", + "PodPortForwardOptions", + "PodProxyOptions", + "NodeProxyOptions", + "ServiceProxyOptions", + "ObjectReference", + "LocalObjectReference", + "TypedLocalObjectReference", + "SerializedReference", + "EventSource", + "Event", + "EventSeries", + "EventList", + "LimitRangeItem", + "LimitRangeSpec", + "LimitRange", + "LimitRangeList", + "ResourceQuotaSpec", + "ScopeSelector", + "ScopedResourceSelectorRequirement", + "ResourceQuotaStatus", + "ResourceQuota", + "ResourceQuotaList", + "Secret", + "SecretList", + "ConfigMap", + "ConfigMapList", + "ComponentCondition", + "ComponentStatus", + "ComponentStatusList", + "DownwardAPIVolumeSource", + "DownwardAPIVolumeFile", + "DownwardAPIProjection", + "SecurityContext", + "SELinuxOptions", + "WindowsSecurityContextOptions", + "RangeAllocation", + "Sysctl", + "NodeResources", + "PortStatus", + "PersistentVolumeReclaimPolicy", + "PersistentVolumeMode", + "PersistentVolumeClaimConditionType", + "PersistentVolumeClaimResizeStatus", + "PersistentVolumeAccessMode", + "PersistentVolumePhase", + "PersistentVolumeClaimPhase", + "HostPathType", + "StorageMedium", + "Protocol", + "AzureDataDiskCachingMode", + "AzureDataDiskKind", + "MountPropagationMode", + "URIScheme", + "PullPolicy", + "PreemptionPolicy", + "TerminationMessagePolicy", + "Capability", + "ConditionStatus", + "PodPhase", + "PodConditionType", + "RestartPolicy", + "DNSPolicy", + "NodeSelectorOperator", + "TaintEffect", + "TolerationOperator", + "OSName", + "UnsatisfiableConstraintAction", + "NodeInclusionPolicy", + "PodFSGroupChangePolicy", + "SeccompProfileType", + "PodQOSClass", + "ReplicationControllerConditionType", + "ServiceAffinity", + "ServiceType", + "ServiceInternalTrafficPolicyType", + "ServiceExternalTrafficPolicyType", + "IPFamily", + "IPFamilyPolicy", + "UniqueVolumeName", + "NodePhase", + "NodeConditionType", + "NodeAddressType", + "ResourceName", + "FinalizerName", + "NamespacePhase", + "NamespaceConditionType", + "LimitType", + "ResourceQuotaScope", + "ScopeSelectorOperator", + "SecretType", + "ComponentConditionType", + "ProcMountType", + "ResourceList" + ], + "metav1": [ + "TypeMeta", + "ListMeta", + "ObjectMeta", + "OwnerReference", + "ListOptions", + "GetOptions", + "DeleteOptions", + "CreateOptions", + "PatchOptions", + "ApplyOptions", + "UpdateOptions", + "Preconditions", + "Status", + "StatusDetails", + "StatusCause", + "List", + "APIVersions", + "APIGroupList", + "APIGroup", + "ServerAddressByClientCIDR", + "GroupVersionForDiscovery", + "APIResource", + "APIResourceList", + "RootPaths", + "Patch", + "LabelSelector", + "LabelSelectorRequirement", + "ManagedFieldsEntry", + "Table", + "TableColumnDefinition", + "TableRow", + "TableRowCondition", + "TableOptions", + "PartialObjectMetadata", + "PartialObjectMetadataList", + "FooStatus", + "Condition", + "ResourceVersionMatch", + "DeletionPropagation", + "StatusReason", + "CauseType", + "LabelSelectorOperator", + "ManagedFieldsOperationType", + "RowConditionType", + "ConditionStatus", + "IncludeObjectPolicy" + ], + "rbacv1": [ + "PolicyRule", + "Subject", + "RoleRef", + "Role", + "RoleBinding", + "RoleBindingList", + "RoleList", + "ClusterRole", + "AggregationRule", + "ClusterRoleBinding", + "ClusterRoleBindingList", + "ClusterRoleList" + ], + "schedulingv1": [ + "PriorityClass", + "PriorityClassList" + ] +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..bfe61ac --- /dev/null +++ b/go.mod @@ -0,0 +1,32 @@ +module helm_to_controller/packages + +go 1.19 + +require ( + github.com/liyue201/gostl v1.2.0 + github.com/sirupsen/logrus v1.9.3 + k8s.io/api v0.27.3 + k8s.io/apimachinery v0.27.3 + k8s.io/kubectl v0.27.3 +) + +require ( + github.com/go-logr/logr v1.2.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/client-go v0.27.3 // indirect + k8s.io/klog/v2 v2.90.1 // indirect + k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2d6f204 --- /dev/null +++ b/go.sum @@ -0,0 +1,101 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/liyue201/gostl v1.2.0 h1:hiijDhO7u/hpJ9/UX7ffW8cbKCnOMn+eu5U79MJE0b8= +github.com/liyue201/gostl v1.2.0/go.mod h1:hmiO0/B1JgfZWtzpst0Y4VSu11iJvM6jHnRvINYB6Sg= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y= +k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg= +k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM= +k8s.io/apimachinery v0.27.3/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= +k8s.io/client-go v0.27.3 h1:7dnEGHZEJld3lYwxvLl7WoehK6lAq7GvgjxpA3nv1E8= +k8s.io/client-go v0.27.3/go.mod h1:2MBEKuTo6V1lbKy3z1euEGnhPfGZLKTS9tiJ2xodM48= +k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= +k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kubectl v0.27.3 h1:HyC4o+8rCYheGDWrkcOQHGwDmyLKR5bxXFgpvF82BOw= +k8s.io/kubectl v0.27.3/go.mod h1:g9OQNCC2zxT+LT3FS09ZYqnDhlvsKAfFq76oyarBcq4= +k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= +k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/inputs/README.md b/inputs/README.md new file mode 100644 index 0000000..42f4615 --- /dev/null +++ b/inputs/README.md @@ -0,0 +1 @@ +# helm-operatort-sdk \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..ee5ed81 --- /dev/null +++ b/main.go @@ -0,0 +1,187 @@ +package main + +import ( + "bufio" + "fmt" + "helm_to_controller/packages/common" + "io" + "os" + "strings" + + // logging "github.com/op/go-logging" + + "github.com/liyue201/gostl/ds/set" + "github.com/liyue201/gostl/utils/comparator" + "github.com/sirupsen/logrus" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer/yaml" + "k8s.io/kubectl/pkg/scheme" +) + +var runtimeSupportKinds = []string{"Deployment", "Service", "Secret", "Role", "RoleBinding", "ClusterRoleBinding", + "PersistentVolumeClaim", "StatefulSet", "ServiceAccount", "ClusterRole", "PriorityClass", "ConfigMap"} +var runtimeSupportKindSet = set.New[string](comparator.StringComparator, set.WithGoroutineSafe()) + +func init() { + // Runtime Support Kind Set contains the set of all kinds whose runtime-support is handled by the script + for _, val := range runtimeSupportKinds { + runtimeSupportKindSet.Insert(val) + } +} + +/* +Convert the KRM Resources to *unstructured.Unstructured (map[string]interface{}) +Returns the *unstructured.Unstructured Object, GroupVersionKind (gvk), error +*/ +func unstructuredDecode(data []byte) (*unstructured.Unstructured, *schema.GroupVersionKind, error) { + obj := &unstructured.Unstructured{} + // Decode YAML into unstructured.Unstructured + dec := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) + _, gvk, err := dec.Decode(data, nil, obj) + if err != nil { + return nil, nil, err + } + return obj, gvk, nil +} + +/* +Input: Reads the yaml file from filepath +Output: + + runtimeObjList: List of runtime Objects Converted from the input yaml + gvkList : List of Group-Version-Kind for the runtime objects of runtimeObjList, mapped Index-wise + unstructObjList: List of unstructured Objects Converted from the input yaml, whose Kind are not default to kubernetes| Third Party Kinds + unstructGvkList: List of Group-Version-Kind for the unstructured objects of unstructObjList, mapped Index-wise +*/ +func handleSingleYaml(filepath string) (runtimeObjList []runtime.Object, gvkList []schema.GroupVersionKind, unstructObjList []unstructured.Unstructured, unstructGvkList []schema.GroupVersionKind) { + file, err := os.Open(filepath) + if err != nil { + logrus.Error("Error While Opening YAML file | ", filepath, " \t |", err) + return + } + fp := bufio.NewReader(file) + data, err := io.ReadAll(fp) + if err != nil { + logrus.Error("Error While Reading YAML file | ", filepath, " \t |", err) + return + } + // A Single yaml can contain muliple KRM reosurces, separated by ---, Therefore Spliting the yaml-file-content over "---" to get single KRM Resource + for _, doc := range strings.Split(string(data), "\n---") { + if doc == "" { + continue + } + // Parsing the KRM Resource to get the Kind which will decide to use either runtime-object-method or unstructured.Unstructured method + unstructObject, gvk, err := unstructuredDecode([]byte(doc)) + if err != nil { + logrus.Error("Unable to convert yaml to unstructured |", err) + continue + } + resourceKind := gvk.Kind + if runtimeSupportKindSet.Contains(resourceKind) { + // Handle the current yaml with runtimeObject method + decoder := scheme.Codecs.UniversalDeserializer() + runtimeObject, gvk, err := decoder.Decode([]byte(doc), nil, nil) + if err != nil { + logrus.Error("Cant decode the section of yaml, by Runtime-Object \t |", err) + continue + } + runtimeObjList = append(runtimeObjList, runtimeObject) + gvkList = append(gvkList, *gvk) + } else { + logrus.Info("Kind | ", resourceKind, " Would Be Treated as Third Party Kind") + unstructObjList = append(unstructObjList, *unstructObject) + unstructGvkList = append(unstructGvkList, *gvk) + } + } + return +} + +func init() { + // Setting the Logrus Logging Level + lvl := "debug" + lvl = "info" + // lvl = "error" + ll, err := logrus.ParseLevel(lvl) + if err != nil { + ll = logrus.DebugLevel + } + logrus.SetLevel(ll) +} + +func main() { + // curHelmChart := "testing_helpers/free5gc_helm_chart" + curHelmChart := "inputs" + cmdArgs := os.Args[1:] + if len(cmdArgs) != 0 { + curHelmChart = cmdArgs[0] + } + namespace := "" + if len(cmdArgs) >= 2 { + namespace = cmdArgs[1] + } + + var helmYamlConvertor = common.HelmYamlConvertor{Namespace: namespace, Chartpath: curHelmChart} + err := helmYamlConvertor.ConvertHelmToYaml() + if err != nil { + logrus.Fatal("Unable to Convert Helm to Yamls| Error | ", err) + } + allYamlPaths := common.RecursiveListYamls("temp/templated") + + // Intialising Convertor Structs/Classes + var jsonStringConverterObj = common.JsonStringConverter{} + jsonStringConverterObj.Intialise() + var goFileObj = common.GoFile{Namespace: namespace} + goFileObj.Intialise(runtimeSupportKinds) + var runtimeJsonConverterObj = common.RuntimeJsonConverter{} + var unstructStringConverterObj = common.UnstructStringConverter{} + + // Loop over each Yaml File (recursively) and get their gocodes + var gocodes = map[string][]string{} + for _, yamlfile := range allYamlPaths { + logrus.Info("CurFile --> | ", yamlfile) + runtimeObjList, gvkList, unstructObjList, unstructGvkList := handleSingleYaml(yamlfile) + // _, _ = runtimeObjList, gvkList + // _, _ = unstructGvkList, unstructObjList + for i := 0; i < len(runtimeObjList); i++ { + logrus.Info(fmt.Sprintf(" Current KRM Resource| Kind : %s| YamlFilePath : %s", gvkList[i].Kind, yamlfile)) + err := runtimeJsonConverterObj.Convert(runtimeObjList[i], gvkList[i]) + if err != nil { + logrus.Error("\t Converting Runtime to Json Failed (Skipping Current Resource)| Error : ", err) + continue + } + + logrus.Info("\t Converting Runtime to Json Completed") + gocodeStr, err := jsonStringConverterObj.Convert(gvkList[i]) + if err != nil { + logrus.Info("\t Converting Json to String Failed (Skipping Current Resource)| Error : ", err) + continue + } + gocodes[gvkList[i].Kind] = append(gocodes[gvkList[i].Kind], gocodeStr) + logrus.Info("\t Converting Json to String Completed ") + } + + for i := 0; i < len(unstructObjList); i++ { + gocode := unstructStringConverterObj.Convert(unstructObjList[i]) + // _ = unstructGvkList[i] + // gocodes["third_party_kinds"] = append(gocodes["third_party_kinds"], gocode) + gocodes[unstructGvkList[i].Kind] = append(gocodes[unstructGvkList[i].Kind], gocode) + logrus.Info("\t Converting Unstructured to String Completed ") + } + } + logrus.Info("----------------- Writing GO Code ---------------------------------") + goFileObj.Generate(gocodes) + goFileObj.WriteToFile() + logrus.Info("----------------- Program Run Successful| Summary ---------------------------------") + for resourceType, resourceList := range gocodes { + logrus.Info(resourceType, "\t\t |", len(resourceList)) + } + err = os.RemoveAll("temp") + if err != nil { + logrus.Warn("Failed to delete the Temp Directory| Error | ", err) + logrus.Warn("Manual Delete Advised| temp") + } + +} diff --git a/outputs/README.md b/outputs/README.md new file mode 100644 index 0000000..42f4615 --- /dev/null +++ b/outputs/README.md @@ -0,0 +1 @@ +# helm-operatort-sdk \ No newline at end of file diff --git a/to-do.txt b/to-do.txt new file mode 100644 index 0000000..b94ed05 --- /dev/null +++ b/to-do.txt @@ -0,0 +1,18 @@ +1: Identifying and Handling Private Attributes (Special Cases), Currently Found Private Attributes are: + ResourceList: Handled + v1.Time: Handled + +2: Automation of struct_module_mapping and enum_module_mapping creation (enums) (Done for corev1, appsv1, rbacv1, metav1, schedulingv1) + +3: Handling Unused Modules in the go-code generated: Partially Done By HardCoding + +4: To check if converting from helm to yaml by helm-go client is feasible or not, since it will eliminate Helm as prerequiste for the script + +5: Extending The Support For Enum-pointers (Example: PreemptionPolicy attribute in schedulingv1.PriorityClass) + +---------- TMP Branch Text --------------------- +It should only come in new branch! +Object Oriented Design: +Global Variables and Variables that were getting passing from one fxn to another are made private attributes of struct +Attributes that can be set by user are made Public +Every Common Package File (except util.go), represents a struct (class) with its own methods From 117b114890a8b9cdf67ad9d6531e42a3e153b5e7 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Wed, 1 Nov 2023 15:56:26 +0530 Subject: [PATCH 02/19] Secret-Bug-Fix and Multi-Line Strings are now handled with concatenated double-quote string --- common/json_to_string.go | 15 ++++++++++----- common/runtime_to_json.go | 10 ++++++---- common/string_to_gofile.go | 18 +++++++++++++++++- common/unstruct_to_string.go | 5 +++-- common/util.go | 16 ++++++++++++++++ 5 files changed, 52 insertions(+), 12 deletions(-) diff --git a/common/json_to_string.go b/common/json_to_string.go index e889bf8..baf8def 100644 --- a/common/json_to_string.go +++ b/common/json_to_string.go @@ -150,7 +150,8 @@ func (obj *JsonStringConverter) formatTypeVal(objType string, objVal string, tab objVal = objVal[1 : len(objVal)-1] // Removing the double quotes from the objVal (because we need int) return fmt.Sprintf("%s(%s)", objType, objVal) } else if objType == "[]uint8" || objType == "[]byte" { - return fmt.Sprintf("%s(%s)", objType, objVal) + // Generally []uint8 is only used for secret + return fmt.Sprintf("getDataForSecret(%s)", objVal) } // Special Data-Types area Ends @@ -307,12 +308,16 @@ func (obj *JsonStringConverter) traverseJson(v reflect.Value, curObjType string, logrus.Debug(repeat("\t", tabs), v.String()) data := v.String() if strings.Contains(data, "\n") { + return handleMultiLineStrings(data) // New Lines Are now handled fmt.Sprint - // Since now The string goes under ``, therefore we don't need to replace /" with ", + // Since now The string goes under ``, therefore we don't need to replace \" with ", and \\ with \, // So, reverting the change that is currently being done in runtimetojson - data = strings.ReplaceAll(data, "\\\"", "\"") // Replacing String containing /" with " - data = fmt.Sprintf("fmt.Sprint(`%s`)", data) - return data + // data = strings.ReplaceAll(data, "\\\\", "\\") // Replacing String containing \\ with \ + // data = strings.ReplaceAll(data, "\\\"", "\"") // Replacing String containing \" with " + // // But now `` comes with its own problem, Handling backquote in a backquoted string + // data = strings.ReplaceAll(data, "`", "` + \"`\" + `") // Replacing String containing ` with ` + "`" + ' + // data = fmt.Sprintf("fmt.Sprint(`%s`)", data) + // return data } return "\"" + data + "\"" // Need to return the output with double quotes, " --> /" diff --git a/common/runtime_to_json.go b/common/runtime_to_json.go index 0fad966..f20fa6b 100644 --- a/common/runtime_to_json.go +++ b/common/runtime_to_json.go @@ -100,8 +100,10 @@ func (obj *RuntimeJsonConverter) runDfsJsonOmitEmpty(curObj interface{}, tabs in if data == "" { // "" is considered to be default value, Therefore, Omitting it return nil } - - data = strings.ReplaceAll(data, "\"", "\\\"") // Replacing String containing " with /" + // Todo: Need much better handling to strings, Since Different combinations can lead to bad-buggy results + // Below Additional Replace helps in building integrity of the "" string + data = strings.ReplaceAll(data, "\\", "\\\\") // Replacing String containing \ with \\ + data = strings.ReplaceAll(data, "\"", "\\\"") // Replacing String containing " with \" return data case reflect.Slice: @@ -193,8 +195,8 @@ Example: */ func (obj *RuntimeJsonConverter) Convert(runtimeObj runtime.Object, gvk schema.GroupVersionKind) error { if gvk.Version != "v1" { - logrus.Error("Currently Only Api-Version v1 is supported (Skipping)") - return fmt.Errorf("currently only Api-version v1 is supported") + logrus.Error("Currently Only Api-Version v1 is supported (Skipping)| Given Version " + gvk.Version) + return fmt.Errorf("currently only Api-version v1 is supported| Given Version " + gvk.Version) } logrus.Debug("----------------------------------Your Runtime Object--------------------\n", runtimeObj) logrus.Debug("----------------------------------Your Runtime Object Ends--------------------\n") diff --git a/common/string_to_gofile.go b/common/string_to_gofile.go index b60939b..41ac1b6 100644 --- a/common/string_to_gofile.go +++ b/common/string_to_gofile.go @@ -98,7 +98,8 @@ import ( "context" "fmt" "time" - + "encoding/base64" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -159,6 +160,21 @@ func stringPtr(val string) *string { a := val return &a } + +func getDataForSecret(encodedVal string) []byte { + /* + Concept: Based on my Understanding, corev1.Secret requires the actual data(not encoded) as secret-Data + But in general terms, we put encoded values in secret-data, which make sense (why to write actual value in readable format) + This function takes the encodedVal and decodes it and returns + */ + decodeVal, err := base64.StdEncoding.DecodeString(encodedVal) + if err != nil { + fmt.Println("Unable to decode the SecretVal ", encodedVal, " || This Secret Will Probably would give error during deployment| Kindly Check") + return []byte(encodedVal) + } + return decodeVal +} + `, packageName) + obj.getMasterFxn(fxnCreated, true) + obj.getMasterFxn(fxnCreated, false) + allFxn mainfxn := ` func main(){ diff --git a/common/unstruct_to_string.go b/common/unstruct_to_string.go index 2de1011..24cd693 100644 --- a/common/unstruct_to_string.go +++ b/common/unstruct_to_string.go @@ -63,8 +63,9 @@ func (obj *UnstructStringConverter) runDfsUnstruct(v reflect.Value, tabs int) st if strings.Contains(data, "\n") { // New Lines Are now handled fmt.Sprint - data = fmt.Sprintf("fmt.Sprint(`%s`)", data) - return data + // data = strings.ReplaceAll(data, "`", "` + \"`\" + `") // Replacing String containing ` with ` + "`" + ' + // data = fmt.Sprintf("fmt.Sprint(`%s`)", data) + return handleMultiLineStrings(data) } data = strings.ReplaceAll(data, "\"", "\\\"") // Need to preserve double quotes, therefore preserving by adding a backslash (" --> /") return "\"" + data + "\"" // Sending with double quotes diff --git a/common/util.go b/common/util.go index 0f08386..ff7da11 100644 --- a/common/util.go +++ b/common/util.go @@ -4,6 +4,7 @@ import ( "errors" "log" "os" + "strings" ) /* @@ -56,3 +57,18 @@ func RecursiveListYamls(curFolder string) (yamlfiles []string) { } return } + +func handleMultiLineStrings(input string) string { + /* There are different ways to handle Multi-Line-Strings + Method-1: Usage of "Str1" + "Str2" + Replacing "\n" with `\n " + \n "` + "Str1\nStr2" : + Str1\n" + + "Str2 + */ + input = strings.ReplaceAll(input, "\n", "\\n\" + \n \"") + return "\"" + input + "\"" + + // Method-2: Usage of `` for Raw-String Literal: To be Decided if Method 1 has any limitations + +} From fd8fb905ee211bdec7215810d38d1bc6f052e793 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Wed, 1 Nov 2023 16:01:23 +0530 Subject: [PATCH 03/19] Adding Unit Test-Cases --- common/json_to_string.go | 9 - common/json_to_string_test.go | 172 ++++++++++++++++++++ common/runtime_to_json_test.go | 142 ++++++++++++++++ common/string_to_gofile_test.go | 110 +++++++++++++ common/test_helper.go | 62 +++++++ common/tests/expected-json/deployment.json | 125 ++++++++++++++ common/tests/test-yamls/deployment.yaml | 21 +++ common/tests/test-yamls/third-party-cr.yaml | 11 ++ common/unstruct_to_string_test.go | 58 +++++++ common/util_test.go | 55 +++++++ main_test.go | 36 ++++ 11 files changed, 792 insertions(+), 9 deletions(-) create mode 100644 common/json_to_string_test.go create mode 100644 common/runtime_to_json_test.go create mode 100644 common/string_to_gofile_test.go create mode 100644 common/test_helper.go create mode 100644 common/tests/expected-json/deployment.json create mode 100644 common/tests/test-yamls/deployment.yaml create mode 100644 common/tests/test-yamls/third-party-cr.yaml create mode 100644 common/unstruct_to_string_test.go create mode 100644 common/util_test.go create mode 100644 main_test.go diff --git a/common/json_to_string.go b/common/json_to_string.go index baf8def..c0e751a 100644 --- a/common/json_to_string.go +++ b/common/json_to_string.go @@ -309,15 +309,6 @@ func (obj *JsonStringConverter) traverseJson(v reflect.Value, curObjType string, data := v.String() if strings.Contains(data, "\n") { return handleMultiLineStrings(data) - // New Lines Are now handled fmt.Sprint - // Since now The string goes under ``, therefore we don't need to replace \" with ", and \\ with \, - // So, reverting the change that is currently being done in runtimetojson - // data = strings.ReplaceAll(data, "\\\\", "\\") // Replacing String containing \\ with \ - // data = strings.ReplaceAll(data, "\\\"", "\"") // Replacing String containing \" with " - // // But now `` comes with its own problem, Handling backquote in a backquoted string - // data = strings.ReplaceAll(data, "`", "` + \"`\" + `") // Replacing String containing ` with ` + "`" + ' - // data = fmt.Sprintf("fmt.Sprint(`%s`)", data) - // return data } return "\"" + data + "\"" // Need to return the output with double quotes, " --> /" diff --git a/common/json_to_string_test.go b/common/json_to_string_test.go new file mode 100644 index 0000000..2573eb0 --- /dev/null +++ b/common/json_to_string_test.go @@ -0,0 +1,172 @@ +package common + +import ( + "fmt" + "reflect" + "testing" + + "github.com/sirupsen/logrus" +) + +var jsonStringConverterObj = JsonStringConverter{} + +func TestIntialise(t *testing.T) { + ll, _ := logrus.ParseLevel("fatal") + logrus.SetLevel(ll) + + moduleStructMappingFile := "../config/struct_module_mapping.json" + jsonStringConverterObj.setModStructMapping(moduleStructMappingFile) + if len(jsonStringConverterObj.globalStructMapping) == 0 { + t.Errorf("Intialise Failed| Unable to Populate Global-Struct-Mapping From Config-File %s", moduleStructMappingFile) + } + + enumModuleMapping := "../config/enum_module_mapping.json" + jsonStringConverterObj.setEnums(enumModuleMapping) + if jsonStringConverterObj.globalEnumsSet.Size() == 0 { + t.Errorf("Intialise Failed| Unable to Populate Enum-Module-Mapping From Config-File %s", enumModuleMapping) + } +} + +func TestCheckOpeningAndClosingBraces(t *testing.T) { + tests := []Tests{ + {"{{}}{}", true}, + {"{{}}}", false}, + } + + for _, test := range tests { + jsonStringConverterObj.generatedGoCode = test.input.(string) + result := jsonStringConverterObj.checkOpeningAndClosingBraces() + if result != test.expected.(bool) { + t.Errorf("checkOpeningAndClosingBraces Failed| Expected %t | Got %t | Input %s", test.expected.(bool), result, test.input.(string)) + } + } +} + +/* +Tests For Base-Cases (int, bool, string, *int, *string, *bool) +*/ +func TestFormatTypeValBaseCases(t *testing.T) { + tests := []Tests{ + {[]string{"int32", "\"34\""}, "34"}, + {[]string{"string", "\"34\""}, "\"34\""}, + {[]string{"bool", "\"false\""}, "\"false\""}, + {[]string{"*int32", "\"34\""}, "int32Ptr(34)"}, + {[]string{"*string", "\"34\""}, "stringPtr(\"34\")"}, + {[]string{"*bool", "\"false\""}, "boolPtr(\"false\")"}, + } + for _, test := range tests { + testTyp, testVal := test.input.([]string)[0], test.input.([]string)[1] + expected := test.expected.(string) + result := jsonStringConverterObj.formatTypeVal(testTyp, testVal, 0) + if result != expected { + t.Errorf("FormatTypeVal Failed| Input : Type %s \t Value %s \nExpected %s \t Got %s\n", testTyp, testVal, expected, result) + } + + } +} + +/* +Tests For Composite-Literals (struct, enums, *struct) +*/ +func TestFormatTypeValCompositeCases(t *testing.T) { + tests := []Tests{ + { + input: []string{"v1.ObjectMeta", "\tName : \"ABC\""}, + expected: `metav1.ObjectMeta{ + Name : "ABC" +}`}, + { + input: []string{"*v1.ObjectMeta", "\tName : \"ABC\""}, + expected: `&metav1.ObjectMeta{ + Name : "ABC" +}`}, + { + input: []string{"v1.IncludeObjectPolicy", "\"True\""}, + expected: "metav1.IncludeObjectPolicy(\"True\")", + }, + } + + for _, test := range tests { + testTyp, testVal := test.input.([]string)[0], test.input.([]string)[1] + expected := test.expected.(string) + result := jsonStringConverterObj.formatTypeVal(testTyp, testVal, 0) + if result != expected { + // compare2Strings(result, expected) + t.Errorf("FormatTypeVal Failed (Composite-Literal)| Input : Type %s \t Value %s \nExpected %s \t Got %s\n", testTyp, testVal, expected, result) + } + + } +} + +/* +Tests For Special-Type ([]byte, intstr.Type) +*/ +func TestFormatTypeValSpecialCases(t *testing.T) { + tests := []Tests{ + {[]string{"intstr.Type", "\"56\""}, "intstr.Type(56)"}, + {[]string{"[]byte", "\"my-secret\""}, "getDataForSecret(\"my-secret\")"}, + } + + for _, test := range tests { + testTyp, testVal := test.input.([]string)[0], test.input.([]string)[1] + expected := test.expected.(string) + result := jsonStringConverterObj.formatTypeVal(testTyp, testVal, 0) + if result != expected { + // compare2Strings(result, expected) + t.Errorf("FormatTypeVal Failed (Special Cases)| Input : Type %s \t Value %s \nExpected %s \t Got %s\n", testTyp, testVal, expected, result) + } + + } +} + +/* +Tests TraverseJson for Composite Cases (struct, slice, map) +*/ +func TestTraverseJsonCompositeCases(t *testing.T) { + tests := []Tests{ + { + input: []string{"abc", "def"}, + expected: ` +"abc", +"def", +`, + }, + { + input: map[string]interface{}{ + "Condition": map[string]interface{}{ + "type": "bool", + "val": true, + }, + }, + expected: "Condition : true, ", + }, + { + input: map[string]interface{}{ + "Labels": map[string]interface{}{ + "type": "map[string]string", + "val": map[string]interface{}{ + "label1": "app1", + }, + }, + }, + expected: "Labels : map[string]string{\n\t\"label1\" : \"app1\",\n},", + }, + } + + for _, test := range tests { + expected := test.expected.(string) + result := jsonStringConverterObj.traverseJson(reflect.ValueOf(test.input), fmt.Sprint(reflect.TypeOf(test.input)), 0) + if result != expected { + // compare2Strings(result, expected) + t.Errorf("TraverseJson Failed (Composite-Literal)| Input : Type %v \nExpected %s \t Got %s\n", test.input, expected, result) + } + + } +} + +func TestJsonToGoCode(t *testing.T) { + jsonStringConverterObj.jsonToGoCode("tests/expected-json/deployment.json") + if jsonStringConverterObj.generatedGoCode == "" { + t.Errorf("JsonToGoCode Failed| Unable to Convert JSON To Go-Code") + } +} diff --git a/common/runtime_to_json_test.go b/common/runtime_to_json_test.go new file mode 100644 index 0000000..89de744 --- /dev/null +++ b/common/runtime_to_json_test.go @@ -0,0 +1,142 @@ +package common + +import ( + "encoding/json" + "os" + "reflect" + "testing" + "time" + + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kubectl/pkg/scheme" +) + +var runtimeJsonConverterObj = RuntimeJsonConverter{} + +/* +It Tests the Full-Flow From Runtime-Obj to Json +And Also Test For Combined-Cases (Struct-in-a-Struct, Slice-in-a-Struct, Struct-in-a-Slice) & so-on +*/ +func TestConvert(t *testing.T) { + inputFile := "tests/test-yamls/deployment.yaml" + + decoder := scheme.Codecs.UniversalDeserializer() + data, err := getFileContents(inputFile) + if err != nil { + t.Errorf("Unable to Load File %s| Error %s", inputFile, err) + } + runtimeObject, gvk, err := decoder.Decode(data, nil, nil) + if err != nil { + t.Errorf("Unable to Decode the Yaml| %s", inputFile) + } + runtimeJsonConverterObj.Convert(runtimeObject, *gvk) + + resultFile := "temp/temp.json" + expectedFile := "tests/expected-json/deployment.json" + resultData, _ := getFileContents(resultFile) + expectedData, _ := getFileContents(expectedFile) + + var result interface{} + var expected interface{} + json.Unmarshal([]byte(resultData), &result) + json.Unmarshal([]byte(expectedData), &expected) + if !reflect.DeepEqual(result, expected) { + t.Errorf("Result Doesn't Matches with Expected| Kindly Check |\n ResultFile %s \t| ExpectedFile %s\n", resultFile, expectedFile) + } + os.RemoveAll("temp") +} + +/* +Tests For Base Cases in DFS Traversal (Float, Int, String, Bool) +*/ +func TestRunDfsJsonOmitEmptyBaseCases(t *testing.T) { + tests := []Tests{ + {"abc", "abc"}, + {"", nil}, + {5, "5"}, + {3.14, "3.14"}, + {0, nil}, + {true, true}, + } + for _, test := range tests { + result := runtimeJsonConverterObj.runDfsJsonOmitEmpty(test.input, 0) + if result != test.expected { + t.Errorf("Test DfsJson (Base Cases) Failed | EXpected %v | Got %v", test.expected, result) + } + } +} + +/* +Tests For Complex Cases in DFS Traversal (Struct, Slices, Maps) +*/ +func TestRunDfsJsonOmitEmptyComplexCases(t *testing.T) { + tests := []Tests{ + {[]string{"abc", "def", ""}, []interface{}{"abc", "def", ""}}, + {[]byte("my-secret"), "bXktc2VjcmV0"}, //Base64 encoded version of my-secret// This is also a TODO task (to check if it is important or not) + // {[]interface{}{0, "abc"}, []interface{}{"", "abc"}},// This is TODO Task + {metav1.ObjectMeta{}, nil}, //Empty Struct Should Return Nil + { + input: map[string]interface{}{ + "key1": "abc", + "key2": 6, + }, + expected: map[string]interface{}{ + "key1": "abc", + "key2": "6", + }, + }, + { + input: metav1.ObjectMeta{ + Name: "tests", + Generation: 2, + }, + expected: map[string]interface{}{ + "Name": map[string]interface{}{ + "type": "string", + "val": "tests", + }, + "Generation": map[string]interface{}{ + "type": "int64", + "val": "2", + }, + }, + }, + } + for _, test := range tests { + result := runtimeJsonConverterObj.runDfsJsonOmitEmpty(test.input, 0) + if !reflect.DeepEqual(test.expected, result) { + t.Errorf("Test DfsJson (Complex Cases) Failed | Expected %v | Got %v", test.expected, result) + } + } + _ = metav1.ConditionStatus("True") +} + +/* +Tests For Special Cases in DFS Traversal (resource.Quantity, v1.Time) +*/ +func TestRunDfsJsonOmitEmptySpecialCases(t *testing.T) { + tests := []Tests{ + {resource.MustParse("0"), nil}, + {metav1.Time{}, nil}, + { + input: resource.MustParse("64Mi"), + expected: map[string]string{"type": "int", "val": "resource.MustParse(\"64Mi\")"}, + }, + { + input: metav1.Time{Time: time.Time.AddDate(time.Time{}, 2, 3, 0)}, + expected: map[string]interface{}{ + "Time": map[string]string{ + "type": "int", + "val": time.Time.AddDate(time.Time{}, 2, 3, 0).GoString(), + }, + }, + }, + } + for _, test := range tests { + result := runtimeJsonConverterObj.runDfsJsonOmitEmpty(test.input, 0) + if !reflect.DeepEqual(test.expected, result) { + t.Errorf("Test DfsJson (Special Cases) Failed | Expected %v | Got %v", test.expected, result) + } + } +} diff --git a/common/string_to_gofile_test.go b/common/string_to_gofile_test.go new file mode 100644 index 0000000..97dd6d2 --- /dev/null +++ b/common/string_to_gofile_test.go @@ -0,0 +1,110 @@ +package common + +import ( + "strings" + "testing" + + "github.com/sirupsen/logrus" +) + +var goFileObj = GoFile{Namespace: "default"} + +func TestGoFileIntialise(t *testing.T) { + ll, _ := logrus.ParseLevel("fatal") + logrus.SetLevel(ll) + + var runtimeSupportKinds = []string{"Deployment", "Service", "Secret", "Role", "RoleBinding", "ClusterRoleBinding", + "PersistentVolumeClaim", "StatefulSet", "ServiceAccount", "ClusterRole", "PriorityClass", "ConfigMap"} + + goFileObj.Intialise(runtimeSupportKinds) + if goFileObj.runtimeSupportKindSet.Size() == 0 { + t.Error("Runtime Support Kind Set Failed to Intialise") + } +} + +func TestGetRunnableFunction(t *testing.T) { + result := goFileObj.getRunnableFunction("Deployment", []string{"appsv1.Deployment{struct_attributes...}"}) + expectedLines := []string{"func GetDeployment() []appsv1.Deployment{", "deployment1 := appsv1.Deployment{struct_attributes...}", + "return []appsv1.Deployment{deployment1, }"} + for _, expected := range expectedLines { + if !strings.Contains(result, expected) { + t.Errorf("Current Line '%s' Not Found in Runnable Function| Actual Output : %s \n", expected, result) + break + } + } + +} + +func TestGetMasterFxnCreateAll(t *testing.T) { + result := goFileObj.getMasterFxn([]string{"GetDeployment"}, true) + expectedContent := ` +/* +// Before Uncommenting the following function, Make sure the data-type of r is same as of your Reconciler, +// Replace "YourKindReconciler" with the type of your Reconciler +func (r *YourKindReconciler)CreateAll(){ + var err error + namespaceProvided := "default" + + for _, resource := range GetDeployment{ + if resource.GetNamespace() == ""{ + resource.SetNamespace(namespaceProvided) + } + err = r.Create(context.TODO(), resource) + if err != nil { + fmt.Println("Erorr During Creating resource of GetDeployment| Error --> |", err) + } + } + +} +*/ +` + expectedLines := strings.Split(expectedContent, "\n") + for _, expected := range expectedLines { + if !strings.Contains(result, expected) { + t.Errorf("Current Line '%s' Not Found in Runnable Function| Actual Output : %s \n", expected, result) + break + } + } +} + +func TestGetMasterFxnDeleteAll(t *testing.T) { + result := goFileObj.getMasterFxn([]string{"GetDeployment"}, false) + expectedContent := ` +/* +// Before Uncommenting the following function, Make sure the data-type of r is same as of your Reconciler, +// Replace "YourKindReconciler" with the type of your Reconciler +func (r *YourKindReconciler)DeleteAll(){ + var err error + namespaceProvided := "default" + + for _, resource := range GetDeployment{ + if resource.GetNamespace() == ""{ + resource.SetNamespace(namespaceProvided) + } + err = r.Delete(context.TODO(), resource) + if err != nil { + fmt.Println("Erorr During Deleting resource of GetDeployment| Error --> |", err) + } + } + +} +*/ +` + expectedLines := strings.Split(expectedContent, "\n") + for _, expected := range expectedLines { + if !strings.Contains(result, expected) { + t.Errorf("Current Line '%s' Not Found in Runnable Function| Actual Output : %s \n", expected, result) + break + } + } +} + +func TestGenerate(t *testing.T) { + input := map[string][]string{ + "Deployment": {"appsv1.Deployment{struct_attributes...}"}, + } + goFileObj.Generate(input) + if goFileObj.FileContent == "" { + t.Errorf("Generate GoCode Failed| Unable to Generate Go-File from Go-Code") + } +} diff --git a/common/test_helper.go b/common/test_helper.go new file mode 100644 index 0000000..9a0bbd7 --- /dev/null +++ b/common/test_helper.go @@ -0,0 +1,62 @@ +package common + +import ( + "bufio" + "io" + "os" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer/yaml" +) + +type Tests struct { + input interface{} + expected interface{} +} + +/* +Reads the File from FilePath and returns the file-data +*/ +func getFileContents(filePath string) ([]byte, error) { + file, err := os.Open(filePath) + if err != nil { + return nil, err + } + fp := bufio.NewReader(file) + data, err := io.ReadAll(fp) + if err != nil { + return nil, err + } + return data, nil + +} + +/* +Convert the KRM Resources to *unstructured.Unstructured (map[string]interface{}) +Returns the *unstructured.Unstructured Object, GroupVersionKind (gvk), error +*/ +func unstructuredDecode(data []byte) (*unstructured.Unstructured, *schema.GroupVersionKind, error) { + obj := &unstructured.Unstructured{} + // Decode YAML into unstructured.Unstructured + dec := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) + _, gvk, err := dec.Decode(data, nil, obj) + if err != nil { + return nil, nil, err + } + return obj, gvk, nil +} + +// func compare2Strings(a string, b string) { + +// lenA, lenB := len(a), len(b) +// fmt.Println(lenA, lenB) +// minL := lenA +// if lenB < lenA { +// minL = lenB +// } + +// for index := 0; index < minL; index++ { +// fmt.Printf("%d %c %c\n", index, a[index], b[index]) +// } +// } diff --git a/common/tests/expected-json/deployment.json b/common/tests/expected-json/deployment.json new file mode 100644 index 0000000..1e60fe8 --- /dev/null +++ b/common/tests/expected-json/deployment.json @@ -0,0 +1,125 @@ +{ + "ObjectMeta": { + "type": "v1.ObjectMeta", + "val": { + "Labels": { + "type": "map[string]string", + "val": { + "app": "nginx" + } + }, + "Name": { + "type": "string", + "val": "my-nginx" + } + } + }, + "Spec": { + "type": "v1.DeploymentSpec", + "val": { + "Paused": { + "type": "bool", + "val": false + }, + "Replicas": { + "type": "*int32", + "val": "2" + }, + "Selector": { + "type": "*v1.LabelSelector", + "val": { + "MatchLabels": { + "type": "map[string]string", + "val": { + "app": "nginx" + } + } + } + }, + "Template": { + "type": "v1.PodTemplateSpec", + "val": { + "ObjectMeta": { + "type": "v1.ObjectMeta", + "val": { + "Labels": { + "type": "map[string]string", + "val": { + "app": "nginx" + } + } + } + }, + "Spec": { + "type": "v1.PodSpec", + "val": { + "Containers": { + "type": "[]v1.Container", + "val": [ + { + "Image": { + "type": "string", + "val": "nginx:1.14.2" + }, + "Name": { + "type": "string", + "val": "nginx" + }, + "Ports": { + "type": "[]v1.ContainerPort", + "val": [ + { + "ContainerPort": { + "type": "int32", + "val": "80" + } + } + ] + }, + "Stdin": { + "type": "bool", + "val": false + }, + "StdinOnce": { + "type": "bool", + "val": false + }, + "TTY": { + "type": "bool", + "val": false + } + } + ] + }, + "HostIPC": { + "type": "bool", + "val": false + }, + "HostNetwork": { + "type": "bool", + "val": false + }, + "HostPID": { + "type": "bool", + "val": false + } + } + } + } + } + } + }, + "TypeMeta": { + "type": "v1.TypeMeta", + "val": { + "APIVersion": { + "type": "string", + "val": "apps/v1" + }, + "Kind": { + "type": "string", + "val": "Deployment" + } + } + } +} \ No newline at end of file diff --git a/common/tests/test-yamls/deployment.yaml b/common/tests/test-yamls/deployment.yaml new file mode 100644 index 0000000..0b17598 --- /dev/null +++ b/common/tests/test-yamls/deployment.yaml @@ -0,0 +1,21 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-nginx + labels: + app: nginx +spec: + replicas: 2 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 \ No newline at end of file diff --git a/common/tests/test-yamls/third-party-cr.yaml b/common/tests/test-yamls/third-party-cr.yaml new file mode 100644 index 0000000..5465b49 --- /dev/null +++ b/common/tests/test-yamls/third-party-cr.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ThirdPartyCR +metadata: + name: my-cr + namespace: default +spec: + abc: def + slice: + - name: bde + - name: hello + - name: 5 \ No newline at end of file diff --git a/common/unstruct_to_string_test.go b/common/unstruct_to_string_test.go new file mode 100644 index 0000000..c1e6c09 --- /dev/null +++ b/common/unstruct_to_string_test.go @@ -0,0 +1,58 @@ +package common + +import ( + "reflect" + "testing" +) + +var unstructStringConverterObj = UnstructStringConverter{} + +/* +Composite Cases : Maps, Slices +*/ +func TestRunDfsUnstructCompositeCases(t *testing.T) { + tests := []Tests{ + { + input: map[string]interface{}{ + "Name": "ABC", + }, + expected: "map[string]interface{}{\n\"Name\": \"ABC\",\n}", + }, + { + input: map[string]interface{}{ + "Replicas": 4, + }, + expected: "map[string]interface{}{\n\"Replicas\": 4,\n}", + }, + { + input: map[string]interface{}{ + "Conditions": []interface{}{true}, + }, + expected: "map[string]interface{}{\n\"Conditions\": []interface{}{\n\ttrue,\n\t},\n}", + }, + } + + for _, test := range tests { + result := unstructStringConverterObj.runDfsUnstruct(reflect.ValueOf(test.input), 0) + expected := test.expected.(string) + if expected != result { + t.Errorf("RunDFSUnstructTest Failed| Input %v \nExpected %s \tGot %s", test.input, expected, result) + } + } +} + +func TestConvertUnstruct(t *testing.T) { + inputFilePath := "tests/test-yamls/deployment.yaml" + data, err := getFileContents(inputFilePath) + if err != nil { + t.Errorf("Unable to Open file %s for Unstruct-Convert| Error %v", inputFilePath, err) + } + unstructObj, _, err := unstructuredDecode(data) + if err != nil { + t.Errorf("Unable to convert KRM OBject to Unstruct During Unstruct-Convert| Error %v", err) + } + result := unstructStringConverterObj.Convert(*unstructObj) + if result == "" { + t.Errorf("Unable to generate goCode for Unstruct Object") + } +} diff --git a/common/util_test.go b/common/util_test.go new file mode 100644 index 0000000..f656628 --- /dev/null +++ b/common/util_test.go @@ -0,0 +1,55 @@ +package common + +import ( + "os" + "reflect" + "testing" +) + +func TestRepeat(t *testing.T) { + result := repeat("a", 3) + if result != "aaa" { + t.Errorf("Util-tests | 'Repeat' test failed | Expected %s | Got %s", "aaa", result) + } +} + +func TestCreateDirIfDontExist(t *testing.T) { + err := createDirIfDontExist("tests/test_createdir") + if err != nil { + t.Errorf("Util-tests | 'CreateDirIfDontExist' test failed | %s", err) + } + + // Check If directory is created or not + folderContent, _ := os.ReadDir("tests") + testPassed := false + for _, files := range folderContent { + if files.Type().IsDir() { + if files.Name() == "test_createdir" { + testPassed = true + break + } + } + } + if !testPassed { + t.Errorf("Util-tests | 'CreateDirIfDontExist' test failed | Directory Not Found where expected") + } + os.RemoveAll("tests/test_createdir") +} + +func TestRecursiveListYamls(t *testing.T) { + result := RecursiveListYamls("tests") + expected := []string{"tests/test-yamls/deployment.yaml", "tests/test-yamls/third-party-cr.yaml"} + if !reflect.DeepEqual(result, expected) { + t.Errorf("Util-tests | 'RecursiveListYamls' test failed | \n Expected %v \n Got %v", expected, result) + } + +} + +func TestHandleMultiLineStrings(t *testing.T) { + input := "abc\nd" + result := handleMultiLineStrings(input) + expected := "\"abc\\n\" + \n \"d\"" // Expected "abc\n" + \n "d" where Second \n Represents actual new line + if result != expected { + t.Errorf("Util-tests | 'HandleMultiLineStrings' test failed | \n Expected %v \n Got %v", expected, result) + } +} diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..fa0a743 --- /dev/null +++ b/main_test.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + "testing" +) + +/* +Tests for Runtime-OBject Way of Handling KRM-Object +*/ +func TestHandleSingleYamlDeployment(t *testing.T) { + inputFilePath := "common/tests/test-yamls/deployment.yaml" + runtimeObjList, gvkList, unstructObjList, unstructGvkList := handleSingleYaml(inputFilePath) + fmt.Println(runtimeObjList, gvkList, unstructObjList, unstructGvkList) + if len(runtimeObjList) == 0 { + t.Errorf("Unable to convert yaml to RuntimeObject") + } + if gvkList[0].Kind != "Deployment" { + t.Errorf("Kind Detected is not what expected | Detected %s | Expected Deployment", gvkList[0].Kind) + } +} + +/* +Tests for Unstructured Way of Handling KRM-Object +*/ +func TestHandleSingleYamlCR(t *testing.T) { + inputFilePath := "common/tests/test-yamls/third-party-cr.yaml" + runtimeObjList, gvkList, unstructObjList, unstructGvkList := handleSingleYaml(inputFilePath) + fmt.Println(runtimeObjList, gvkList, unstructObjList, unstructGvkList) + if len(unstructObjList) == 0 { + t.Errorf("Unable to convert yaml to RuntimeObject") + } + if unstructGvkList[0].Kind != "ThirdPartyCR" { + t.Errorf("Kind Detected is not what expected | Detected %s | Expected ThirdPartyCR", unstructGvkList[0].Kind) + } +} From cdabe3cf9907b8e0e38406009c57770249fe4d19 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Wed, 1 Nov 2023 16:06:55 +0530 Subject: [PATCH 04/19] Adding Nephio CLA Headers --- common/helm_to_yaml.go | 16 ++++++++++++++++ common/json_to_string.go | 16 ++++++++++++++++ common/json_to_string_test.go | 16 ++++++++++++++++ common/runtime_to_json.go | 16 ++++++++++++++++ common/runtime_to_json_test.go | 16 ++++++++++++++++ common/string_to_gofile.go | 16 ++++++++++++++++ common/string_to_gofile_test.go | 16 ++++++++++++++++ common/test_helper.go | 16 ++++++++++++++++ common/unstruct_to_string.go | 16 ++++++++++++++++ common/unstruct_to_string_test.go | 16 ++++++++++++++++ common/util.go | 16 ++++++++++++++++ common/util_test.go | 16 ++++++++++++++++ main.go | 16 ++++++++++++++++ main_test.go | 16 ++++++++++++++++ to-do.txt | 16 ++++++++++++++++ 15 files changed, 240 insertions(+) diff --git a/common/helm_to_yaml.go b/common/helm_to_yaml.go index 2512dd1..dfaea2c 100644 --- a/common/helm_to_yaml.go +++ b/common/helm_to_yaml.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package common import ( diff --git a/common/json_to_string.go b/common/json_to_string.go index c0e751a..db3fd26 100644 --- a/common/json_to_string.go +++ b/common/json_to_string.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package common import ( diff --git a/common/json_to_string_test.go b/common/json_to_string_test.go index 2573eb0..cbf45c4 100644 --- a/common/json_to_string_test.go +++ b/common/json_to_string_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package common import ( diff --git a/common/runtime_to_json.go b/common/runtime_to_json.go index f20fa6b..49b472b 100644 --- a/common/runtime_to_json.go +++ b/common/runtime_to_json.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package common import ( diff --git a/common/runtime_to_json_test.go b/common/runtime_to_json_test.go index 89de744..4b02724 100644 --- a/common/runtime_to_json_test.go +++ b/common/runtime_to_json_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package common import ( diff --git a/common/string_to_gofile.go b/common/string_to_gofile.go index 41ac1b6..41436c3 100644 --- a/common/string_to_gofile.go +++ b/common/string_to_gofile.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package common import ( diff --git a/common/string_to_gofile_test.go b/common/string_to_gofile_test.go index 97dd6d2..71bbcdc 100644 --- a/common/string_to_gofile_test.go +++ b/common/string_to_gofile_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package common import ( diff --git a/common/test_helper.go b/common/test_helper.go index 9a0bbd7..108a6aa 100644 --- a/common/test_helper.go +++ b/common/test_helper.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package common import ( diff --git a/common/unstruct_to_string.go b/common/unstruct_to_string.go index 24cd693..2e3aa91 100644 --- a/common/unstruct_to_string.go +++ b/common/unstruct_to_string.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package common import ( diff --git a/common/unstruct_to_string_test.go b/common/unstruct_to_string_test.go index c1e6c09..abc5827 100644 --- a/common/unstruct_to_string_test.go +++ b/common/unstruct_to_string_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package common import ( diff --git a/common/util.go b/common/util.go index ff7da11..5303fab 100644 --- a/common/util.go +++ b/common/util.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package common import ( diff --git a/common/util_test.go b/common/util_test.go index f656628..1ddf016 100644 --- a/common/util_test.go +++ b/common/util_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package common import ( diff --git a/main.go b/main.go index ee5ed81..010545a 100644 --- a/main.go +++ b/main.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package main import ( diff --git a/main_test.go b/main_test.go index fa0a743..bf7f084 100644 --- a/main_test.go +++ b/main_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package main import ( diff --git a/to-do.txt b/to-do.txt index b94ed05..857c19d 100644 --- a/to-do.txt +++ b/to-do.txt @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + 1: Identifying and Handling Private Attributes (Special Cases), Currently Found Private Attributes are: ResourceList: Handled v1.Time: Handled From d881d8567b5d433914c5762602975479f51f722e Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Wed, 1 Nov 2023 16:32:04 +0530 Subject: [PATCH 05/19] Adding MakeFile for Unit-Testing, Linting And Gosec --- Makefile | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2bfbc7c --- /dev/null +++ b/Makefile @@ -0,0 +1,84 @@ +GO_VERSION ?= 1.20.2 +GOLANG_CI_VER ?= v1.52 +GOSEC_VER ?= 2.15.0 +TEST_COVERAGE_FILE=lcov.info +TEST_COVERAGE_HTML_FILE=coverage_unit.html +TEST_COVERAGE_FUNC_FILE=func_coverage.out + +# CONTAINER_RUNNABLE checks if tests and lint check can be run inside container. +PODMAN ?= $(shell podman -v > /dev/null 2>&1; echo $$?) +ifeq ($(PODMAN), 0) +CONTAINER_RUNTIME=podman +else +CONTAINER_RUNTIME=docker +endif +CONTAINER_RUNNABLE ?= $(shell $(CONTAINER_RUNTIME) -v > /dev/null 2>&1; echo $$?) + +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +# Setting SHELL to bash allows bash commands to be executed by recipes. +# This is a requirement for 'setup-envtest.sh' in the test target. +# Options are set to exit when a recipe line exits non-zero or a piped command fails. +SHELL = /usr/bin/env bash -o pipefail +.SHELLFLAGS = -ec + +##@ General + +# The help target prints out all targets with their descriptions organized +# beneath their categories. The categories are represented by '##@' and the +# target descriptions by '##'. The awk commands is responsible for reading the +# entire set of makefiles included in this invocation, looking for lines of the +# file as xyz: ## something, and then pretty-format the target and help. Then, +# if there's a line with ##@ something, that gets pretty-printed as a category. +# More info on the usage of ANSI control characters for terminal formatting: +# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters +# More info on the awk command: +# http://linuxcommand.org/lc3_adv_awk.php + +.PHONY: help +help: ## Display this help. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + + +.PHONY: unit_clean +unit_clean: ## clean up the unit test artifacts created +ifeq ($(CONTAINER_RUNNABLE), 0) + $(CONTAINER_RUNTIME) system prune -f +endif + rm ${TEST_COVERAGE_FILE} ${TEST_COVERAGE_HTML_FILE} ${TEST_COVERAGE_FUNC_FILE} > /dev/null 2>&1 + +.PHONY: unit +unit: ## Run unit tests against code. +ifeq ($(CONTAINER_RUNNABLE), 0) + $(CONTAINER_RUNTIME) run -it -v ${PWD}:/go/src -w /go/src docker.io/library/golang:${GO_VERSION}-alpine3.17 \ + /bin/sh -c "go test ./... -v -coverprofile ${TEST_COVERAGE_FILE}; \ + go tool cover -html=${TEST_COVERAGE_FILE} -o ${TEST_COVERAGE_HTML_FILE}; \ + go tool cover -func=${TEST_COVERAGE_FILE} -o ${TEST_COVERAGE_FUNC_FILE}" +else + go test ./... -v -coverprofile ${TEST_COVERAGE_FILE} + go tool cover -html=${TEST_COVERAGE_FILE} -o ${TEST_COVERAGE_HTML_FILE} + go tool cover -func=${TEST_COVERAGE_FILE} -o ${TEST_COVERAGE_FUNC_FILE} +endif + +# Install link at https://golangci-lint.run/usage/install/ if not running inside a container +.PHONY: lint +lint: ## Run lint against code. +ifeq ($(CONTAINER_RUNNABLE), 0) + $(CONTAINER_RUNTIME) run -it -v ${PWD}:/go/src -w /go/src docker.io/golangci/golangci-lint:${GOLANG_CI_VER}-alpine golangci-lint run ./... -v +else + golangci-lint run ./... -v --timeout 10m +endif + +# Install link at https://github.com/securego/gosec#install if not running inside a container +.PHONY: gosec +gosec: ## inspects source code for security problem by scanning the Go Abstract Syntax Tree +ifeq ($(CONTAINER_RUNNABLE), 0) + $(CONTAINER_RUNTIME) run -it -v ${PWD}:/go/src -w /go/src docker.io/securego/gosec:${GOSEC_VER} ./... +else + gosec ./... +endif From f22891706a818dfc4f67764aabd1e8e75fae9044 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Wed, 1 Nov 2023 20:12:20 +0530 Subject: [PATCH 06/19] Adding More Unit Tests (Helm-Based) --- Makefile | 15 ++-- common/helm_to_yaml.go | 5 +- common/helm_to_yaml_test.go | 15 ++++ common/runtime_to_json_test.go | 26 ++++++- common/string_to_gofile_test.go | 21 ++++++ .../test-helmCharts/hello-world/.helmignore | 23 ++++++ .../test-helmCharts/hello-world/Chart.yaml | 24 +++++++ .../test-helmCharts/hello-world/README.md | 1 + .../hello-world/templates/NOTES.txt | 16 +++++ .../hello-world/templates/_helpers.tpl | 62 ++++++++++++++++ .../hello-world/templates/deployment.yaml | 33 +++++++++ .../hello-world/templates/service.yaml | 15 ++++ .../hello-world/templates/serviceaccount.yaml | 12 ++++ .../hello-world/templates/third-party-cr.yaml | 11 +++ .../test-helmCharts/hello-world/values.yaml | 27 +++++++ common/util_test.go | 16 +++-- main_test.go | 71 +++++++++++++++++-- to-do.txt | 7 -- 18 files changed, 377 insertions(+), 23 deletions(-) create mode 100644 common/helm_to_yaml_test.go create mode 100644 common/tests/test-helmCharts/hello-world/.helmignore create mode 100644 common/tests/test-helmCharts/hello-world/Chart.yaml create mode 100644 common/tests/test-helmCharts/hello-world/README.md create mode 100644 common/tests/test-helmCharts/hello-world/templates/NOTES.txt create mode 100644 common/tests/test-helmCharts/hello-world/templates/_helpers.tpl create mode 100644 common/tests/test-helmCharts/hello-world/templates/deployment.yaml create mode 100644 common/tests/test-helmCharts/hello-world/templates/service.yaml create mode 100644 common/tests/test-helmCharts/hello-world/templates/serviceaccount.yaml create mode 100644 common/tests/test-helmCharts/hello-world/templates/third-party-cr.yaml create mode 100644 common/tests/test-helmCharts/hello-world/values.yaml diff --git a/Makefile b/Makefile index 2bfbc7c..93f45c4 100644 --- a/Makefile +++ b/Makefile @@ -50,15 +50,20 @@ unit_clean: ## clean up the unit test artifacts created ifeq ($(CONTAINER_RUNNABLE), 0) $(CONTAINER_RUNTIME) system prune -f endif - rm ${TEST_COVERAGE_FILE} ${TEST_COVERAGE_HTML_FILE} ${TEST_COVERAGE_FUNC_FILE} > /dev/null 2>&1 + # rm ${TEST_COVERAGE_FILE} ${TEST_COVERAGE_HTML_FILE} ${TEST_COVERAGE_FUNC_FILE} > /dev/null 2>&1 + rm -f ${TEST_COVERAGE_FILE} ${TEST_COVERAGE_HTML_FILE} ${TEST_COVERAGE_FUNC_FILE} .PHONY: unit -unit: ## Run unit tests against code. +unit: ## Run unit tests against code. Installing Helm also as a pre-requisite ifeq ($(CONTAINER_RUNNABLE), 0) $(CONTAINER_RUNTIME) run -it -v ${PWD}:/go/src -w /go/src docker.io/library/golang:${GO_VERSION}-alpine3.17 \ - /bin/sh -c "go test ./... -v -coverprofile ${TEST_COVERAGE_FILE}; \ - go tool cover -html=${TEST_COVERAGE_FILE} -o ${TEST_COVERAGE_HTML_FILE}; \ - go tool cover -func=${TEST_COVERAGE_FILE} -o ${TEST_COVERAGE_FUNC_FILE}" + /bin/sh -c "wget https://get.helm.sh/helm-v3.9.3-linux-amd64.tar.gz; \ + tar xvf helm-v3.9.3-linux-amd64.tar.gz; \ + mv linux-amd64/helm /usr/local/bin; \ + rm -rf linux-amd64 helm-v3.9.3-linux-amd64.tar.gz; \ + go test ./... -v -coverprofile ${TEST_COVERAGE_FILE}; \ + go tool cover -html=${TEST_COVERAGE_FILE} -o ${TEST_COVERAGE_HTML_FILE}; \ + go tool cover -func=${TEST_COVERAGE_FILE} -o ${TEST_COVERAGE_FUNC_FILE}" else go test ./... -v -coverprofile ${TEST_COVERAGE_FILE} go tool cover -html=${TEST_COVERAGE_FILE} -o ${TEST_COVERAGE_HTML_FILE} diff --git a/common/helm_to_yaml.go b/common/helm_to_yaml.go index dfaea2c..1afa8fb 100644 --- a/common/helm_to_yaml.go +++ b/common/helm_to_yaml.go @@ -35,9 +35,10 @@ Runs the bash command "helm template --namespace --outpu Todo: Increase the functionality to handle remote helm charts, and support for using different values.yaml & so on */ func (obj *HelmYamlConvertor) ConvertHelmToYaml() error { - logrus.Info(obj.Namespace, obj.Chartpath) + logrus.Info(obj.Namespace, " ", obj.Chartpath) logrus.Info(" ----------------- Converting Helm to Yaml --------------------------") - createDirIfDontExist("temp") + _ = createDirIfDontExist("temp") + // logrus.Info(err) if obj.Namespace == "" { obj.Namespace = "default" } diff --git a/common/helm_to_yaml_test.go b/common/helm_to_yaml_test.go new file mode 100644 index 0000000..79d2290 --- /dev/null +++ b/common/helm_to_yaml_test.go @@ -0,0 +1,15 @@ +package common + +import ( + "os" + "testing" +) + +func TestConvertHelmToYaml(t *testing.T) { + var helmYamlConvertor = HelmYamlConvertor{Namespace: "myns", Chartpath: "tests/test-helmCharts/hello-world/"} + err := helmYamlConvertor.ConvertHelmToYaml() + if err != nil { + t.Errorf("Unable to convert helm-chart to yamls using helm template | Error %v", err) + } + os.RemoveAll("temp") +} diff --git a/common/runtime_to_json_test.go b/common/runtime_to_json_test.go index 4b02724..302581c 100644 --- a/common/runtime_to_json_test.go +++ b/common/runtime_to_json_test.go @@ -23,6 +23,7 @@ import ( "testing" "time" + "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/kubectl/pkg/scheme" @@ -31,7 +32,7 @@ import ( var runtimeJsonConverterObj = RuntimeJsonConverter{} /* -It Tests the Full-Flow From Runtime-Obj to Json +It Tests the Full-Flow From Runtime-Obj to Json then StringConvert And Also Test For Combined-Cases (Struct-in-a-Struct, Slice-in-a-Struct, Struct-in-a-Slice) & so-on */ func TestConvert(t *testing.T) { @@ -60,6 +61,29 @@ func TestConvert(t *testing.T) { if !reflect.DeepEqual(result, expected) { t.Errorf("Result Doesn't Matches with Expected| Kindly Check |\n ResultFile %s \t| ExpectedFile %s\n", resultFile, expectedFile) } + // ----------------- Testing for JSON to GoCode -------------------------- + var jsonStringConverterObj = JsonStringConverter{} + ll, _ := logrus.ParseLevel("fatal") + logrus.SetLevel(ll) + + moduleStructMappingFile := "../config/struct_module_mapping.json" + jsonStringConverterObj.setModStructMapping(moduleStructMappingFile) + if len(jsonStringConverterObj.globalStructMapping) == 0 { + t.Errorf("Intialise Failed| Unable to Populate Global-Struct-Mapping From Config-File %s", moduleStructMappingFile) + } + + enumModuleMapping := "../config/enum_module_mapping.json" + jsonStringConverterObj.setEnums(enumModuleMapping) + if jsonStringConverterObj.globalEnumsSet.Size() == 0 { + t.Errorf("Intialise Failed| Unable to Populate Enum-Module-Mapping From Config-File %s", enumModuleMapping) + } + gocode, err := jsonStringConverterObj.Convert(*gvk) + if err != nil { + t.Errorf("Error encountered while converting json to gocode | Error %v", err) + } + if gocode == "" { + t.Error("Empty Go-Code returned While Converting json to gocode-string") + } os.RemoveAll("temp") } diff --git a/common/string_to_gofile_test.go b/common/string_to_gofile_test.go index 71bbcdc..f7bc0d4 100644 --- a/common/string_to_gofile_test.go +++ b/common/string_to_gofile_test.go @@ -17,6 +17,7 @@ limitations under the License. package common import ( + "os" "strings" "testing" @@ -116,6 +117,7 @@ func (r *YourKindReconciler)DeleteAll(){ } func TestGenerate(t *testing.T) { + goFileObj.FileContent = "" input := map[string][]string{ "Deployment": {"appsv1.Deployment{struct_attributes...}"}, } @@ -124,3 +126,22 @@ func TestGenerate(t *testing.T) { t.Errorf("Generate GoCode Failed| Unable to Generate Go-File from Go-Code") } } + +func TestGenerateWithEmptyNamespace(t *testing.T) { + goFileObj.FileContent = "" + goFileObj.Namespace = "" + input := map[string][]string{ + "Deployment": {"appsv1.Deployment{struct_attributes...}"}, + } + goFileObj.Generate(input) + if goFileObj.FileContent == "" { + t.Errorf("Generate GoCode Failed| Unable to Generate Go-File from Go-Code") + } +} +func TestWriteToFile(t *testing.T) { + goFileObj.WriteToFile() + if _, err := os.Stat("outputs/generated_code.go"); err != nil { + t.Errorf("Generated_code.go File doesn't exist| Failing this test") + } + os.RemoveAll("outputs") +} diff --git a/common/tests/test-helmCharts/hello-world/.helmignore b/common/tests/test-helmCharts/hello-world/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/common/tests/test-helmCharts/hello-world/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/common/tests/test-helmCharts/hello-world/Chart.yaml b/common/tests/test-helmCharts/hello-world/Chart.yaml new file mode 100644 index 0000000..576f5e9 --- /dev/null +++ b/common/tests/test-helmCharts/hello-world/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: hello-world +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/common/tests/test-helmCharts/hello-world/README.md b/common/tests/test-helmCharts/hello-world/README.md new file mode 100644 index 0000000..2965834 --- /dev/null +++ b/common/tests/test-helmCharts/hello-world/README.md @@ -0,0 +1 @@ +# Hello World diff --git a/common/tests/test-helmCharts/hello-world/templates/NOTES.txt b/common/tests/test-helmCharts/hello-world/templates/NOTES.txt new file mode 100644 index 0000000..3aeaf1b --- /dev/null +++ b/common/tests/test-helmCharts/hello-world/templates/NOTES.txt @@ -0,0 +1,16 @@ +1. Get the application URL by running these commands: +{{- if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "hello-world.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "hello-world.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "hello-world.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "hello-world.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/common/tests/test-helmCharts/hello-world/templates/_helpers.tpl b/common/tests/test-helmCharts/hello-world/templates/_helpers.tpl new file mode 100644 index 0000000..8ce40d0 --- /dev/null +++ b/common/tests/test-helmCharts/hello-world/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "hello-world.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "hello-world.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "hello-world.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "hello-world.labels" -}} +helm.sh/chart: {{ include "hello-world.chart" . }} +{{ include "hello-world.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "hello-world.selectorLabels" -}} +app.kubernetes.io/name: {{ include "hello-world.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "hello-world.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "hello-world.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/common/tests/test-helmCharts/hello-world/templates/deployment.yaml b/common/tests/test-helmCharts/hello-world/templates/deployment.yaml new file mode 100644 index 0000000..bf9e042 --- /dev/null +++ b/common/tests/test-helmCharts/hello-world/templates/deployment.yaml @@ -0,0 +1,33 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "hello-world.fullname" . }} + labels: + {{- include "hello-world.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "hello-world.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "hello-world.selectorLabels" . | nindent 8 }} + spec: + serviceAccountName: {{ include "hello-world.serviceAccountName" . }} + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: 80 + protocol: TCP + livenessProbe: + httpGet: + path: / + port: http + readinessProbe: + httpGet: + path: / + port: http diff --git a/common/tests/test-helmCharts/hello-world/templates/service.yaml b/common/tests/test-helmCharts/hello-world/templates/service.yaml new file mode 100644 index 0000000..e6da84e --- /dev/null +++ b/common/tests/test-helmCharts/hello-world/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "hello-world.fullname" . }} + labels: + {{- include "hello-world.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "hello-world.selectorLabels" . | nindent 4 }} diff --git a/common/tests/test-helmCharts/hello-world/templates/serviceaccount.yaml b/common/tests/test-helmCharts/hello-world/templates/serviceaccount.yaml new file mode 100644 index 0000000..886099a --- /dev/null +++ b/common/tests/test-helmCharts/hello-world/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "hello-world.serviceAccountName" . }} + labels: + {{- include "hello-world.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/common/tests/test-helmCharts/hello-world/templates/third-party-cr.yaml b/common/tests/test-helmCharts/hello-world/templates/third-party-cr.yaml new file mode 100644 index 0000000..5465b49 --- /dev/null +++ b/common/tests/test-helmCharts/hello-world/templates/third-party-cr.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ThirdPartyCR +metadata: + name: my-cr + namespace: default +spec: + abc: def + slice: + - name: bde + - name: hello + - name: 5 \ No newline at end of file diff --git a/common/tests/test-helmCharts/hello-world/values.yaml b/common/tests/test-helmCharts/hello-world/values.yaml new file mode 100644 index 0000000..a85d5a9 --- /dev/null +++ b/common/tests/test-helmCharts/hello-world/values.yaml @@ -0,0 +1,27 @@ +# Default values for hello-world. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: nginx + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +service: + type: ClusterIP + port: 80 diff --git a/common/util_test.go b/common/util_test.go index 1ddf016..8529ede 100644 --- a/common/util_test.go +++ b/common/util_test.go @@ -18,7 +18,6 @@ package common import ( "os" - "reflect" "testing" ) @@ -54,9 +53,18 @@ func TestCreateDirIfDontExist(t *testing.T) { func TestRecursiveListYamls(t *testing.T) { result := RecursiveListYamls("tests") - expected := []string{"tests/test-yamls/deployment.yaml", "tests/test-yamls/third-party-cr.yaml"} - if !reflect.DeepEqual(result, expected) { - t.Errorf("Util-tests | 'RecursiveListYamls' test failed | \n Expected %v \n Got %v", expected, result) + // testHelmChartFolder := "tests/test-helmCharts/hello-world/" + // expected := []string{"tests/test-yamls/deployment.yaml", "tests/test-yamls/third-party-cr.yaml", + // testHelmChartFolder + "Chart.yaml", testHelmChartFolder + "values.yaml", + // testHelmChartFolder + "templates/deployment.yaml", testHelmChartFolder + "templates/service.yaml", + // testHelmChartFolder + "templates/serviceaccount.yaml", testHelmChartFolder + "templates/third-party-cr.yaml", + // } + + // if !reflect.DeepEqual(result, expected) { + // t.Errorf("Util-tests | 'RecursiveListYamls' test failed | \n Expected %v \n Got %v", expected, result) + // } + if len(result) != 8 { + t.Errorf("Util-tests | 'RecursiveListYamls' test failed | \n Expected Length %v \n Got %v", 8, result) } } diff --git a/main_test.go b/main_test.go index bf7f084..669f427 100644 --- a/main_test.go +++ b/main_test.go @@ -17,17 +17,52 @@ limitations under the License. package main import ( + "bufio" "fmt" + "os" + "os/exec" "testing" + + "github.com/sirupsen/logrus" ) +func setLogLevelFatal() { + ll, err := logrus.ParseLevel("fatal") + if err != nil { + ll = logrus.DebugLevel + } + logrus.SetLevel(ll) +} + +func checkIfHelmInstalled() error { + cmdStruct := exec.Command("helm", "version") + stderr, _ := cmdStruct.StderrPipe() // Intialising a Pipe to read error stream + if err := cmdStruct.Start(); err != nil { + fmt.Println("Unable to Start Cmd Pipe to check Helm-Install") + return err + } + + scanner := bufio.NewScanner(stderr) + helmCmdErr := "" + for scanner.Scan() { + helmCmdErr += scanner.Text() + } + if len(helmCmdErr) > 0 { + fmt.Println("Error while checking the Helm Version|", helmCmdErr) + return fmt.Errorf(helmCmdErr) + } + // fmt.Println("helm is installed") + return nil +} + /* Tests for Runtime-OBject Way of Handling KRM-Object */ func TestHandleSingleYamlDeployment(t *testing.T) { + setLogLevelFatal() inputFilePath := "common/tests/test-yamls/deployment.yaml" - runtimeObjList, gvkList, unstructObjList, unstructGvkList := handleSingleYaml(inputFilePath) - fmt.Println(runtimeObjList, gvkList, unstructObjList, unstructGvkList) + runtimeObjList, gvkList, _, _ := handleSingleYaml(inputFilePath) + // fmt.Println(runtimeObjList, gvkList, unstructObjList, unstructGvkList) if len(runtimeObjList) == 0 { t.Errorf("Unable to convert yaml to RuntimeObject") } @@ -40,9 +75,10 @@ func TestHandleSingleYamlDeployment(t *testing.T) { Tests for Unstructured Way of Handling KRM-Object */ func TestHandleSingleYamlCR(t *testing.T) { + setLogLevelFatal() inputFilePath := "common/tests/test-yamls/third-party-cr.yaml" - runtimeObjList, gvkList, unstructObjList, unstructGvkList := handleSingleYaml(inputFilePath) - fmt.Println(runtimeObjList, gvkList, unstructObjList, unstructGvkList) + _, _, unstructObjList, unstructGvkList := handleSingleYaml(inputFilePath) + // fmt.Println(runtimeObjList, gvkList, unstructObjList, unstructGvkList) if len(unstructObjList) == 0 { t.Errorf("Unable to convert yaml to RuntimeObject") } @@ -50,3 +86,30 @@ func TestHandleSingleYamlCR(t *testing.T) { t.Errorf("Kind Detected is not what expected | Detected %s | Expected ThirdPartyCR", unstructGvkList[0].Kind) } } + +func TestMainFunc(t *testing.T) { + setLogLevelFatal() + err := checkIfHelmInstalled() + if err != nil { + fmt.Println("Helm Not Installed Detected| Aborting the current test") + return + } + saveCmdArgs := os.Args + os.Args = []string{"main.go", "common/tests/test-helmCharts/hello-world/", "abc"} + os.Args = append(os.Args, saveCmdArgs...) + main() + os.Args = saveCmdArgs + // According to the temp directory should have been deleted, If it is not Then The flow has encountered as error + if _, err := os.Stat("temp/"); err == nil { + _ = os.RemoveAll("temp") + _ = os.Remove("outputs/generated_code.go") + t.Errorf("Temp Directory still exists| Manually deleting | Failing this test") + } + + // The Test is Considered PASSED if generated_code.go is created in the outputs/ folder + if _, err := os.Stat("outputs/generated_code.go"); err != nil { + t.Errorf("Generated_code.go File doesn't exist| Failing this test") + } + _ = os.Remove("outputs/generated_code.go") + +} diff --git a/to-do.txt b/to-do.txt index 857c19d..e9f5773 100644 --- a/to-do.txt +++ b/to-do.txt @@ -25,10 +25,3 @@ limitations under the License. 4: To check if converting from helm to yaml by helm-go client is feasible or not, since it will eliminate Helm as prerequiste for the script 5: Extending The Support For Enum-pointers (Example: PreemptionPolicy attribute in schedulingv1.PriorityClass) - ----------- TMP Branch Text --------------------- -It should only come in new branch! -Object Oriented Design: -Global Variables and Variables that were getting passing from one fxn to another are made private attributes of struct -Attributes that can be set by user are made Public -Every Common Package File (except util.go), represents a struct (class) with its own methods From 3ac1cf7e758dda838312010aa15367d6dce73819 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Wed, 1 Nov 2023 21:17:09 +0530 Subject: [PATCH 07/19] Resolving Linter and Gosec Issues --- common/helm_to_yaml.go | 4 ++-- common/json_to_string.go | 17 +++++++++-------- common/runtime_to_json.go | 4 ++-- common/runtime_to_json_test.go | 10 ++++++---- common/string_to_gofile.go | 4 ++-- common/test_helper.go | 5 +++-- common/util.go | 2 +- main.go | 13 +++++++------ 8 files changed, 32 insertions(+), 27 deletions(-) diff --git a/common/helm_to_yaml.go b/common/helm_to_yaml.go index 1afa8fb..ed4af43 100644 --- a/common/helm_to_yaml.go +++ b/common/helm_to_yaml.go @@ -42,8 +42,8 @@ func (obj *HelmYamlConvertor) ConvertHelmToYaml() error { if obj.Namespace == "" { obj.Namespace = "default" } - cmdStruct := exec.Command("helm", "template", obj.Chartpath, "--namespace", obj.Namespace, "--output-dir", "temp/templated/") - stderr, _ := cmdStruct.StderrPipe() // Intialising a Pipe to read error stream + cmdStruct := exec.Command("helm", "template", obj.Chartpath, "--namespace", obj.Namespace, "--output-dir", "temp/templated/") // #nosec G204 + stderr, _ := cmdStruct.StderrPipe() // Intialising a Pipe to read error stream if err := cmdStruct.Start(); err != nil { logrus.Error(err) return err diff --git a/common/json_to_string.go b/common/json_to_string.go index db3fd26..e022076 100644 --- a/common/json_to_string.go +++ b/common/json_to_string.go @@ -21,6 +21,7 @@ import ( "fmt" "log" "os" + "path/filepath" "reflect" "regexp" "strings" @@ -92,14 +93,14 @@ globalStructMapping Says: Give me any data-type (v1.Deployment) and I will tell This is required, because runtime-Object type if you reflect, will say the datatype as v1.Service or v1.DeploymentSpec, whereas in code, we want corev1.Service or appsv1.DeploymentSpec */ -func (obj *JsonStringConverter) setModStructMapping(filepath string) { +func (obj *JsonStringConverter) setModStructMapping(inputFilepath string) { out := map[string]string{} - plan, _ := os.ReadFile(filepath) + plan, _ := os.ReadFile(filepath.Clean(inputFilepath)) var data map[string]interface{} err := json.Unmarshal(plan, &data) if err != nil { - logrus.Fatal("Cannot unmarshal the json For Struct Mapping | ", filepath, " Error | ", err) + logrus.Fatal("Cannot unmarshal the json For Struct Mapping | ", inputFilepath, " Error | ", err) } // Validating Struct Mapping, Every Mapping value should contain unique values: pending @@ -129,12 +130,12 @@ func (obj *JsonStringConverter) setModStructMapping(filepath string) { Enums are needed to handle differently than structs, therefore the below set tells which data-types are enum (non-composite), So, that it could be handled differently (Used by "formatTypeVal" function) */ -func (obj *JsonStringConverter) setEnums(filepath string) { - fp, _ := os.ReadFile(filepath) +func (obj *JsonStringConverter) setEnums(inputFilepath string) { + fp, _ := os.ReadFile(filepath.Clean(inputFilepath)) var data map[string]interface{} err := json.Unmarshal(fp, &data) if err != nil { - logrus.Fatal("Cannot unmarshal the json For Enum-Mapping | ", filepath, " Error | ", err) + logrus.Fatal("Cannot unmarshal the json For Enum-Mapping | ", inputFilepath, " Error | ", err) } var tempSet = set.New[string](comparator.StringComparator, set.WithGoroutineSafe()) // Saving to Global Enum Set, so that it could be used by "formatTypeVal" function @@ -341,9 +342,9 @@ func (obj *JsonStringConverter) traverseJson(v reflect.Value, curObjType string, /* Reads the temp.json created by runtime_to_json.go and traverse json(DFS Runner) and generates the go-code requried */ -func (obj *JsonStringConverter) jsonToGoCode(filepath string) { +func (obj *JsonStringConverter) jsonToGoCode(inputFilepath string) { - plan, _ := os.ReadFile(filepath) + plan, _ := os.ReadFile(filepath.Clean(inputFilepath)) var data map[string]interface{} err := json.Unmarshal(plan, &data) if err != nil { diff --git a/common/runtime_to_json.go b/common/runtime_to_json.go index 49b472b..5326685 100644 --- a/common/runtime_to_json.go +++ b/common/runtime_to_json.go @@ -265,7 +265,7 @@ func (obj *RuntimeJsonConverter) Convert(runtimeObj runtime.Object, gvk schema.G if jsonErr != nil { return jsonErr } - createDirIfDontExist("temp") - err := os.WriteFile("temp/temp.json", jsonString, 0777) + _ = createDirIfDontExist("temp") + err := os.WriteFile("temp/temp.json", jsonString, 0600) return err } diff --git a/common/runtime_to_json_test.go b/common/runtime_to_json_test.go index 302581c..811e508 100644 --- a/common/runtime_to_json_test.go +++ b/common/runtime_to_json_test.go @@ -47,8 +47,10 @@ func TestConvert(t *testing.T) { if err != nil { t.Errorf("Unable to Decode the Yaml| %s", inputFile) } - runtimeJsonConverterObj.Convert(runtimeObject, *gvk) - + err = runtimeJsonConverterObj.Convert(runtimeObject, *gvk) + if err != nil { + t.Errorf("Unable to Convert Runtime-Obj to JSON | Error %v", err) + } resultFile := "temp/temp.json" expectedFile := "tests/expected-json/deployment.json" resultData, _ := getFileContents(resultFile) @@ -56,8 +58,8 @@ func TestConvert(t *testing.T) { var result interface{} var expected interface{} - json.Unmarshal([]byte(resultData), &result) - json.Unmarshal([]byte(expectedData), &expected) + _ = json.Unmarshal([]byte(resultData), &result) + _ = json.Unmarshal([]byte(expectedData), &expected) if !reflect.DeepEqual(result, expected) { t.Errorf("Result Doesn't Matches with Expected| Kindly Check |\n ResultFile %s \t| ExpectedFile %s\n", resultFile, expectedFile) } diff --git a/common/string_to_gofile.go b/common/string_to_gofile.go index 41436c3..4e6e42f 100644 --- a/common/string_to_gofile.go +++ b/common/string_to_gofile.go @@ -309,8 +309,8 @@ func (obj *GoFile) Generate(gocodes map[string][]string) { Writes the "output_gocode/generated_code.go" file */ func (obj *GoFile) WriteToFile() { - createDirIfDontExist("outputs") - err := os.WriteFile("outputs/generated_code.go", []byte(obj.FileContent), 0777) + _ = createDirIfDontExist("outputs") + err := os.WriteFile("outputs/generated_code.go", []byte(obj.FileContent), 0600) if err != nil { logrus.Fatal("Writing gocode to outputs/generated_code.go FAILED| Error --> | ", err) } diff --git a/common/test_helper.go b/common/test_helper.go index 108a6aa..0c7d6d6 100644 --- a/common/test_helper.go +++ b/common/test_helper.go @@ -20,6 +20,7 @@ import ( "bufio" "io" "os" + "path/filepath" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" @@ -34,8 +35,8 @@ type Tests struct { /* Reads the File from FilePath and returns the file-data */ -func getFileContents(filePath string) ([]byte, error) { - file, err := os.Open(filePath) +func getFileContents(inputFilePath string) ([]byte, error) { + file, err := os.Open(filepath.Clean(inputFilePath)) if err != nil { return nil, err } diff --git a/common/util.go b/common/util.go index 5303fab..8449bc6 100644 --- a/common/util.go +++ b/common/util.go @@ -40,7 +40,7 @@ Creates the directory if it doesn't exist intially */ func createDirIfDontExist(path string) error { if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { - err := os.Mkdir(path, 0777) + err := os.Mkdir(path, 0750) if err != nil { log.Println(err) return err diff --git a/main.go b/main.go index 010545a..d1a85e5 100644 --- a/main.go +++ b/main.go @@ -22,6 +22,7 @@ import ( "helm_to_controller/packages/common" "io" "os" + "path/filepath" "strings" // logging "github.com/op/go-logging" @@ -72,16 +73,16 @@ Output: unstructObjList: List of unstructured Objects Converted from the input yaml, whose Kind are not default to kubernetes| Third Party Kinds unstructGvkList: List of Group-Version-Kind for the unstructured objects of unstructObjList, mapped Index-wise */ -func handleSingleYaml(filepath string) (runtimeObjList []runtime.Object, gvkList []schema.GroupVersionKind, unstructObjList []unstructured.Unstructured, unstructGvkList []schema.GroupVersionKind) { - file, err := os.Open(filepath) +func handleSingleYaml(inputFilepath string) (runtimeObjList []runtime.Object, gvkList []schema.GroupVersionKind, unstructObjList []unstructured.Unstructured, unstructGvkList []schema.GroupVersionKind) { + file, err := os.Open(filepath.Clean(inputFilepath)) if err != nil { - logrus.Error("Error While Opening YAML file | ", filepath, " \t |", err) + logrus.Error("Error While Opening YAML file | ", inputFilepath, " \t |", err) return } fp := bufio.NewReader(file) data, err := io.ReadAll(fp) if err != nil { - logrus.Error("Error While Reading YAML file | ", filepath, " \t |", err) + logrus.Error("Error While Reading YAML file | ", inputFilepath, " \t |", err) return } // A Single yaml can contain muliple KRM reosurces, separated by ---, Therefore Spliting the yaml-file-content over "---" to get single KRM Resource @@ -117,8 +118,8 @@ func handleSingleYaml(filepath string) (runtimeObjList []runtime.Object, gvkList func init() { // Setting the Logrus Logging Level - lvl := "debug" - lvl = "info" + // lvl := "debug" + lvl := "info" // lvl = "error" ll, err := logrus.ParseLevel(lvl) if err != nil { From eb20d60ffeeba655a944b29481e6ab124db1b0d1 Mon Sep 17 00:00:00 2001 From: jain-ashish-sam <145763342+jain-ashish-sam@users.noreply.github.com> Date: Thu, 2 Nov 2023 02:52:08 +0530 Subject: [PATCH 08/19] Updating README.md with Design-Docs Links --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0790820..dcfae7b 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ sudo mv linux-amd64/helm /usr/local/bin 3. Go Packages: ``` -git clone https://github.sec.samsung.net/s-jaisawal/helm-operatort-sdk.git -cd helm-operatort-sdk/ +# Clone the Repo +cd nephio-sdk/ go mod tidy ``` @@ -46,7 +46,12 @@ Note: The generated Go-Code would be written in the "outputs/generated_code.go" file The Generated Go-Code would contain the following plugable function: -1. Create_All(): It when called, create all the k8s resources(services, deployment) in the kubernetes cluster. -2. Get_Resources(): would return the list of a particular resource. +1. Create_All(): It when called, create all the k8s resources(services, deployment) in the kubernetes cluster. +2. Delete_All(): It when called, delete all the k8s resources(services, deployment) in the kubernetes cluster. +3. Get_Resources(): would return the list of a particular resource. 1. Get_Service() would return the list of all Services-Objects. 2. Get_Deployment() would return the list of all Deployment-Objects. & so on + +Further Docs: +1. Design Document: [link](https://docs.google.com/document/d/1b7WpK_BHe7nRuGP5MOy6Mxf3hpN_cro9/edit) +2. Detailed Algorithm: [link](https://1drv.ms/p/s!AkgeY1fT2A5UhQK4IWBxOJ6YUerh?e=BmBkRc) From 2370479db69f0a48331db8012773f89221796bac Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Thu, 2 Nov 2023 11:19:45 +0530 Subject: [PATCH 09/19] Adding Prow and whitelist.json --- .prow.yaml | 71 +++++++++++++++++++ common/helm_to_yaml_test.go | 16 +++++ .../hello-world/templates/NOTES.txt | 16 +++++ .../hello-world/templates/deployment.yaml | 14 ++++ .../hello-world/templates/service.yaml | 14 ++++ .../hello-world/templates/serviceaccount.yaml | 14 ++++ .../hello-world/templates/third-party-cr.yaml | 14 ++++ .../test-helmCharts/hello-world/values.yaml | 17 ++++- common/tests/test-yamls/deployment.yaml | 14 ++++ common/tests/test-yamls/third-party-cr.yaml | 14 ++++ whitelist.json | 13 ++++ 11 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 .prow.yaml create mode 100644 whitelist.json diff --git a/.prow.yaml b/.prow.yaml new file mode 100644 index 0000000..66165b1 --- /dev/null +++ b/.prow.yaml @@ -0,0 +1,71 @@ +presubmits: + - name: presubmit-api-go-test-sdk + decorate: true + run_if_changed: "^.*.go$" + spec: + containers: + - image: nephio/gotests:7 + command: + - make + args: + - unit + - name: presubmit-api-gosec-sdk + decorate: true + run_if_changed: "^.*.go$" + spec: + containers: + - image: nephio/gotests:7 + command: + - make + args: + - gosec + - name: presubmit-api-golangci-lint-sdk + decorate: true + run_if_changed: "^.*.go$" + spec: + containers: + - image: nephio/gotests:7 + command: + - make + args: + - lint + - name: presubmit-api-license-header-check-sdk + decorate: true + run_if_changed: "^.*.go$" + spec: + containers: + - image: nephio/gotests:7 + command: + - "/bin/sh" + - "-c" + - | + /usr/local/bin/checklicense.sh > ${ARTIFACTS}/license_headers_results.txt + - name: presubmit-api-scancode-toolkit-sdk + decorate: true + always_run: true + spec: + containers: + - image: nephio/scancode-toolkit:v31.2.5 + command: + - "/bin/sh" + args: + - "-c" + - | + /scancode-toolkit/scancode --ignore "whitelist.json" -clpeui -n 2 --html ${ARTIFACTS}/scancode_report.html . --tallies-with-details + resources: + requests: + cpu: 2 + memory: 1Gi + - name: presubmit-api-fossology-sdk + decorate: true + always_run: true + spec: + containers: + - image: fossology/fossology:scanner + command: + - "/bin/bash" + args: + - "-c" + - | + /bin/fossologyscanner --report SPDX_JSON repo nomos ojo copyright keyword + cp -R results ${ARTIFACTS}/ \ No newline at end of file diff --git a/common/helm_to_yaml_test.go b/common/helm_to_yaml_test.go index 79d2290..06786ac 100644 --- a/common/helm_to_yaml_test.go +++ b/common/helm_to_yaml_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package common import ( diff --git a/common/tests/test-helmCharts/hello-world/templates/NOTES.txt b/common/tests/test-helmCharts/hello-world/templates/NOTES.txt index 3aeaf1b..8a505c8 100644 --- a/common/tests/test-helmCharts/hello-world/templates/NOTES.txt +++ b/common/tests/test-helmCharts/hello-world/templates/NOTES.txt @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + 1. Get the application URL by running these commands: {{- if contains "NodePort" .Values.service.type }} export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "hello-world.fullname" . }}) diff --git a/common/tests/test-helmCharts/hello-world/templates/deployment.yaml b/common/tests/test-helmCharts/hello-world/templates/deployment.yaml index bf9e042..f4be693 100644 --- a/common/tests/test-helmCharts/hello-world/templates/deployment.yaml +++ b/common/tests/test-helmCharts/hello-world/templates/deployment.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 The Nephio Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/common/tests/test-helmCharts/hello-world/templates/service.yaml b/common/tests/test-helmCharts/hello-world/templates/service.yaml index e6da84e..ee7ce84 100644 --- a/common/tests/test-helmCharts/hello-world/templates/service.yaml +++ b/common/tests/test-helmCharts/hello-world/templates/service.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 The Nephio Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + apiVersion: v1 kind: Service metadata: diff --git a/common/tests/test-helmCharts/hello-world/templates/serviceaccount.yaml b/common/tests/test-helmCharts/hello-world/templates/serviceaccount.yaml index 886099a..20bb3bc 100644 --- a/common/tests/test-helmCharts/hello-world/templates/serviceaccount.yaml +++ b/common/tests/test-helmCharts/hello-world/templates/serviceaccount.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 The Nephio Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + {{- if .Values.serviceAccount.create -}} apiVersion: v1 kind: ServiceAccount diff --git a/common/tests/test-helmCharts/hello-world/templates/third-party-cr.yaml b/common/tests/test-helmCharts/hello-world/templates/third-party-cr.yaml index 5465b49..51e11d6 100644 --- a/common/tests/test-helmCharts/hello-world/templates/third-party-cr.yaml +++ b/common/tests/test-helmCharts/hello-world/templates/third-party-cr.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 The Nephio Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + apiVersion: v1 kind: ThirdPartyCR metadata: diff --git a/common/tests/test-helmCharts/hello-world/values.yaml b/common/tests/test-helmCharts/hello-world/values.yaml index a85d5a9..8e492ea 100644 --- a/common/tests/test-helmCharts/hello-world/values.yaml +++ b/common/tests/test-helmCharts/hello-world/values.yaml @@ -1,8 +1,23 @@ +# Copyright 2023 The Nephio Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + # Default values for hello-world. # This is a YAML-formatted file. # Declare variables to be passed into your templates. -replicaCount: 1 +replicaCount: 3 image: repository: nginx diff --git a/common/tests/test-yamls/deployment.yaml b/common/tests/test-yamls/deployment.yaml index 0b17598..02ee279 100644 --- a/common/tests/test-yamls/deployment.yaml +++ b/common/tests/test-yamls/deployment.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 The Nephio Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/common/tests/test-yamls/third-party-cr.yaml b/common/tests/test-yamls/third-party-cr.yaml index 5465b49..51e11d6 100644 --- a/common/tests/test-yamls/third-party-cr.yaml +++ b/common/tests/test-yamls/third-party-cr.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 The Nephio Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + apiVersion: v1 kind: ThirdPartyCR metadata: diff --git a/whitelist.json b/whitelist.json new file mode 100644 index 0000000..622da07 --- /dev/null +++ b/whitelist.json @@ -0,0 +1,13 @@ +{ + "licenses": [ + "Apache-2.0", + "MIT", + "BSD-3-Clause", + "ISC" + ], + "exclude": [ + ".git/*", + ".prow.yaml", + "*.json" + ] +} From 80c4d276b059ffe7427b33f3d10ea48618979790 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Thu, 11 Jan 2024 12:03:49 +0530 Subject: [PATCH 10/19] Moving the code to sub-dir --- .gitignore => helm-to-operator-codegen-sdk/.gitignore | 0 .prow.yaml => helm-to-operator-codegen-sdk/.prow.yaml | 0 LICENSE => helm-to-operator-codegen-sdk/LICENSE | 0 Makefile => helm-to-operator-codegen-sdk/Makefile | 0 README.md => helm-to-operator-codegen-sdk/README.md | 0 {common => helm-to-operator-codegen-sdk/common}/README.md | 0 {common => helm-to-operator-codegen-sdk/common}/helm_to_yaml.go | 0 .../common}/helm_to_yaml_test.go | 0 {common => helm-to-operator-codegen-sdk/common}/json_to_string.go | 0 .../common}/json_to_string_test.go | 0 .../common}/runtime_to_json.go | 0 .../common}/runtime_to_json_test.go | 0 .../common}/string_to_gofile.go | 0 .../common}/string_to_gofile_test.go | 0 {common => helm-to-operator-codegen-sdk/common}/test_helper.go | 0 .../common}/tests/expected-json/deployment.json | 0 .../common}/tests/test-helmCharts/hello-world/.helmignore | 0 .../common}/tests/test-helmCharts/hello-world/Chart.yaml | 0 .../common}/tests/test-helmCharts/hello-world/README.md | 0 .../common}/tests/test-helmCharts/hello-world/templates/NOTES.txt | 0 .../tests/test-helmCharts/hello-world/templates/_helpers.tpl | 0 .../tests/test-helmCharts/hello-world/templates/deployment.yaml | 0 .../tests/test-helmCharts/hello-world/templates/service.yaml | 0 .../test-helmCharts/hello-world/templates/serviceaccount.yaml | 0 .../test-helmCharts/hello-world/templates/third-party-cr.yaml | 0 .../common}/tests/test-helmCharts/hello-world/values.yaml | 0 .../common}/tests/test-yamls/deployment.yaml | 0 .../common}/tests/test-yamls/third-party-cr.yaml | 0 .../common}/unstruct_to_string.go | 0 .../common}/unstruct_to_string_test.go | 0 {common => helm-to-operator-codegen-sdk/common}/util.go | 0 {common => helm-to-operator-codegen-sdk/common}/util_test.go | 0 {config => helm-to-operator-codegen-sdk/config}/README.md | 0 .../config}/enum_module_mapping.json | 0 .../config}/struct_module_mapping.json | 0 go.mod => helm-to-operator-codegen-sdk/go.mod | 0 go.sum => helm-to-operator-codegen-sdk/go.sum | 0 {inputs => helm-to-operator-codegen-sdk/inputs}/README.md | 0 main.go => helm-to-operator-codegen-sdk/main.go | 0 main_test.go => helm-to-operator-codegen-sdk/main_test.go | 0 {outputs => helm-to-operator-codegen-sdk/outputs}/README.md | 0 to-do.txt => helm-to-operator-codegen-sdk/to-do.txt | 0 whitelist.json => helm-to-operator-codegen-sdk/whitelist.json | 0 43 files changed, 0 insertions(+), 0 deletions(-) rename .gitignore => helm-to-operator-codegen-sdk/.gitignore (100%) rename .prow.yaml => helm-to-operator-codegen-sdk/.prow.yaml (100%) rename LICENSE => helm-to-operator-codegen-sdk/LICENSE (100%) rename Makefile => helm-to-operator-codegen-sdk/Makefile (100%) rename README.md => helm-to-operator-codegen-sdk/README.md (100%) rename {common => helm-to-operator-codegen-sdk/common}/README.md (100%) rename {common => helm-to-operator-codegen-sdk/common}/helm_to_yaml.go (100%) rename {common => helm-to-operator-codegen-sdk/common}/helm_to_yaml_test.go (100%) rename {common => helm-to-operator-codegen-sdk/common}/json_to_string.go (100%) rename {common => helm-to-operator-codegen-sdk/common}/json_to_string_test.go (100%) rename {common => helm-to-operator-codegen-sdk/common}/runtime_to_json.go (100%) rename {common => helm-to-operator-codegen-sdk/common}/runtime_to_json_test.go (100%) rename {common => helm-to-operator-codegen-sdk/common}/string_to_gofile.go (100%) rename {common => helm-to-operator-codegen-sdk/common}/string_to_gofile_test.go (100%) rename {common => helm-to-operator-codegen-sdk/common}/test_helper.go (100%) rename {common => helm-to-operator-codegen-sdk/common}/tests/expected-json/deployment.json (100%) rename {common => helm-to-operator-codegen-sdk/common}/tests/test-helmCharts/hello-world/.helmignore (100%) rename {common => helm-to-operator-codegen-sdk/common}/tests/test-helmCharts/hello-world/Chart.yaml (100%) rename {common => helm-to-operator-codegen-sdk/common}/tests/test-helmCharts/hello-world/README.md (100%) rename {common => helm-to-operator-codegen-sdk/common}/tests/test-helmCharts/hello-world/templates/NOTES.txt (100%) rename {common => helm-to-operator-codegen-sdk/common}/tests/test-helmCharts/hello-world/templates/_helpers.tpl (100%) rename {common => helm-to-operator-codegen-sdk/common}/tests/test-helmCharts/hello-world/templates/deployment.yaml (100%) rename {common => helm-to-operator-codegen-sdk/common}/tests/test-helmCharts/hello-world/templates/service.yaml (100%) rename {common => helm-to-operator-codegen-sdk/common}/tests/test-helmCharts/hello-world/templates/serviceaccount.yaml (100%) rename {common => helm-to-operator-codegen-sdk/common}/tests/test-helmCharts/hello-world/templates/third-party-cr.yaml (100%) rename {common => helm-to-operator-codegen-sdk/common}/tests/test-helmCharts/hello-world/values.yaml (100%) rename {common => helm-to-operator-codegen-sdk/common}/tests/test-yamls/deployment.yaml (100%) rename {common => helm-to-operator-codegen-sdk/common}/tests/test-yamls/third-party-cr.yaml (100%) rename {common => helm-to-operator-codegen-sdk/common}/unstruct_to_string.go (100%) rename {common => helm-to-operator-codegen-sdk/common}/unstruct_to_string_test.go (100%) rename {common => helm-to-operator-codegen-sdk/common}/util.go (100%) rename {common => helm-to-operator-codegen-sdk/common}/util_test.go (100%) rename {config => helm-to-operator-codegen-sdk/config}/README.md (100%) rename {config => helm-to-operator-codegen-sdk/config}/enum_module_mapping.json (100%) rename {config => helm-to-operator-codegen-sdk/config}/struct_module_mapping.json (100%) rename go.mod => helm-to-operator-codegen-sdk/go.mod (100%) rename go.sum => helm-to-operator-codegen-sdk/go.sum (100%) rename {inputs => helm-to-operator-codegen-sdk/inputs}/README.md (100%) rename main.go => helm-to-operator-codegen-sdk/main.go (100%) rename main_test.go => helm-to-operator-codegen-sdk/main_test.go (100%) rename {outputs => helm-to-operator-codegen-sdk/outputs}/README.md (100%) rename to-do.txt => helm-to-operator-codegen-sdk/to-do.txt (100%) rename whitelist.json => helm-to-operator-codegen-sdk/whitelist.json (100%) diff --git a/.gitignore b/helm-to-operator-codegen-sdk/.gitignore similarity index 100% rename from .gitignore rename to helm-to-operator-codegen-sdk/.gitignore diff --git a/.prow.yaml b/helm-to-operator-codegen-sdk/.prow.yaml similarity index 100% rename from .prow.yaml rename to helm-to-operator-codegen-sdk/.prow.yaml diff --git a/LICENSE b/helm-to-operator-codegen-sdk/LICENSE similarity index 100% rename from LICENSE rename to helm-to-operator-codegen-sdk/LICENSE diff --git a/Makefile b/helm-to-operator-codegen-sdk/Makefile similarity index 100% rename from Makefile rename to helm-to-operator-codegen-sdk/Makefile diff --git a/README.md b/helm-to-operator-codegen-sdk/README.md similarity index 100% rename from README.md rename to helm-to-operator-codegen-sdk/README.md diff --git a/common/README.md b/helm-to-operator-codegen-sdk/common/README.md similarity index 100% rename from common/README.md rename to helm-to-operator-codegen-sdk/common/README.md diff --git a/common/helm_to_yaml.go b/helm-to-operator-codegen-sdk/common/helm_to_yaml.go similarity index 100% rename from common/helm_to_yaml.go rename to helm-to-operator-codegen-sdk/common/helm_to_yaml.go diff --git a/common/helm_to_yaml_test.go b/helm-to-operator-codegen-sdk/common/helm_to_yaml_test.go similarity index 100% rename from common/helm_to_yaml_test.go rename to helm-to-operator-codegen-sdk/common/helm_to_yaml_test.go diff --git a/common/json_to_string.go b/helm-to-operator-codegen-sdk/common/json_to_string.go similarity index 100% rename from common/json_to_string.go rename to helm-to-operator-codegen-sdk/common/json_to_string.go diff --git a/common/json_to_string_test.go b/helm-to-operator-codegen-sdk/common/json_to_string_test.go similarity index 100% rename from common/json_to_string_test.go rename to helm-to-operator-codegen-sdk/common/json_to_string_test.go diff --git a/common/runtime_to_json.go b/helm-to-operator-codegen-sdk/common/runtime_to_json.go similarity index 100% rename from common/runtime_to_json.go rename to helm-to-operator-codegen-sdk/common/runtime_to_json.go diff --git a/common/runtime_to_json_test.go b/helm-to-operator-codegen-sdk/common/runtime_to_json_test.go similarity index 100% rename from common/runtime_to_json_test.go rename to helm-to-operator-codegen-sdk/common/runtime_to_json_test.go diff --git a/common/string_to_gofile.go b/helm-to-operator-codegen-sdk/common/string_to_gofile.go similarity index 100% rename from common/string_to_gofile.go rename to helm-to-operator-codegen-sdk/common/string_to_gofile.go diff --git a/common/string_to_gofile_test.go b/helm-to-operator-codegen-sdk/common/string_to_gofile_test.go similarity index 100% rename from common/string_to_gofile_test.go rename to helm-to-operator-codegen-sdk/common/string_to_gofile_test.go diff --git a/common/test_helper.go b/helm-to-operator-codegen-sdk/common/test_helper.go similarity index 100% rename from common/test_helper.go rename to helm-to-operator-codegen-sdk/common/test_helper.go diff --git a/common/tests/expected-json/deployment.json b/helm-to-operator-codegen-sdk/common/tests/expected-json/deployment.json similarity index 100% rename from common/tests/expected-json/deployment.json rename to helm-to-operator-codegen-sdk/common/tests/expected-json/deployment.json diff --git a/common/tests/test-helmCharts/hello-world/.helmignore b/helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/.helmignore similarity index 100% rename from common/tests/test-helmCharts/hello-world/.helmignore rename to helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/.helmignore diff --git a/common/tests/test-helmCharts/hello-world/Chart.yaml b/helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/Chart.yaml similarity index 100% rename from common/tests/test-helmCharts/hello-world/Chart.yaml rename to helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/Chart.yaml diff --git a/common/tests/test-helmCharts/hello-world/README.md b/helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/README.md similarity index 100% rename from common/tests/test-helmCharts/hello-world/README.md rename to helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/README.md diff --git a/common/tests/test-helmCharts/hello-world/templates/NOTES.txt b/helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/templates/NOTES.txt similarity index 100% rename from common/tests/test-helmCharts/hello-world/templates/NOTES.txt rename to helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/templates/NOTES.txt diff --git a/common/tests/test-helmCharts/hello-world/templates/_helpers.tpl b/helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/templates/_helpers.tpl similarity index 100% rename from common/tests/test-helmCharts/hello-world/templates/_helpers.tpl rename to helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/templates/_helpers.tpl diff --git a/common/tests/test-helmCharts/hello-world/templates/deployment.yaml b/helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/templates/deployment.yaml similarity index 100% rename from common/tests/test-helmCharts/hello-world/templates/deployment.yaml rename to helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/templates/deployment.yaml diff --git a/common/tests/test-helmCharts/hello-world/templates/service.yaml b/helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/templates/service.yaml similarity index 100% rename from common/tests/test-helmCharts/hello-world/templates/service.yaml rename to helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/templates/service.yaml diff --git a/common/tests/test-helmCharts/hello-world/templates/serviceaccount.yaml b/helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/templates/serviceaccount.yaml similarity index 100% rename from common/tests/test-helmCharts/hello-world/templates/serviceaccount.yaml rename to helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/templates/serviceaccount.yaml diff --git a/common/tests/test-helmCharts/hello-world/templates/third-party-cr.yaml b/helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/templates/third-party-cr.yaml similarity index 100% rename from common/tests/test-helmCharts/hello-world/templates/third-party-cr.yaml rename to helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/templates/third-party-cr.yaml diff --git a/common/tests/test-helmCharts/hello-world/values.yaml b/helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/values.yaml similarity index 100% rename from common/tests/test-helmCharts/hello-world/values.yaml rename to helm-to-operator-codegen-sdk/common/tests/test-helmCharts/hello-world/values.yaml diff --git a/common/tests/test-yamls/deployment.yaml b/helm-to-operator-codegen-sdk/common/tests/test-yamls/deployment.yaml similarity index 100% rename from common/tests/test-yamls/deployment.yaml rename to helm-to-operator-codegen-sdk/common/tests/test-yamls/deployment.yaml diff --git a/common/tests/test-yamls/third-party-cr.yaml b/helm-to-operator-codegen-sdk/common/tests/test-yamls/third-party-cr.yaml similarity index 100% rename from common/tests/test-yamls/third-party-cr.yaml rename to helm-to-operator-codegen-sdk/common/tests/test-yamls/third-party-cr.yaml diff --git a/common/unstruct_to_string.go b/helm-to-operator-codegen-sdk/common/unstruct_to_string.go similarity index 100% rename from common/unstruct_to_string.go rename to helm-to-operator-codegen-sdk/common/unstruct_to_string.go diff --git a/common/unstruct_to_string_test.go b/helm-to-operator-codegen-sdk/common/unstruct_to_string_test.go similarity index 100% rename from common/unstruct_to_string_test.go rename to helm-to-operator-codegen-sdk/common/unstruct_to_string_test.go diff --git a/common/util.go b/helm-to-operator-codegen-sdk/common/util.go similarity index 100% rename from common/util.go rename to helm-to-operator-codegen-sdk/common/util.go diff --git a/common/util_test.go b/helm-to-operator-codegen-sdk/common/util_test.go similarity index 100% rename from common/util_test.go rename to helm-to-operator-codegen-sdk/common/util_test.go diff --git a/config/README.md b/helm-to-operator-codegen-sdk/config/README.md similarity index 100% rename from config/README.md rename to helm-to-operator-codegen-sdk/config/README.md diff --git a/config/enum_module_mapping.json b/helm-to-operator-codegen-sdk/config/enum_module_mapping.json similarity index 100% rename from config/enum_module_mapping.json rename to helm-to-operator-codegen-sdk/config/enum_module_mapping.json diff --git a/config/struct_module_mapping.json b/helm-to-operator-codegen-sdk/config/struct_module_mapping.json similarity index 100% rename from config/struct_module_mapping.json rename to helm-to-operator-codegen-sdk/config/struct_module_mapping.json diff --git a/go.mod b/helm-to-operator-codegen-sdk/go.mod similarity index 100% rename from go.mod rename to helm-to-operator-codegen-sdk/go.mod diff --git a/go.sum b/helm-to-operator-codegen-sdk/go.sum similarity index 100% rename from go.sum rename to helm-to-operator-codegen-sdk/go.sum diff --git a/inputs/README.md b/helm-to-operator-codegen-sdk/inputs/README.md similarity index 100% rename from inputs/README.md rename to helm-to-operator-codegen-sdk/inputs/README.md diff --git a/main.go b/helm-to-operator-codegen-sdk/main.go similarity index 100% rename from main.go rename to helm-to-operator-codegen-sdk/main.go diff --git a/main_test.go b/helm-to-operator-codegen-sdk/main_test.go similarity index 100% rename from main_test.go rename to helm-to-operator-codegen-sdk/main_test.go diff --git a/outputs/README.md b/helm-to-operator-codegen-sdk/outputs/README.md similarity index 100% rename from outputs/README.md rename to helm-to-operator-codegen-sdk/outputs/README.md diff --git a/to-do.txt b/helm-to-operator-codegen-sdk/to-do.txt similarity index 100% rename from to-do.txt rename to helm-to-operator-codegen-sdk/to-do.txt diff --git a/whitelist.json b/helm-to-operator-codegen-sdk/whitelist.json similarity index 100% rename from whitelist.json rename to helm-to-operator-codegen-sdk/whitelist.json From 84040173fd725e4a4ba03d98461582dc0f2eb261 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Fri, 12 Jan 2024 18:01:09 +0530 Subject: [PATCH 11/19] Upgrading go version to 1.21 and Paranthesis & square-brackets functionality addition in checkGoCode --- helm-to-operator-codegen-sdk/.gitignore | 3 +- .../common/json_to_string.go | 45 ++++++++++++------- .../common/json_to_string_test.go | 4 +- helm-to-operator-codegen-sdk/go.mod | 2 +- helm-to-operator-codegen-sdk/go.sum | 6 +++ 5 files changed, 42 insertions(+), 18 deletions(-) diff --git a/helm-to-operator-codegen-sdk/.gitignore b/helm-to-operator-codegen-sdk/.gitignore index 492149d..372bc29 100644 --- a/helm-to-operator-codegen-sdk/.gitignore +++ b/helm-to-operator-codegen-sdk/.gitignore @@ -1,3 +1,4 @@ testing_helpers outputs/generated_code.go -temp \ No newline at end of file +temp +experiments \ No newline at end of file diff --git a/helm-to-operator-codegen-sdk/common/json_to_string.go b/helm-to-operator-codegen-sdk/common/json_to_string.go index e022076..eb6e466 100644 --- a/helm-to-operator-codegen-sdk/common/json_to_string.go +++ b/helm-to-operator-codegen-sdk/common/json_to_string.go @@ -39,37 +39,52 @@ type JsonStringConverter struct { generatedGoCode string // To be set by jsonToGoCode Function } +func getCorrespondingOpening(closingBrackect rune) rune { + switch closingBrackect { + case '}': + return '{' + case ')': + return '(' + case ']': + return '[' + } + logrus.Error("Invalid closingBracket ", closingBrackect) + return ' ' +} + func (obj *JsonStringConverter) checkOpeningAndClosingBraces() bool { - s := stack.New[int]() + s := stack.New[rune]() lineCount := 0 - opCount, closeCount := 0, 0 stringDetected := false - for i := 0; i < len(obj.generatedGoCode); i++ { - if obj.generatedGoCode[i] == '\n' { + for _, c := range obj.generatedGoCode { + if c == '\n' { lineCount++ } - if obj.generatedGoCode[i] == '"' { - // If Enters in double quotes (string) Don't Check For opening/Closing Braces + if c == '"' { + // If Enters in double quotes (string) Don't Check For opening/Closing Braces (because within a string, it's user input which may or may-not be consistent with opening-closing {}) stringDetected = !stringDetected } if !stringDetected { - if obj.generatedGoCode[i] == '{' { - s.Push('{') - opCount++ - } else if obj.generatedGoCode[i] == '}' { - closeCount++ + switch c { + case '{', '(', '[': + s.Push(c) + case '}', ')', ']': + openingBracket := getCorrespondingOpening(c) if s.Empty() { - logrus.Error("Closing Brace has no Opening Brace| Linenumber ", lineCount) + logrus.Errorf("Closing Brace %c has no Opening Brace| Linenumber %d", c, lineCount) return false } else { - s.Pop() + if openingBracket == s.Top() { + s.Pop() + } else { + logrus.Errorf("Invalid arrangement of brackets detected for %c at Linenumber %d", c, lineCount) + return false + } } } } } - - logrus.Debugf("Test Summary| Opening Brace Count %d | Closing Brace Count %d| \n", opCount, closeCount) for !s.Empty() { logrus.Error("Extra Opening Braces Found at the End ") return false diff --git a/helm-to-operator-codegen-sdk/common/json_to_string_test.go b/helm-to-operator-codegen-sdk/common/json_to_string_test.go index cbf45c4..e8236eb 100644 --- a/helm-to-operator-codegen-sdk/common/json_to_string_test.go +++ b/helm-to-operator-codegen-sdk/common/json_to_string_test.go @@ -45,8 +45,10 @@ func TestIntialise(t *testing.T) { func TestCheckOpeningAndClosingBraces(t *testing.T) { tests := []Tests{ - {"{{}}{}", true}, + {"{()}[]", true}, {"{{}}}", false}, + {"{([)}", false}, + {"(", false}, } for _, test := range tests { diff --git a/helm-to-operator-codegen-sdk/go.mod b/helm-to-operator-codegen-sdk/go.mod index bfe61ac..6d219de 100644 --- a/helm-to-operator-codegen-sdk/go.mod +++ b/helm-to-operator-codegen-sdk/go.mod @@ -1,6 +1,6 @@ module helm_to_controller/packages -go 1.19 +go 1.21 require ( github.com/liyue201/gostl v1.2.0 diff --git a/helm-to-operator-codegen-sdk/go.sum b/helm-to-operator-codegen-sdk/go.sum index 2d6f204..f896a11 100644 --- a/helm-to-operator-codegen-sdk/go.sum +++ b/helm-to-operator-codegen-sdk/go.sum @@ -8,6 +8,7 @@ github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -31,13 +32,16 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -74,6 +78,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -81,6 +86,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y= k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg= k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM= From 65e7f59e9d89d7a837167cff800e87a19a86ff11 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Fri, 12 Jan 2024 18:04:10 +0530 Subject: [PATCH 12/19] Unicode Related Issues Fixed & strconv usage for data-type conversions --- .../common/json_to_string.go | 36 +++++++++--------- .../common/json_to_string_test.go | 3 +- .../common/runtime_to_json.go | 37 ++++++++++++------- .../common/string_to_gofile.go | 19 ++++++---- .../common/unstruct_to_string.go | 22 ++++++----- helm-to-operator-codegen-sdk/common/util.go | 7 +--- 6 files changed, 68 insertions(+), 56 deletions(-) diff --git a/helm-to-operator-codegen-sdk/common/json_to_string.go b/helm-to-operator-codegen-sdk/common/json_to_string.go index eb6e466..be8c659 100644 --- a/helm-to-operator-codegen-sdk/common/json_to_string.go +++ b/helm-to-operator-codegen-sdk/common/json_to_string.go @@ -19,11 +19,11 @@ package common import ( "encoding/json" "fmt" - "log" "os" "path/filepath" "reflect" "regexp" + "strconv" "strings" "github.com/liyue201/gostl/ds/set" @@ -177,28 +177,27 @@ Example objType objVal Format_Val(Out) */ func (obj *JsonStringConverter) formatTypeVal(objType string, objVal string, tabCount int) string { // Special Data-Types are Handled Here - if objType == "intstr.Type" { + switch objType { + case "intstr.Type": /*intstr.Type need to be handled explictly: intstr.Type is a int enum*/ objVal = objVal[1 : len(objVal)-1] // Removing the double quotes from the objVal (because we need int) return fmt.Sprintf("%s(%s)", objType, objVal) - } else if objType == "[]uint8" || objType == "[]byte" { + case "[]uint8", "[]byte": // Generally []uint8 is only used for secret return fmt.Sprintf("getDataForSecret(%s)", objVal) } // Special Data-Types area Ends - - pointerType := false - if objType[0] == '*' { - pointerType = true - } else if objType[0] == '&' { - log.Fatal(fmt.Errorf("& Types are not supported yet")) + if strings.HasPrefix(objType, "&") { + logrus.Fatal(fmt.Errorf("& Types are not supported yet")) } + afterObjType, pointerType := strings.CutPrefix(objType, "*") if pointerType { - switch objType[1:] { + switch afterObjType { // You can find the defination of func boolPtr, int32Ptr, int64Ptr, intPtr, int16Ptr in the string_to_gocode.go + // TODO:= to use "k8s.io/utils/pointer" library case "int", "int16", "int32", "int64": - return fmt.Sprintf("%sPtr(%s)", objType[1:], objVal[1:len(objVal)-1]) + return fmt.Sprintf("%sPtr(%s)", afterObjType, objVal[1:len(objVal)-1]) case "bool": return fmt.Sprintf("boolPtr(%s)", objVal) case "string": @@ -218,7 +217,7 @@ func (obj *JsonStringConverter) formatTypeVal(objType string, objVal string, tab // It will reach here If It is a Composite Literal i.e. Struct OR a Enum // Step-1: If type contains v1 --> Needs to change with corresponding modules using the globalStructMapping if pointerType { - objType = "&" + objType[1:] //Converting pointer to address + objType = "&" + afterObjType //Converting pointer to address } re := regexp.MustCompile(`v1.`) index := re.FindStringIndex(objType) @@ -238,7 +237,7 @@ func (obj *JsonStringConverter) formatTypeVal(objType string, objVal string, tab Enum are intialised using () where Structs are intialised using {} Therefore, Need to Handle Separatly */ - if obj.globalEnumsSet.Contains(curStruct) && objType[:2] != "[]" { // List of enums([]enumtype) are also Intailised as Structs using {} + if obj.globalEnumsSet.Contains(curStruct) && !strings.HasPrefix(objType, "[]") { // List of enums([]enumtype) are also Intailised as Structs using {} return fmt.Sprintf("%s(%s)", objTypeWithModule, objVal) // Replacing {} with (), For Enums } else { return fmt.Sprintf("%s{\n%s\n%s}", objTypeWithModule, objVal, repeat("\t", tabCount)) @@ -259,11 +258,11 @@ func (obj *JsonStringConverter) traverseJson(v reflect.Value, curObjType string, switch v.Kind() { case reflect.Array, reflect.Slice: inter_str := "\n" - objType := curObjType[2:] // Removing the [] + sliceItemObjType, _ := strings.CutPrefix(curObjType, "[]") // If the kind is Slice/Array then it should always has [] prefix for i := 0; i < v.Len(); i++ { // Run DFS Over each iterations of slice and capture the backtrack-values - backtrackVal := obj.traverseJson(v.Index(i), objType, tabs+1) - inter_str += repeat("\t", tabs) + obj.formatTypeVal(objType, backtrackVal, tabs) + backtrackVal := obj.traverseJson(v.Index(i), sliceItemObjType, tabs+1) + inter_str += repeat("\t", tabs) + obj.formatTypeVal(sliceItemObjType, backtrackVal, tabs) inter_str += ",\n" // Add a Comma plus a New Line after every value } /* @@ -346,7 +345,7 @@ func (obj *JsonStringConverter) traverseJson(v reflect.Value, curObjType string, case reflect.Bool: logrus.Debug(repeat("\t", tabs), v.Bool()) - return fmt.Sprint(v.Bool()) // Return string version of bool + return strconv.FormatBool(v.Bool()) // Return string version of bool default: logrus.Fatal("Unsupported Kind For Json-String DFS Traversal| ", v.Kind()) @@ -363,7 +362,8 @@ func (obj *JsonStringConverter) jsonToGoCode(inputFilepath string) { var data map[string]interface{} err := json.Unmarshal(plan, &data) if err != nil { - fmt.Println("Cannot unmarshal the json ", err) + logrus.Error("Cannot unmarshal the json ", err) + return } logrus.Debug("Json Data", data) obj.generatedGoCode = obj.traverseJson(reflect.ValueOf(data), "", 2) diff --git a/helm-to-operator-codegen-sdk/common/json_to_string_test.go b/helm-to-operator-codegen-sdk/common/json_to_string_test.go index e8236eb..f668a06 100644 --- a/helm-to-operator-codegen-sdk/common/json_to_string_test.go +++ b/helm-to-operator-codegen-sdk/common/json_to_string_test.go @@ -17,7 +17,6 @@ limitations under the License. package common import ( - "fmt" "reflect" "testing" @@ -173,7 +172,7 @@ func TestTraverseJsonCompositeCases(t *testing.T) { for _, test := range tests { expected := test.expected.(string) - result := jsonStringConverterObj.traverseJson(reflect.ValueOf(test.input), fmt.Sprint(reflect.TypeOf(test.input)), 0) + result := jsonStringConverterObj.traverseJson(reflect.ValueOf(test.input), reflect.TypeOf(test.input).String(), 0) if result != expected { // compare2Strings(result, expected) t.Errorf("TraverseJson Failed (Composite-Literal)| Input : Type %v \nExpected %s \t Got %s\n", test.input, expected, result) diff --git a/helm-to-operator-codegen-sdk/common/runtime_to_json.go b/helm-to-operator-codegen-sdk/common/runtime_to_json.go index 5326685..6d9d77f 100644 --- a/helm-to-operator-codegen-sdk/common/runtime_to_json.go +++ b/helm-to-operator-codegen-sdk/common/runtime_to_json.go @@ -22,6 +22,7 @@ import ( "fmt" "os" "reflect" + "strconv" "strings" "time" @@ -47,7 +48,7 @@ The DFS Algorithm would traverse all the nodes, but will not return empty fields func (obj *RuntimeJsonConverter) runDfsJsonOmitEmpty(curObj interface{}, tabs int) interface{} { // Handling Special Cases, When We can't move further because of Private Attributes - if fmt.Sprint(reflect.TypeOf(curObj)) == "resource.Quantity" { + if reflect.TypeOf(curObj).String() == "resource.Quantity" { res := curObj.(resource.Quantity) resourceVal := res.String() if resourceVal == "0" { @@ -55,7 +56,7 @@ func (obj *RuntimeJsonConverter) runDfsJsonOmitEmpty(curObj interface{}, tabs in } // Hack: type is changed to int, because we don't want the value in double quote when converting it to string return map[string]string{"type": "int", "val": fmt.Sprintf("resource.MustParse(\"%s\")", resourceVal)} - } else if fmt.Sprint(reflect.TypeOf(curObj)) == "v1.Time" { + } else if reflect.TypeOf(curObj).String() == "v1.Time" { /* Since the attributes of v1.Time struct are private, Therefore we need to send back the value using GoString() method */ @@ -90,27 +91,36 @@ func (obj *RuntimeJsonConverter) runDfsJsonOmitEmpty(curObj interface{}, tabs in // Run DFS over the attributes (Fields) of current Struct backtrackVal := obj.runDfsJsonOmitEmpty(objRef.Field(i).Interface(), tabs+1) if backtrackVal != nil { - inter["type"] = fmt.Sprint(objRef.Type().Field(i).Type) // Type of i'th Field - inter["val"] = backtrackVal // Backtracked/Actual Value of i'th Field - out[objRef.Type().Field(i).Name] = inter // Save the (Type and Value of i'th Field) with key as i'th Field Name + inter["type"] = objRef.Type().Field(i).Type.String() // Type of i'th Field + inter["val"] = backtrackVal // Backtracked/Actual Value of i'th Field + out[objRef.Type().Field(i).Name] = inter // Save the (Type and Value of i'th Field) with key as i'th Field Name } } if len(out) == 0 { return nil } return out - - case reflect.Float32, reflect.Float64, reflect.Int, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int64: - data := fmt.Sprint(objRef) // Converts the Obj Val to String Val - if data == "0" { // 0 is considered to be default value, Therefore, Omitting it + case reflect.Int, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int64: + data := strconv.Itoa(int(objRef.Int())) + if data == "0" { // 0 is considered to be default value, Therefore, Omitting it + return nil + } + return data + case reflect.Float32: + data := strconv.FormatFloat(objRef.Float(), 'f', -1, 32) // Converts the Obj Val to String Val + if data == "0" { // 0 is considered to be default value, Therefore, Omitting it + return nil + } + return data + case reflect.Float64: + data := strconv.FormatFloat(objRef.Float(), 'f', -1, 64) // Converts the Obj Val to String Val + if data == "0" { // 0 is considered to be default value, Therefore, Omitting it return nil } return data - case reflect.Bool: data := objRef.Bool() return data - case reflect.String: data := objRef.String() if data == "" { // "" is considered to be default value, Therefore, Omitting it @@ -121,16 +131,15 @@ func (obj *RuntimeJsonConverter) runDfsJsonOmitEmpty(curObj interface{}, tabs in data = strings.ReplaceAll(data, "\\", "\\\\") // Replacing String containing \ with \\ data = strings.ReplaceAll(data, "\"", "\\\"") // Replacing String containing " with \" return data - case reflect.Slice: var out []interface{} if objRef.Len() == 0 { return nil } - sliceElementType := objRef.Index(0).Type() + sliceElementType := objRef.Index(0).Type().String() // Special Cases: // Case 1: If The Slice represents a Byte, then its Element data-type would be uint8 - if fmt.Sprint(sliceElementType) == "uint8" { + if sliceElementType == "uint8" { // Assuming that the byte has come from Kind: Secret, So, we need to encode the string to base64, before writing in code // Thought: You never write the actual value of secret in yaml, but the encoded versions of it, The same is happening below // Todo: Finding out where []byte is used other than Secret, and if it also represent encoded version or actual string diff --git a/helm-to-operator-codegen-sdk/common/string_to_gofile.go b/helm-to-operator-codegen-sdk/common/string_to_gofile.go index 4e6e42f..810cc02 100644 --- a/helm-to-operator-codegen-sdk/common/string_to_gofile.go +++ b/helm-to-operator-codegen-sdk/common/string_to_gofile.go @@ -19,6 +19,7 @@ package common import ( "fmt" "os" + "strconv" "strings" "github.com/liyue201/gostl/ds/set" @@ -55,27 +56,29 @@ func (obj *GoFile) getRunnableFunction(resourceType string, resourceList []strin } fxnReturnType := "" firstResource := resourceList[0] - for i := 0; i < len(firstResource); i++ { - if firstResource[i] == '{' { + for _, c := range firstResource { + if c == '{' { break - } else if firstResource[i] != '\t' { - fxnReturnType += string(firstResource[i]) + } else if c != '\t' { + fxnReturnType += string(c) } } - if fxnReturnType[0] == '&' { - fxnReturnType = "*" + fxnReturnType[1:] + + afterFxnReturnType, pointerType := strings.CutPrefix(fxnReturnType, "&") + if pointerType { + fxnReturnType = "*" + afterFxnReturnType } varList := "" varNamePrefix := fmt.Sprintf("%s%s", strings.ToLower(string(resourceType[0])), resourceType[1:]) // Service --> service createdVars := "" for i := 0; i < len(resourceList); i++ { - curVarName := varNamePrefix + fmt.Sprint(i+1) + curVarName := varNamePrefix + strconv.Itoa(i+1) varList += fmt.Sprintf(` %s := %s `, curVarName, resourceList[i]) - createdVars += fmt.Sprintf("%s, ", curVarName) + createdVars += curVarName + ", " } fxn := fmt.Sprintf(` diff --git a/helm-to-operator-codegen-sdk/common/unstruct_to_string.go b/helm-to-operator-codegen-sdk/common/unstruct_to_string.go index 2e3aa91..8131670 100644 --- a/helm-to-operator-codegen-sdk/common/unstruct_to_string.go +++ b/helm-to-operator-codegen-sdk/common/unstruct_to_string.go @@ -20,6 +20,7 @@ import ( "fmt" "log" "reflect" + "strconv" "strings" "github.com/sirupsen/logrus" @@ -76,20 +77,23 @@ func (obj *UnstructStringConverter) runDfsUnstruct(v reflect.Value, tabs int) st return fmt.Sprintf("map[string]interface{}{\n%s\n%s}", out, repeat("\t", tabs)) case reflect.String: data := v.String() - + // Todo: Need much better handling to strings, Since Different combinations can lead to bad-buggy results + // Below Additional Replace helps in building integrity of the "" string + data = strings.ReplaceAll(data, "\\", "\\\\") // Replacing String containing \ with \\ + data = strings.ReplaceAll(data, "\"", "\\\"") // Replacing String containing " with \" if strings.Contains(data, "\n") { - // New Lines Are now handled fmt.Sprint - // data = strings.ReplaceAll(data, "`", "` + \"`\" + `") // Replacing String containing ` with ` + "`" + ' - // data = fmt.Sprintf("fmt.Sprint(`%s`)", data) return handleMultiLineStrings(data) } - data = strings.ReplaceAll(data, "\"", "\\\"") // Need to preserve double quotes, therefore preserving by adding a backslash (" --> /") - return "\"" + data + "\"" // Sending with double quotes + return "\"" + data + "\"" // Sending with double quotes case reflect.Bool: - return fmt.Sprint(v.Bool()) // Return the Bool value as String - case reflect.Float32, reflect.Float64, reflect.Int, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int64: - return fmt.Sprint(v) // Return the Int, Float value as String + return strconv.FormatBool(v.Bool()) // Return the Bool value as String + case reflect.Int, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int64: + return strconv.Itoa(int(v.Int())) // Return the Int value as String + case reflect.Float32: + return strconv.FormatFloat(v.Float(), 'f', -1, 32) // Return the float32 value as string + case reflect.Float64: + return strconv.FormatFloat(v.Float(), 'f', -1, 64) // Returns the float64 value as string default: logrus.Error("Current Type is Not Supported in Unstruct-To-String| ", v.Kind()) diff --git a/helm-to-operator-codegen-sdk/common/util.go b/helm-to-operator-codegen-sdk/common/util.go index 8449bc6..0a24d5e 100644 --- a/helm-to-operator-codegen-sdk/common/util.go +++ b/helm-to-operator-codegen-sdk/common/util.go @@ -63,12 +63,9 @@ func RecursiveListYamls(curFolder string) (yamlfiles []string) { yamlfiles = append(yamlfiles, returnedYamlFiles...) } else { fileName := files.Name() - if len(fileName) > 5 { - if fileName[len(fileName)-5:] == ".yaml" { - yamlfiles = append(yamlfiles, curFolder+"/"+fileName) - } + if strings.HasSuffix(fileName, ".yaml") { + yamlfiles = append(yamlfiles, curFolder+"/"+fileName) } - } } return From 8d298dfb3390eed585e4d5279eee1c4da2fd65d2 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Fri, 12 Jan 2024 18:50:43 +0530 Subject: [PATCH 13/19] Helm labels Deletion Support Added --- .../common/runtime_to_json.go | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/helm-to-operator-codegen-sdk/common/runtime_to_json.go b/helm-to-operator-codegen-sdk/common/runtime_to_json.go index 6d9d77f..00fee53 100644 --- a/helm-to-operator-codegen-sdk/common/runtime_to_json.go +++ b/helm-to-operator-codegen-sdk/common/runtime_to_json.go @@ -40,6 +40,25 @@ import ( type RuntimeJsonConverter struct { } +func (obj *RuntimeJsonConverter) refactorHelmLabels(labels map[string]interface{}) map[string]interface{} { + /* + According to helm-k8s docs: + Standard-Labels = [app.kubernetes.io/name, helm.sh/chart, app.kubernetes.io/managed-by, app.kubernetes.io/instance, app.kubernetes.io/component, app.kubernetes.io/part-of] + as of : https://helm.sh/docs/chart_best_practices/labels/ + According to me: + Helm-Specific-Labels are: [helm.sh/chart, app.kubernetes.io/managed-by] + ToDo: to check above intution + */ + helmSpecificLabels := []string{"helm.sh/chart", "app.kubernetes.io/managed-by"} + for _, prohibitedLabels := range helmSpecificLabels { + _, ok := labels[prohibitedLabels] + if ok { + delete(labels, prohibitedLabels) + } + } + return labels +} + /* Recursive Function (DFS Algorithm) to traverse the object structure and identify data-types of various attributes If you see the Runtime-Object as a Hierachial Structure (Tree), then you say curObj would be the node of the tree/graph you are currently at @@ -93,7 +112,11 @@ func (obj *RuntimeJsonConverter) runDfsJsonOmitEmpty(curObj interface{}, tabs in if backtrackVal != nil { inter["type"] = objRef.Type().Field(i).Type.String() // Type of i'th Field inter["val"] = backtrackVal // Backtracked/Actual Value of i'th Field - out[objRef.Type().Field(i).Name] = inter // Save the (Type and Value of i'th Field) with key as i'th Field Name + attributeName := objRef.Type().Field(i).Name + if attributeName == "Labels" { + inter["val"] = obj.refactorHelmLabels(backtrackVal.(map[string]interface{})) + } + out[attributeName] = inter // Save the (Type and Value of i'th Field) with key as i'th Field Name } } if len(out) == 0 { From 97958727de98f545f077158bfa83e39ecc65acb3 Mon Sep 17 00:00:00 2001 From: jain-ashish-sam <145763342+jain-ashish-sam@users.noreply.github.com> Date: Fri, 12 Jan 2024 18:56:09 +0530 Subject: [PATCH 14/19] Updating README.md for Golang-Version-Update to 1.21 --- helm-to-operator-codegen-sdk/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/helm-to-operator-codegen-sdk/README.md b/helm-to-operator-codegen-sdk/README.md index dcfae7b..6c6148a 100644 --- a/helm-to-operator-codegen-sdk/README.md +++ b/helm-to-operator-codegen-sdk/README.md @@ -1,9 +1,9 @@ # helm-to-operator-codegen-sdk ### Prerequisties: -1. GoLang Version: 1.19 +1. GoLang Version: 1.21 ``` -wget -c https://golang.org/dl/go1.19.8.linux-amd64.tar.gz -sudo tar -C /usr/local -xvzf go1.19.8.linux-amd64.tar.gz +wget -c https://golang.org/dl/go1.21.6.linux-amd64.tar.gz +sudo tar -C /usr/local -xvzf go1.21.6.linux-amd64.tar.gz # Create Folder for Go Packages: mkdir go cd go From b7cbeeac0224546c85dfc73e71c7197d849fb437 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Wed, 24 Jan 2024 20:49:19 +0530 Subject: [PATCH 15/19] Removing commented-code & Replacing interface{} to any --- helm-to-operator-codegen-sdk/Makefile | 1 - helm-to-operator-codegen-sdk/README.md | 16 +++++++-------- .../common/helm_to_yaml.go | 1 - .../common/json_to_string.go | 16 +++++++-------- .../common/json_to_string_test.go | 10 +++++----- .../common/runtime_to_json.go | 18 ++++++++--------- .../common/runtime_to_json_test.go | 20 +++++++++---------- .../common/test_helper.go | 20 +++---------------- .../common/unstruct_to_string.go | 8 ++++---- .../common/unstruct_to_string_test.go | 14 ++++++------- .../common/util_test.go | 10 ---------- helm-to-operator-codegen-sdk/main.go | 9 +-------- 12 files changed, 55 insertions(+), 88 deletions(-) diff --git a/helm-to-operator-codegen-sdk/Makefile b/helm-to-operator-codegen-sdk/Makefile index 93f45c4..fd5ef96 100644 --- a/helm-to-operator-codegen-sdk/Makefile +++ b/helm-to-operator-codegen-sdk/Makefile @@ -50,7 +50,6 @@ unit_clean: ## clean up the unit test artifacts created ifeq ($(CONTAINER_RUNNABLE), 0) $(CONTAINER_RUNTIME) system prune -f endif - # rm ${TEST_COVERAGE_FILE} ${TEST_COVERAGE_HTML_FILE} ${TEST_COVERAGE_FUNC_FILE} > /dev/null 2>&1 rm -f ${TEST_COVERAGE_FILE} ${TEST_COVERAGE_HTML_FILE} ${TEST_COVERAGE_FUNC_FILE} .PHONY: unit diff --git a/helm-to-operator-codegen-sdk/README.md b/helm-to-operator-codegen-sdk/README.md index 6c6148a..4e1e0ba 100644 --- a/helm-to-operator-codegen-sdk/README.md +++ b/helm-to-operator-codegen-sdk/README.md @@ -43,14 +43,14 @@ e.g. go run main.go /home/ubuntu/free5gccharts/towards5gs-helm/charts/free5gc/ch Note: 1. If is not provided, then by default it would take the helm_charts present in Input-folder. -The generated Go-Code would be written in the "outputs/generated_code.go" file - -The Generated Go-Code would contain the following plugable function: -1. Create_All(): It when called, create all the k8s resources(services, deployment) in the kubernetes cluster. -2. Delete_All(): It when called, delete all the k8s resources(services, deployment) in the kubernetes cluster. -3. Get_Resources(): would return the list of a particular resource. - 1. Get_Service() would return the list of all Services-Objects. - 2. Get_Deployment() would return the list of all Deployment-Objects. & so on +The generated Go-Code would be written to the "outputs/generated_code.go" file + +The Generated Go-Code shall contain the following plugable functions: +1. Create_All(): When called, it will create all the k8s resources(services, deployment) on the kubernetes cluster. +2. Delete_All(): When called, it will delete all the k8s resources(services, deployment) on the kubernetes cluster. +3. Get_Resources(): Shall return the list of a particular resource. + 1. Get_Service(): Shall return the list of all services. + 2. Get_Deployment(): Shall return the list of all deployments. & so on Further Docs: 1. Design Document: [link](https://docs.google.com/document/d/1b7WpK_BHe7nRuGP5MOy6Mxf3hpN_cro9/edit) diff --git a/helm-to-operator-codegen-sdk/common/helm_to_yaml.go b/helm-to-operator-codegen-sdk/common/helm_to_yaml.go index ed4af43..816b63a 100644 --- a/helm-to-operator-codegen-sdk/common/helm_to_yaml.go +++ b/helm-to-operator-codegen-sdk/common/helm_to_yaml.go @@ -38,7 +38,6 @@ func (obj *HelmYamlConvertor) ConvertHelmToYaml() error { logrus.Info(obj.Namespace, " ", obj.Chartpath) logrus.Info(" ----------------- Converting Helm to Yaml --------------------------") _ = createDirIfDontExist("temp") - // logrus.Info(err) if obj.Namespace == "" { obj.Namespace = "default" } diff --git a/helm-to-operator-codegen-sdk/common/json_to_string.go b/helm-to-operator-codegen-sdk/common/json_to_string.go index be8c659..df45a9b 100644 --- a/helm-to-operator-codegen-sdk/common/json_to_string.go +++ b/helm-to-operator-codegen-sdk/common/json_to_string.go @@ -112,7 +112,7 @@ func (obj *JsonStringConverter) setModStructMapping(inputFilepath string) { out := map[string]string{} plan, _ := os.ReadFile(filepath.Clean(inputFilepath)) - var data map[string]interface{} + var data map[string]any err := json.Unmarshal(plan, &data) if err != nil { logrus.Fatal("Cannot unmarshal the json For Struct Mapping | ", inputFilepath, " Error | ", err) @@ -121,7 +121,7 @@ func (obj *JsonStringConverter) setModStructMapping(inputFilepath string) { // Validating Struct Mapping, Every Mapping value should contain unique values: pending var validateSet = set.New[string](comparator.StringComparator, set.WithGoroutineSafe()) for _, structsList := range data { - for _, structName := range structsList.([]interface{}) { + for _, structName := range structsList.([]any) { // ToDo: What to do when duplication is found, currently we are only logging if validateSet.Contains(structName.(string)) { logrus.Warn("Duplication Detected in Struct Mapping | For ", structName.(string)) @@ -133,7 +133,7 @@ func (obj *JsonStringConverter) setModStructMapping(inputFilepath string) { // Saving to Global Map, so that it could be used by "formatTypeVal" function for module, structs := range data { - for _, structName := range structs.([]interface{}) { + for _, structName := range structs.([]any) { structNameStr := structName.(string) out[structNameStr] = module } @@ -147,7 +147,7 @@ func (obj *JsonStringConverter) setModStructMapping(inputFilepath string) { */ func (obj *JsonStringConverter) setEnums(inputFilepath string) { fp, _ := os.ReadFile(filepath.Clean(inputFilepath)) - var data map[string]interface{} + var data map[string]any err := json.Unmarshal(fp, &data) if err != nil { logrus.Fatal("Cannot unmarshal the json For Enum-Mapping | ", inputFilepath, " Error | ", err) @@ -155,7 +155,7 @@ func (obj *JsonStringConverter) setEnums(inputFilepath string) { var tempSet = set.New[string](comparator.StringComparator, set.WithGoroutineSafe()) // Saving to Global Enum Set, so that it could be used by "formatTypeVal" function for _, enums := range data { - for _, val := range enums.([]interface{}) { + for _, val := range enums.([]any) { enum := val.(string) if tempSet.Contains(enum) { // ToDo: What to do when duplication is found, currently we are only logging @@ -278,7 +278,7 @@ func (obj *JsonStringConverter) traverseJson(v reflect.Value, curObjType string, out := "" for _, key := range v.MapKeys() { // Here key represents the struct Attribute Name/ Field Name - objMap, _ := v.MapIndex(key).Interface().(map[string]interface{}) + objMap, _ := v.MapIndex(key).Interface().(map[string]any) logrus.Debug(repeat("\t", tabs), key) objType := objMap["type"].(string) // objType represents the type of i'th attribute logrus.Debug(repeat("\t", tabs) + objType) @@ -291,7 +291,7 @@ func (obj *JsonStringConverter) traverseJson(v reflect.Value, curObjType string, } // Assuming the Key in Map is always string, map[string] --> 11 Characters mapValuesType := objType[mapStartIndex+11:] - curMap := objVal.(map[string]interface{}) + curMap := objVal.(map[string]any) backTrackValues := "" for curKey, curVal := range curMap { @@ -359,7 +359,7 @@ Reads the temp.json created by runtime_to_json.go and traverse json(DFS Runner) func (obj *JsonStringConverter) jsonToGoCode(inputFilepath string) { plan, _ := os.ReadFile(filepath.Clean(inputFilepath)) - var data map[string]interface{} + var data map[string]any err := json.Unmarshal(plan, &data) if err != nil { logrus.Error("Cannot unmarshal the json ", err) diff --git a/helm-to-operator-codegen-sdk/common/json_to_string_test.go b/helm-to-operator-codegen-sdk/common/json_to_string_test.go index f668a06..8d35ae2 100644 --- a/helm-to-operator-codegen-sdk/common/json_to_string_test.go +++ b/helm-to-operator-codegen-sdk/common/json_to_string_test.go @@ -149,8 +149,8 @@ func TestTraverseJsonCompositeCases(t *testing.T) { `, }, { - input: map[string]interface{}{ - "Condition": map[string]interface{}{ + input: map[string]any{ + "Condition": map[string]any{ "type": "bool", "val": true, }, @@ -158,10 +158,10 @@ func TestTraverseJsonCompositeCases(t *testing.T) { expected: "Condition : true, ", }, { - input: map[string]interface{}{ - "Labels": map[string]interface{}{ + input: map[string]any{ + "Labels": map[string]any{ "type": "map[string]string", - "val": map[string]interface{}{ + "val": map[string]any{ "label1": "app1", }, }, diff --git a/helm-to-operator-codegen-sdk/common/runtime_to_json.go b/helm-to-operator-codegen-sdk/common/runtime_to_json.go index 00fee53..8be6087 100644 --- a/helm-to-operator-codegen-sdk/common/runtime_to_json.go +++ b/helm-to-operator-codegen-sdk/common/runtime_to_json.go @@ -40,7 +40,7 @@ import ( type RuntimeJsonConverter struct { } -func (obj *RuntimeJsonConverter) refactorHelmLabels(labels map[string]interface{}) map[string]interface{} { +func (obj *RuntimeJsonConverter) refactorHelmLabels(labels map[string]any) map[string]any { /* According to helm-k8s docs: Standard-Labels = [app.kubernetes.io/name, helm.sh/chart, app.kubernetes.io/managed-by, app.kubernetes.io/instance, app.kubernetes.io/component, app.kubernetes.io/part-of] @@ -64,7 +64,7 @@ Recursive Function (DFS Algorithm) to traverse the object structure and identify If you see the Runtime-Object as a Hierachial Structure (Tree), then you say curObj would be the node of the tree/graph you are currently at The DFS Algorithm would traverse all the nodes, but will not return empty fields */ -func (obj *RuntimeJsonConverter) runDfsJsonOmitEmpty(curObj interface{}, tabs int) interface{} { +func (obj *RuntimeJsonConverter) runDfsJsonOmitEmpty(curObj any, tabs int) any { // Handling Special Cases, When We can't move further because of Private Attributes if reflect.TypeOf(curObj).String() == "resource.Quantity" { @@ -85,7 +85,7 @@ func (obj *RuntimeJsonConverter) runDfsJsonOmitEmpty(curObj interface{}, tabs in if timeVar == defaultVal { //If The LHS is the Default Value, then omit it return nil } else { - var out = make(map[string]interface{}) + var out = make(map[string]any) timeVal := timeVar.GoString() out["Time"] = map[string]string{"type": "int", "val": timeVal} // Hack: type is changed to int, because we don't want the value in double quote return out @@ -100,9 +100,9 @@ func (obj *RuntimeJsonConverter) runDfsJsonOmitEmpty(curObj interface{}, tabs in switch objRef.Kind() { case reflect.Struct: - var out = make(map[string]interface{}) + var out = make(map[string]any) for i := 0; i < objRef.NumField(); i++ { - var inter = make(map[string]interface{}) + var inter = make(map[string]any) if !objRef.Field(i).CanInterface() { logrus.Warn("Private Attributes are not visible to me ! Support Missing For || ", objRef.Type().Field(i).Name, objRef.Type().Field(i).Type) continue @@ -114,7 +114,7 @@ func (obj *RuntimeJsonConverter) runDfsJsonOmitEmpty(curObj interface{}, tabs in inter["val"] = backtrackVal // Backtracked/Actual Value of i'th Field attributeName := objRef.Type().Field(i).Name if attributeName == "Labels" { - inter["val"] = obj.refactorHelmLabels(backtrackVal.(map[string]interface{})) + inter["val"] = obj.refactorHelmLabels(backtrackVal.(map[string]any)) } out[attributeName] = inter // Save the (Type and Value of i'th Field) with key as i'th Field Name } @@ -155,7 +155,7 @@ func (obj *RuntimeJsonConverter) runDfsJsonOmitEmpty(curObj interface{}, tabs in data = strings.ReplaceAll(data, "\"", "\\\"") // Replacing String containing " with \" return data case reflect.Slice: - var out []interface{} + var out []any if objRef.Len() == 0 { return nil } @@ -190,7 +190,7 @@ func (obj *RuntimeJsonConverter) runDfsJsonOmitEmpty(curObj interface{}, tabs in case reflect.Map: // Assumption : Key Value is Always String - var out = make(map[string]interface{}) + var out = make(map[string]any) switch objRef.Type().Key().Kind() { case reflect.String: for _, key := range objRef.MapKeys() { @@ -248,7 +248,7 @@ func (obj *RuntimeJsonConverter) Convert(runtimeObj runtime.Object, gvk schema.G } logrus.Debug("----------------------------------Your Runtime Object--------------------\n", runtimeObj) logrus.Debug("----------------------------------Your Runtime Object Ends--------------------\n") - var objMap interface{} + var objMap any switch gvk.Kind { case "Service": curObj := runtimeObj.(*corev1.Service) diff --git a/helm-to-operator-codegen-sdk/common/runtime_to_json_test.go b/helm-to-operator-codegen-sdk/common/runtime_to_json_test.go index 811e508..d0d086a 100644 --- a/helm-to-operator-codegen-sdk/common/runtime_to_json_test.go +++ b/helm-to-operator-codegen-sdk/common/runtime_to_json_test.go @@ -56,8 +56,8 @@ func TestConvert(t *testing.T) { resultData, _ := getFileContents(resultFile) expectedData, _ := getFileContents(expectedFile) - var result interface{} - var expected interface{} + var result any + var expected any _ = json.Unmarshal([]byte(resultData), &result) _ = json.Unmarshal([]byte(expectedData), &expected) if !reflect.DeepEqual(result, expected) { @@ -114,16 +114,16 @@ Tests For Complex Cases in DFS Traversal (Struct, Slices, Maps) */ func TestRunDfsJsonOmitEmptyComplexCases(t *testing.T) { tests := []Tests{ - {[]string{"abc", "def", ""}, []interface{}{"abc", "def", ""}}, + {[]string{"abc", "def", ""}, []any{"abc", "def", ""}}, {[]byte("my-secret"), "bXktc2VjcmV0"}, //Base64 encoded version of my-secret// This is also a TODO task (to check if it is important or not) - // {[]interface{}{0, "abc"}, []interface{}{"", "abc"}},// This is TODO Task + // {[]any{0, "abc"}, []any{"", "abc"}},// This is TODO Task {metav1.ObjectMeta{}, nil}, //Empty Struct Should Return Nil { - input: map[string]interface{}{ + input: map[string]any{ "key1": "abc", "key2": 6, }, - expected: map[string]interface{}{ + expected: map[string]any{ "key1": "abc", "key2": "6", }, @@ -133,12 +133,12 @@ func TestRunDfsJsonOmitEmptyComplexCases(t *testing.T) { Name: "tests", Generation: 2, }, - expected: map[string]interface{}{ - "Name": map[string]interface{}{ + expected: map[string]any{ + "Name": map[string]any{ "type": "string", "val": "tests", }, - "Generation": map[string]interface{}{ + "Generation": map[string]any{ "type": "int64", "val": "2", }, @@ -167,7 +167,7 @@ func TestRunDfsJsonOmitEmptySpecialCases(t *testing.T) { }, { input: metav1.Time{Time: time.Time.AddDate(time.Time{}, 2, 3, 0)}, - expected: map[string]interface{}{ + expected: map[string]any{ "Time": map[string]string{ "type": "int", "val": time.Time.AddDate(time.Time{}, 2, 3, 0).GoString(), diff --git a/helm-to-operator-codegen-sdk/common/test_helper.go b/helm-to-operator-codegen-sdk/common/test_helper.go index 0c7d6d6..9a35e62 100644 --- a/helm-to-operator-codegen-sdk/common/test_helper.go +++ b/helm-to-operator-codegen-sdk/common/test_helper.go @@ -28,8 +28,8 @@ import ( ) type Tests struct { - input interface{} - expected interface{} + input any + expected any } /* @@ -50,7 +50,7 @@ func getFileContents(inputFilePath string) ([]byte, error) { } /* -Convert the KRM Resources to *unstructured.Unstructured (map[string]interface{}) +Convert the KRM Resources to *unstructured.Unstructured (map[string]any) Returns the *unstructured.Unstructured Object, GroupVersionKind (gvk), error */ func unstructuredDecode(data []byte) (*unstructured.Unstructured, *schema.GroupVersionKind, error) { @@ -63,17 +63,3 @@ func unstructuredDecode(data []byte) (*unstructured.Unstructured, *schema.GroupV } return obj, gvk, nil } - -// func compare2Strings(a string, b string) { - -// lenA, lenB := len(a), len(b) -// fmt.Println(lenA, lenB) -// minL := lenA -// if lenB < lenA { -// minL = lenB -// } - -// for index := 0; index < minL; index++ { -// fmt.Printf("%d %c %c\n", index, a[index], b[index]) -// } -// } diff --git a/helm-to-operator-codegen-sdk/common/unstruct_to_string.go b/helm-to-operator-codegen-sdk/common/unstruct_to_string.go index 8131670..970258d 100644 --- a/helm-to-operator-codegen-sdk/common/unstruct_to_string.go +++ b/helm-to-operator-codegen-sdk/common/unstruct_to_string.go @@ -48,7 +48,7 @@ func (obj *UnstructStringConverter) runDfsUnstruct(v reflect.Value, tabs int) st switch v.Kind() { case reflect.Array, reflect.Slice: - curSlice := v.Interface().([]interface{}) + curSlice := v.Interface().([]any) var outStr = "" for _, sliceItem := range curSlice { // Run DFS over all the iterations of Slice, and capture the backtrack value @@ -59,11 +59,11 @@ func (obj *UnstructStringConverter) runDfsUnstruct(v reflect.Value, tabs int) st if len(outStr) > 1 { outStr = outStr[:len(outStr)-1] //Removing the Last \n } - return fmt.Sprintf("[]interface{}{\n%s\n%s}", outStr, repeat("\t", tabs)) + return fmt.Sprintf("[]any{\n%s\n%s}", outStr, repeat("\t", tabs)) case reflect.Map: out := "" - curMap := v.Interface().(map[string]interface{}) + curMap := v.Interface().(map[string]any) for key, val := range curMap { // Run DFS over all the Values of Map, and capture the backtrack value backtackValues := obj.runDfsUnstruct(reflect.ValueOf(val), tabs+1) @@ -74,7 +74,7 @@ func (obj *UnstructStringConverter) runDfsUnstruct(v reflect.Value, tabs int) st if len(out) > 1 { out = out[:len(out)-1] //Removing the Last \n } - return fmt.Sprintf("map[string]interface{}{\n%s\n%s}", out, repeat("\t", tabs)) + return fmt.Sprintf("map[string]any{\n%s\n%s}", out, repeat("\t", tabs)) case reflect.String: data := v.String() // Todo: Need much better handling to strings, Since Different combinations can lead to bad-buggy results diff --git a/helm-to-operator-codegen-sdk/common/unstruct_to_string_test.go b/helm-to-operator-codegen-sdk/common/unstruct_to_string_test.go index abc5827..b2eb088 100644 --- a/helm-to-operator-codegen-sdk/common/unstruct_to_string_test.go +++ b/helm-to-operator-codegen-sdk/common/unstruct_to_string_test.go @@ -29,22 +29,22 @@ Composite Cases : Maps, Slices func TestRunDfsUnstructCompositeCases(t *testing.T) { tests := []Tests{ { - input: map[string]interface{}{ + input: map[string]any{ "Name": "ABC", }, - expected: "map[string]interface{}{\n\"Name\": \"ABC\",\n}", + expected: "map[string]any{\n\"Name\": \"ABC\",\n}", }, { - input: map[string]interface{}{ + input: map[string]any{ "Replicas": 4, }, - expected: "map[string]interface{}{\n\"Replicas\": 4,\n}", + expected: "map[string]any{\n\"Replicas\": 4,\n}", }, { - input: map[string]interface{}{ - "Conditions": []interface{}{true}, + input: map[string]any{ + "Conditions": []any{true}, }, - expected: "map[string]interface{}{\n\"Conditions\": []interface{}{\n\ttrue,\n\t},\n}", + expected: "map[string]any{\n\"Conditions\": []any{\n\ttrue,\n\t},\n}", }, } diff --git a/helm-to-operator-codegen-sdk/common/util_test.go b/helm-to-operator-codegen-sdk/common/util_test.go index 8529ede..7be74b2 100644 --- a/helm-to-operator-codegen-sdk/common/util_test.go +++ b/helm-to-operator-codegen-sdk/common/util_test.go @@ -53,16 +53,6 @@ func TestCreateDirIfDontExist(t *testing.T) { func TestRecursiveListYamls(t *testing.T) { result := RecursiveListYamls("tests") - // testHelmChartFolder := "tests/test-helmCharts/hello-world/" - // expected := []string{"tests/test-yamls/deployment.yaml", "tests/test-yamls/third-party-cr.yaml", - // testHelmChartFolder + "Chart.yaml", testHelmChartFolder + "values.yaml", - // testHelmChartFolder + "templates/deployment.yaml", testHelmChartFolder + "templates/service.yaml", - // testHelmChartFolder + "templates/serviceaccount.yaml", testHelmChartFolder + "templates/third-party-cr.yaml", - // } - - // if !reflect.DeepEqual(result, expected) { - // t.Errorf("Util-tests | 'RecursiveListYamls' test failed | \n Expected %v \n Got %v", expected, result) - // } if len(result) != 8 { t.Errorf("Util-tests | 'RecursiveListYamls' test failed | \n Expected Length %v \n Got %v", 8, result) } diff --git a/helm-to-operator-codegen-sdk/main.go b/helm-to-operator-codegen-sdk/main.go index d1a85e5..7cb731e 100644 --- a/helm-to-operator-codegen-sdk/main.go +++ b/helm-to-operator-codegen-sdk/main.go @@ -25,8 +25,6 @@ import ( "path/filepath" "strings" - // logging "github.com/op/go-logging" - "github.com/liyue201/gostl/ds/set" "github.com/liyue201/gostl/utils/comparator" "github.com/sirupsen/logrus" @@ -50,7 +48,7 @@ func init() { } /* -Convert the KRM Resources to *unstructured.Unstructured (map[string]interface{}) +Convert the KRM Resources to *unstructured.Unstructured (map[string]any) Returns the *unstructured.Unstructured Object, GroupVersionKind (gvk), error */ func unstructuredDecode(data []byte) (*unstructured.Unstructured, *schema.GroupVersionKind, error) { @@ -129,7 +127,6 @@ func init() { } func main() { - // curHelmChart := "testing_helpers/free5gc_helm_chart" curHelmChart := "inputs" cmdArgs := os.Args[1:] if len(cmdArgs) != 0 { @@ -160,8 +157,6 @@ func main() { for _, yamlfile := range allYamlPaths { logrus.Info("CurFile --> | ", yamlfile) runtimeObjList, gvkList, unstructObjList, unstructGvkList := handleSingleYaml(yamlfile) - // _, _ = runtimeObjList, gvkList - // _, _ = unstructGvkList, unstructObjList for i := 0; i < len(runtimeObjList); i++ { logrus.Info(fmt.Sprintf(" Current KRM Resource| Kind : %s| YamlFilePath : %s", gvkList[i].Kind, yamlfile)) err := runtimeJsonConverterObj.Convert(runtimeObjList[i], gvkList[i]) @@ -182,8 +177,6 @@ func main() { for i := 0; i < len(unstructObjList); i++ { gocode := unstructStringConverterObj.Convert(unstructObjList[i]) - // _ = unstructGvkList[i] - // gocodes["third_party_kinds"] = append(gocodes["third_party_kinds"], gocode) gocodes[unstructGvkList[i].Kind] = append(gocodes[unstructGvkList[i].Kind], gocode) logrus.Info("\t Converting Unstructured to String Completed ") } From f7eb43257623ad60efa920e03e15d3e240dd21a7 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Wed, 24 Jan 2024 21:16:03 +0530 Subject: [PATCH 16/19] LogLevel to be set at runtime --- .../common/runtime_to_json_test.go | 6 ++--- .../common/test_helper.go | 2 +- .../common/unstruct_to_string_test.go | 2 +- helm-to-operator-codegen-sdk/main.go | 25 +++++++------------ 4 files changed, 14 insertions(+), 21 deletions(-) diff --git a/helm-to-operator-codegen-sdk/common/runtime_to_json_test.go b/helm-to-operator-codegen-sdk/common/runtime_to_json_test.go index d0d086a..7d007c1 100644 --- a/helm-to-operator-codegen-sdk/common/runtime_to_json_test.go +++ b/helm-to-operator-codegen-sdk/common/runtime_to_json_test.go @@ -39,7 +39,7 @@ func TestConvert(t *testing.T) { inputFile := "tests/test-yamls/deployment.yaml" decoder := scheme.Codecs.UniversalDeserializer() - data, err := getFileContents(inputFile) + data, err := GetFileContents(inputFile) if err != nil { t.Errorf("Unable to Load File %s| Error %s", inputFile, err) } @@ -53,8 +53,8 @@ func TestConvert(t *testing.T) { } resultFile := "temp/temp.json" expectedFile := "tests/expected-json/deployment.json" - resultData, _ := getFileContents(resultFile) - expectedData, _ := getFileContents(expectedFile) + resultData, _ := GetFileContents(resultFile) + expectedData, _ := GetFileContents(expectedFile) var result any var expected any diff --git a/helm-to-operator-codegen-sdk/common/test_helper.go b/helm-to-operator-codegen-sdk/common/test_helper.go index 9a35e62..ba2d362 100644 --- a/helm-to-operator-codegen-sdk/common/test_helper.go +++ b/helm-to-operator-codegen-sdk/common/test_helper.go @@ -35,7 +35,7 @@ type Tests struct { /* Reads the File from FilePath and returns the file-data */ -func getFileContents(inputFilePath string) ([]byte, error) { +func GetFileContents(inputFilePath string) ([]byte, error) { file, err := os.Open(filepath.Clean(inputFilePath)) if err != nil { return nil, err diff --git a/helm-to-operator-codegen-sdk/common/unstruct_to_string_test.go b/helm-to-operator-codegen-sdk/common/unstruct_to_string_test.go index b2eb088..350a70a 100644 --- a/helm-to-operator-codegen-sdk/common/unstruct_to_string_test.go +++ b/helm-to-operator-codegen-sdk/common/unstruct_to_string_test.go @@ -59,7 +59,7 @@ func TestRunDfsUnstructCompositeCases(t *testing.T) { func TestConvertUnstruct(t *testing.T) { inputFilePath := "tests/test-yamls/deployment.yaml" - data, err := getFileContents(inputFilePath) + data, err := GetFileContents(inputFilePath) if err != nil { t.Errorf("Unable to Open file %s for Unstruct-Convert| Error %v", inputFilePath, err) } diff --git a/helm-to-operator-codegen-sdk/main.go b/helm-to-operator-codegen-sdk/main.go index 7cb731e..75950ef 100644 --- a/helm-to-operator-codegen-sdk/main.go +++ b/helm-to-operator-codegen-sdk/main.go @@ -17,12 +17,9 @@ limitations under the License. package main import ( - "bufio" "fmt" "helm_to_controller/packages/common" - "io" "os" - "path/filepath" "strings" "github.com/liyue201/gostl/ds/set" @@ -72,13 +69,7 @@ Output: unstructGvkList: List of Group-Version-Kind for the unstructured objects of unstructObjList, mapped Index-wise */ func handleSingleYaml(inputFilepath string) (runtimeObjList []runtime.Object, gvkList []schema.GroupVersionKind, unstructObjList []unstructured.Unstructured, unstructGvkList []schema.GroupVersionKind) { - file, err := os.Open(filepath.Clean(inputFilepath)) - if err != nil { - logrus.Error("Error While Opening YAML file | ", inputFilepath, " \t |", err) - return - } - fp := bufio.NewReader(file) - data, err := io.ReadAll(fp) + data, err := common.GetFileContents(inputFilepath) if err != nil { logrus.Error("Error While Reading YAML file | ", inputFilepath, " \t |", err) return @@ -114,12 +105,8 @@ func handleSingleYaml(inputFilepath string) (runtimeObjList []runtime.Object, gv return } -func init() { - // Setting the Logrus Logging Level - // lvl := "debug" - lvl := "info" - // lvl = "error" - ll, err := logrus.ParseLevel(lvl) +func setLogLevel(loggingLvl string) { + ll, err := logrus.ParseLevel(loggingLvl) if err != nil { ll = logrus.DebugLevel } @@ -137,6 +124,12 @@ func main() { namespace = cmdArgs[1] } + loggingLvl := "info" + if len(cmdArgs) >= 3 { + loggingLvl = cmdArgs[2] + } + setLogLevel(loggingLvl) + var helmYamlConvertor = common.HelmYamlConvertor{Namespace: namespace, Chartpath: curHelmChart} err := helmYamlConvertor.ConvertHelmToYaml() if err != nil { From 614415eddabc2ecc7fe4f3b961805766ce24fd30 Mon Sep 17 00:00:00 2001 From: jain-ashish-sam <145763342+jain-ashish-sam@users.noreply.github.com> Date: Wed, 24 Jan 2024 23:07:44 +0530 Subject: [PATCH 17/19] Updating Readme for the config-jsons --- helm-to-operator-codegen-sdk/config/README.md | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/helm-to-operator-codegen-sdk/config/README.md b/helm-to-operator-codegen-sdk/config/README.md index 42f4615..fc4ba40 100644 --- a/helm-to-operator-codegen-sdk/config/README.md +++ b/helm-to-operator-codegen-sdk/config/README.md @@ -1 +1,22 @@ -# helm-operatort-sdk \ No newline at end of file +# Helm to Operator Codegen Sdk +## Config +The config-files consists of two jsons: +1. Struct Module Mapping : It Maps the structs (types) of the package with its package-name. ("Package-Name": [List of all the structs it contain]) +2. Enum Module Mapping: It Maps the enums of the package with its package-name. ("Package-Name": [List of all the enums it contain]) + +## Significance +The above config files solves the following problems: + +### Problem-1: “Which v1 Package?” +Mostly, It is seen that inspecting the type of struct(using reflect) would tell us that the struct belong to package “v1”, but there are multiple v1 packages (appsv1, metav1, rbacv1 etc), So, the actual package remains unknown. + +Solution: In order to solve above problems, we build a “structModuleMapping” which is a map that takes “struct-name” as key and gives “package/module name” as value. +``` + v1.Deployment --> appsv1.Deployment + v1.Service --> corev1.Service +``` + +### Problem-2: “Data-Type is Struct or Enum?” +Structs needs to be initialised using curly-brackets {}, whereas enums needs Paranthesis (), Since, reflect doesn’t tell us which data-type is struct or enum, We: + +Solution: We solve above problems by building a “enumModuleMapping” which is a set that stores all data-types that are enums. i.e. If a data-type belongs to the set, then It is a Enum. From 424b30ce408393e25e610277998c7cda48ecc11c Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Thu, 25 Jan 2024 14:34:45 +0530 Subject: [PATCH 18/19] Enum Pointers Support Added --- helm-to-operator-codegen-sdk/common/json_to_string.go | 3 +++ .../common/string_to_gofile.go | 10 ++++++++-- helm-to-operator-codegen-sdk/go.mod | 2 +- helm-to-operator-codegen-sdk/go.sum | 2 ++ helm-to-operator-codegen-sdk/main.go | 1 - helm-to-operator-codegen-sdk/to-do.txt | 2 +- 6 files changed, 15 insertions(+), 5 deletions(-) diff --git a/helm-to-operator-codegen-sdk/common/json_to_string.go b/helm-to-operator-codegen-sdk/common/json_to_string.go index df45a9b..682fdbe 100644 --- a/helm-to-operator-codegen-sdk/common/json_to_string.go +++ b/helm-to-operator-codegen-sdk/common/json_to_string.go @@ -238,6 +238,9 @@ func (obj *JsonStringConverter) formatTypeVal(objType string, objVal string, tab Therefore, Need to Handle Separatly */ if obj.globalEnumsSet.Contains(curStruct) && !strings.HasPrefix(objType, "[]") { // List of enums([]enumtype) are also Intailised as Structs using {} + if pointerType { + return fmt.Sprintf("ptr.To(%s(%s))", objTypeWithModule[1:], objVal) // Using ptr.To for Enum-Pointers + } return fmt.Sprintf("%s(%s)", objTypeWithModule, objVal) // Replacing {} with (), For Enums } else { return fmt.Sprintf("%s{\n%s\n%s}", objTypeWithModule, objVal, repeat("\t", tabCount)) diff --git a/helm-to-operator-codegen-sdk/common/string_to_gofile.go b/helm-to-operator-codegen-sdk/common/string_to_gofile.go index 810cc02..c637f58 100644 --- a/helm-to-operator-codegen-sdk/common/string_to_gofile.go +++ b/helm-to-operator-codegen-sdk/common/string_to_gofile.go @@ -127,6 +127,7 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/utils/ptr" ) func deleteMeAfterDeletingUnusedImportedModules() { @@ -145,6 +146,7 @@ func deleteMeAfterDeletingUnusedImportedModules() { _, _ = resource.ParseQuantity("") _ = context.TODO() _ = fmt.Sprintf("") + _ = ptr.To(32) } func int32Ptr(val int) *int32 { @@ -264,18 +266,22 @@ func (obj *GoFile) getMasterFxn(fxnCreated []string, inCreatedState bool) string } } + namespaceVarStatement := "" + if obj.Namespace != "" { + namespaceVarStatement = fmt.Sprintf("namespaceProvided := \"%s\"", obj.Namespace) + } outFxn := fmt.Sprintf(` /* // Before Uncommenting the following function, Make sure the data-type of r is same as of your Reconciler, // Replace "YourKindReconciler" with the type of your Reconciler func (r *YourKindReconciler)%sAll(){ var err error - namespaceProvided := "%s" + %s %s } */ - `, usage, obj.Namespace, fxnStatement) + `, usage, namespaceVarStatement, fxnStatement) return outFxn } diff --git a/helm-to-operator-codegen-sdk/go.mod b/helm-to-operator-codegen-sdk/go.mod index 6d219de..ee44d66 100644 --- a/helm-to-operator-codegen-sdk/go.mod +++ b/helm-to-operator-codegen-sdk/go.mod @@ -25,7 +25,7 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/client-go v0.27.3 // indirect k8s.io/klog/v2 v2.90.1 // indirect - k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect + k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/helm-to-operator-codegen-sdk/go.sum b/helm-to-operator-codegen-sdk/go.sum index f896a11..01d6480 100644 --- a/helm-to-operator-codegen-sdk/go.sum +++ b/helm-to-operator-codegen-sdk/go.sum @@ -99,6 +99,8 @@ k8s.io/kubectl v0.27.3 h1:HyC4o+8rCYheGDWrkcOQHGwDmyLKR5bxXFgpvF82BOw= k8s.io/kubectl v0.27.3/go.mod h1:g9OQNCC2zxT+LT3FS09ZYqnDhlvsKAfFq76oyarBcq4= k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= +k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= diff --git a/helm-to-operator-codegen-sdk/main.go b/helm-to-operator-codegen-sdk/main.go index 75950ef..4d6b745 100644 --- a/helm-to-operator-codegen-sdk/main.go +++ b/helm-to-operator-codegen-sdk/main.go @@ -136,7 +136,6 @@ func main() { logrus.Fatal("Unable to Convert Helm to Yamls| Error | ", err) } allYamlPaths := common.RecursiveListYamls("temp/templated") - // Intialising Convertor Structs/Classes var jsonStringConverterObj = common.JsonStringConverter{} jsonStringConverterObj.Intialise() diff --git a/helm-to-operator-codegen-sdk/to-do.txt b/helm-to-operator-codegen-sdk/to-do.txt index e9f5773..ba398ba 100644 --- a/helm-to-operator-codegen-sdk/to-do.txt +++ b/helm-to-operator-codegen-sdk/to-do.txt @@ -24,4 +24,4 @@ limitations under the License. 4: To check if converting from helm to yaml by helm-go client is feasible or not, since it will eliminate Helm as prerequiste for the script -5: Extending The Support For Enum-pointers (Example: PreemptionPolicy attribute in schedulingv1.PriorityClass) +5: Investigating the scope of "k8s.io/utils/ptr" library as an alternative of Int32Ptr, like fxns. From f23b66feb1e32f25235afe289cae8fea4affb399 Mon Sep 17 00:00:00 2001 From: jain-ashish-sam <145763342+jain-ashish-sam@users.noreply.github.com> Date: Thu, 25 Jan 2024 22:15:25 +0530 Subject: [PATCH 19/19] Update README.md --- helm-to-operator-codegen-sdk/README.md | 85 ++++++++++++++++---------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/helm-to-operator-codegen-sdk/README.md b/helm-to-operator-codegen-sdk/README.md index 4e1e0ba..bd2342f 100644 --- a/helm-to-operator-codegen-sdk/README.md +++ b/helm-to-operator-codegen-sdk/README.md @@ -1,47 +1,66 @@ -# helm-to-operator-codegen-sdk -### Prerequisties: +# Helm to Operator Codegen Sdk +The "Helm to Operator Codegen Sdk" takes the helm chart as input and generates the golang-code which can be used by kubernetes operator to create/delete all the resources previously managed by the helm charts. The sdk can be employed to transition from helm-way-of-deploying-resources to the operator way. + +Note: It is currently an experimental feature. + +### Step 0: Prerequisite 1. GoLang Version: 1.21 +2. Helm : v3.9.3 +3. Go Packages: ``` -wget -c https://golang.org/dl/go1.21.6.linux-amd64.tar.gz -sudo tar -C /usr/local -xvzf go1.21.6.linux-amd64.tar.gz -# Create Folder for Go Packages: -mkdir go -cd go -mkdir pkg bin src - -# In order to add Go to environment: Append the following to "/etc/profile" file -export PATH=$PATH:/usr/local/go/bin -export GOPATH="$HOME/go" -export GOBIN="$GOPATH/bin" - -# Then, Apply the changes: -source /etc/profile -# Check The Installation -go version +# Clone the Repo +cd nephio-sdk/helm-to-operator-codegen-sdk/ +go mod tidy ``` - -2. Helm : +### Step 1: Running the sdk ``` -wget https://get.helm.sh/helm-v3.9.3-linux-amd64.tar.gz -tar xvf helm-v3.9.3-linux-amd64.tar.gz -sudo mv linux-amd64/helm /usr/local/bin +go run main.go ``` +Note: +1. The logging-level can be set to one of the following values: debug, info (default), error, warn +2. If is not provided, then by default it would take the helm_charts present in Input-folder. -3. Go Packages: +#### Example Run ``` -# Clone the Repo -cd nephio-sdk/ -go mod tidy +go run main.go /home/ubuntu/free5gccharts/towards5gs-helm/charts/free5gc/charts/free5gc-amf/ free5gcns info ``` +
+The output is similar to: -### Running the Script +```console +INFO[0000] ----------------- Converting Helm to Yaml -------------------------- +WARN[0000] Duplication Detected in Struct Mapping | For Preconditions +WARN[0000] Duplication Detected in Struct Mapping | For ConditionStatus +WARN[0000] Duplication Detected in Enum Mapping | For ConditionStatus +INFO[0000] CurFile --> | temp/templated/free5gc-amf/templates/amf-configmap.yaml +INFO[0000] Current KRM Resource| Kind : ConfigMap| YamlFilePath : temp/templated/free5gc-amf/templates/amf-configmap.yaml +INFO[0000] Converting Runtime to Json Completed +INFO[0000] Converting Json to String Completed +INFO[0000] CurFile --> | temp/templated/free5gc-amf/templates/amf-deployment.yaml +INFO[0000] Current KRM Resource| Kind : Deployment| YamlFilePath : temp/templated/free5gc-amf/templates/amf-deployment.yaml +INFO[0000] Converting Runtime to Json Completed +INFO[0000] Converting Json to String Completed +INFO[0000] CurFile --> | temp/templated/free5gc-amf/templates/amf-hpa.yaml +ERRO[0000] Unable to convert yaml to unstructured |Object 'Kind' is missing in 'null' +INFO[0000] CurFile --> | temp/templated/free5gc-amf/templates/amf-ingress.yaml +ERRO[0000] Unable to convert yaml to unstructured |Object 'Kind' is missing in 'null' +INFO[0000] CurFile --> | temp/templated/free5gc-amf/templates/amf-n2-nad.yaml +INFO[0000] Kind | NetworkAttachmentDefinition Would Be Treated as Third Party Kind +INFO[0000] Converting Unstructured to String Completed +INFO[0000] CurFile --> | temp/templated/free5gc-amf/templates/amf-service.yaml +INFO[0000] Current KRM Resource| Kind : Service| YamlFilePath : temp/templated/free5gc-amf/templates/amf-service.yaml +INFO[0000] Converting Runtime to Json Completed +INFO[0000] Converting Json to String Completed +INFO[0000] ----------------- Writing GO Code --------------------------------- +INFO[0000] ----------------- Program Run Successful| Summary --------------------------------- +INFO[0000] Deployment |1 +INFO[0000] NetworkAttachmentDefinition |1 +INFO[0000] Service |1 +INFO[0000] ConfigMap |1 ``` -go run main.go -e.g. go run main.go /home/ubuntu/free5gccharts/towards5gs-helm/charts/free5gc/charts/free5gc-amf free5gcns -``` -Note: -1. If is not provided, then by default it would take the helm_charts present in Input-folder. +
+ The generated Go-Code would be written to the "outputs/generated_code.go" file