From 87d91e85c0dcac53a2c701f543f9fe7527b5400a Mon Sep 17 00:00:00 2001 From: Ere Mid Date: Thu, 19 Jan 2023 18:57:18 +0100 Subject: [PATCH] fix(k8s): recursivly find refs and selectors --- tools/tools.go | 3 +- tui/tui.go | 96 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 74 insertions(+), 25 deletions(-) diff --git a/tools/tools.go b/tools/tools.go index 81b7f7c..81941a5 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -183,8 +183,7 @@ func GenerateExamplesList(c config.Provider) tea.Msg { k.ExampleID = strings.ToLower(fmt.Sprintf("%s.%s", k.Kind, k.APIVersion)) // check for selector - k.Selectors = k.FindForProvider("Selector") - k.Refs = k.FindForProvider("Refs") + k.Selectors, k.Refs = k.FindSelectorsAndRefs() k.DependenciesFiles = map[string]bool{} // check for extra file examples diff --git a/tui/tui.go b/tui/tui.go index d0dd283..cdc12fd 100644 --- a/tui/tui.go +++ b/tui/tui.go @@ -1,14 +1,18 @@ package tui import ( - "fmt" + "regexp" "sort" - "strings" "github.com/charmbracelet/bubbles/list" "golang.org/x/exp/maps" ) +var ( + reRefs, _ = regexp.Compile(`^\w+Refs$`) + reSelector, _ = regexp.Compile(`^\w+Selector$`) +) + // Example is a struct that holds the details of an example type Example struct { FileName string @@ -35,7 +39,7 @@ type Example struct { Name string `yaml:"name"` } `yaml:"metadata"` Spec struct { - ForProvider map[interface{}]interface{} `yaml:"forProvider"` + ForProvider map[string]interface{} `yaml:"forProvider"` } `yaml:"spec"` } @@ -82,37 +86,83 @@ func (e Example) DependenciesFilesList() []string { return list } -// FindForProvider returns a map of all the fields containing the pattern -func (e *Example) FindForProvider(pattern string) map[string]bool { - // FindForProvider Recursively search all fields containing $parameter in the ForProvider.Spec - - maps := make(map[string]bool) +// FindSelectorsAndRefs returns the selectors and refs of the example +func (e *Example) FindSelectorsAndRefs() (map[string]bool, map[string]bool) { + mapsSelector := make(map[string]bool) + mapsRefs := make(map[string]bool) for k, v := range e.Spec.ForProvider { - // TODO use regex to find the pattern - if strings.Contains(fmt.Sprintf("%v", k), pattern) { + if reSelector.MatchString(k) { + if _, ok := v.(map[string]interface{}); ok { + m := getSelector(v.(map[string]interface{})) + maps.Copy(mapsSelector, m) + } + } else if reRefs.MatchString(k) { + for _, v2 := range v.([]interface{}) { + m, ok := v2.(map[string]interface{}) + if !ok { + continue + } - switch pattern { - case "Selector": - if v.(map[string]interface{})["matchLabels"].(map[string]interface{})["testing.upbound.io/example-name"] != nil { - maps[v.(map[string]interface{})["matchLabels"].(map[string]interface{})["testing.upbound.io/example-name"].(string)] = true + if name, ok := m["name"].(string); ok { + mapsRefs[name] = true } - case "Refs": - for _, v2 := range v.([]interface{}) { - m, ok := v2.(map[string]interface{}) - if !ok { - continue - } + } + } else { + if _, ok := v.([]interface{}); ok { + isArrayInterface(v.([]interface{}), mapsSelector, mapsRefs) + } + } + } + + return mapsSelector, mapsRefs +} - if name, ok := m["name"].(string); ok { - maps[name] = true +func isArrayInterface(i []interface{}, mapsSelector map[string]bool, mapsRefs map[string]bool) { + for _, a := range i { + if _, ok := a.(map[string]interface{}); ok { + for k, v := range a.(map[string]interface{}) { + if reSelector.MatchString(k) { + if _, ok := v.(map[string]interface{}); ok { + m := getSelector(v.(map[string]interface{})) + maps.Copy(mapsSelector, m) + } + } else if reRefs.MatchString(k) { + for _, v2 := range v.([]interface{}) { + m, ok := v2.(map[string]interface{}) + if !ok { + continue + } + + if name, ok := m["name"].(string); ok { + mapsRefs[name] = true + } + } + } else { + if _, ok := v.([]interface{}); ok { + isArrayInterface(v.([]interface{}), mapsSelector, mapsRefs) } } } + } + } +} +func getSelector(v map[string]interface{}) map[string]bool { + m := make(map[string]bool) + if _, ok := v["matchLabels"].(map[string]interface{}); !ok { + return nil + } + matchLabels := v["matchLabels"].(map[string]interface{}) + exampleName := matchLabels["testing.upbound.io/example-name"] + if exampleName != nil { + if _, ok := exampleName.(string); ok { + m[exampleName.(string)] = true + } else { + return nil } } - return maps + return m } // FindDependencies Find Selector and Ref in all files examples