github.com/splunk/dan1-qbec@v0.7.3/internal/eval/object-extract.go (about) 1 /* 2 Copyright 2019 Splunk Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package eval 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "reflect" 23 24 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 25 ) 26 27 func tolerantJSON(data interface{}) string { 28 b, _ := json.MarshalIndent(data, "", " ") 29 return string(b) 30 } 31 32 func str(data map[string]interface{}, attr string) string { 33 v := data[attr] 34 if s, ok := v.(string); ok { 35 return s 36 } 37 return "" 38 } 39 40 type rawObjectType = int 41 42 const ( 43 unknownType rawObjectType = iota 44 leafType 45 arrayType 46 ) 47 48 func getRawObjectType(data map[string]interface{}) rawObjectType { 49 kind := str(data, "kind") 50 apiVersion := str(data, "apiVersion") 51 if kind == "" || apiVersion == "" { 52 return unknownType 53 } 54 _, isArray := data["items"].([]interface{}) 55 if isArray { // kubernetes list, not primitive 56 return arrayType 57 } 58 return leafType 59 } 60 61 func walk(data interface{}) ([]map[string]interface{}, error) { 62 return walkObjects("$", data, data) 63 } 64 65 func walkObjects(path string, data interface{}, ctx interface{}) ([]map[string]interface{}, error) { 66 var ret []map[string]interface{} 67 if data == nil { 68 return ret, nil 69 } 70 switch t := data.(type) { 71 case []interface{}: 72 for i, o := range t { 73 objects, err := walkObjects(fmt.Sprintf("%s[%d]", path, i), o, data) 74 if err != nil { 75 return nil, err 76 } 77 ret = append(ret, objects...) 78 } 79 case map[string]interface{}: 80 rt := getRawObjectType(t) 81 switch rt { 82 case arrayType: 83 array := t["items"].([]interface{}) 84 objects, err := walkObjects(fmt.Sprintf("%s.items", path), array, data) 85 if err != nil { 86 return nil, err 87 } 88 ret = append(ret, objects...) 89 case leafType: 90 u := unstructured.Unstructured{Object: t} 91 name := u.GetName() 92 genName := u.GetGenerateName() 93 if name == "" && genName == "" { 94 return nil, fmt.Errorf("object (%v) did not have a name at path %q, (json=\n%s)", 95 reflect.TypeOf(data), 96 path, 97 tolerantJSON(data)) 98 } 99 ret = append(ret, t) 100 default: 101 for k, v := range t { 102 objects, err := walkObjects(fmt.Sprintf("%s.%s", path, k), v, data) 103 if err != nil { 104 return nil, err 105 } 106 ret = append(ret, objects...) 107 } 108 } 109 default: 110 return nil, fmt.Errorf("unexpected type for object (%v) at path %q, (json=\n%s)", 111 reflect.TypeOf(data), 112 path, 113 tolerantJSON(ctx)) 114 } 115 return ret, nil 116 }