github.com/openebs/api@v1.12.0/pkg/util/unstructured.go (about) 1 /* 2 Copyright 2015 The Kubernetes Authors. 3 Copyright 2020 The OpenEBS Authors. 4 5 Licensed under the Apache License, Version 2.0 (the "License"); 6 you may not use this file except in compliance with the License. 7 You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11 Unless required by applicable law or agreed to in writing, software 12 distributed under the License is distributed on an "AS IS" BASIS, 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 See the License for the specific language governing permissions and 15 limitations under the License. 16 */ 17 18 // Package util provides functions based on k8s.io/apimachinery/pkg/apis/meta/v1/unstructured 19 // They are copied here to make them exported. 20 // 21 // TODO 22 // Check if it makes sense to import the entire unstructured package of 23 // k8s.io/apimachinery/pkg/apis/meta/v1/unstructured versus. copying 24 // 25 // TODO 26 // Move to maya/pkg/unstructured/v1alpha1 as helpers.go 27 package util 28 29 import ( 30 "fmt" 31 "strings" 32 33 "k8s.io/apimachinery/pkg/util/json" 34 ) 35 36 // GetNestedField returns a nested field from the provided map 37 func GetNestedField(obj map[string]interface{}, fields ...string) interface{} { 38 var val interface{} = obj 39 for _, field := range fields { 40 if _, ok := val.(map[string]interface{}); !ok { 41 return nil 42 } 43 val = val.(map[string]interface{})[field] 44 } 45 return val 46 } 47 48 // GetNestedFieldInto converts a nested field to requested type from the provided map 49 func GetNestedFieldInto(out interface{}, obj map[string]interface{}, fields ...string) error { 50 objMap := GetNestedField(obj, fields...) 51 if objMap == nil { 52 // If field has no value, leave `out` as is. 53 return nil 54 } 55 // Decode into the requested output type. 56 data, err := json.Marshal(objMap) 57 if err != nil { 58 return fmt.Errorf("can't encode nested field %v: %v", strings.Join(fields, "."), err) 59 } 60 if err := json.Unmarshal(data, out); err != nil { 61 return fmt.Errorf("can't decode nested field %v into type %T: %v", strings.Join(fields, "."), out, err) 62 } 63 return nil 64 } 65 66 // GetNestedString returns a nested string from the provided map 67 func GetNestedString(obj map[string]interface{}, fields ...string) string { 68 if obj == nil { 69 return "" 70 } 71 if str, ok := GetNestedField(obj, fields...).(string); ok { 72 return str 73 } 74 return "" 75 } 76 77 // GetNestedArray returns an nested array from the provided map 78 func GetNestedArray(obj map[string]interface{}, fields ...string) []interface{} { 79 if arr, ok := GetNestedField(obj, fields...).([]interface{}); ok { 80 return arr 81 } 82 return nil 83 } 84 85 // GetNestedInt64 returns an nested int64 from the provided map 86 func GetNestedInt64(obj map[string]interface{}, fields ...string) int64 { 87 if str, ok := GetNestedField(obj, fields...).(int64); ok { 88 return str 89 } 90 return 0 91 } 92 93 // GetNestedInt64Pointer returns a nested int64 pointer from the provided map 94 func GetNestedInt64Pointer(obj map[string]interface{}, fields ...string) *int64 { 95 nested := GetNestedField(obj, fields...) 96 switch n := nested.(type) { 97 case int64: 98 return &n 99 case *int64: 100 return n 101 default: 102 return nil 103 } 104 } 105 106 // GetNestedSlice returns a nested slice from the provided map 107 func GetNestedSlice(obj map[string]interface{}, fields ...string) []string { 108 if m, ok := GetNestedField(obj, fields...).([]interface{}); ok { 109 strSlice := make([]string, 0, len(m)) 110 for _, v := range m { 111 if str, ok := v.(string); ok { 112 strSlice = append(strSlice, str) 113 } 114 } 115 return strSlice 116 } 117 return nil 118 } 119 120 // GetNestedMap returns a nested map from the provided map 121 func GetNestedMap(obj map[string]interface{}, fields ...string) map[string]string { 122 if m, ok := GetNestedField(obj, fields...).(map[string]interface{}); ok { 123 strMap := make(map[string]string, len(m)) 124 for k, v := range m { 125 if str, ok := v.(string); ok { 126 strMap[k] = str 127 } 128 } 129 return strMap 130 } 131 return nil 132 } 133 134 // SetNestedField sets a nested field into the provided map 135 func SetNestedField(obj map[string]interface{}, value interface{}, fields ...string) { 136 if len(fields) == 0 || obj == nil { 137 return 138 } 139 140 m := obj 141 142 if len(fields) > 1 { 143 for _, field := range fields[0 : len(fields)-1] { 144 if _, ok := m[field].(map[string]interface{}); !ok { 145 m[field] = make(map[string]interface{}) 146 } 147 m = m[field].(map[string]interface{}) 148 } 149 } 150 m[fields[len(fields)-1]] = value 151 } 152 153 // DeleteNestedField deletes a nested field from the provided map 154 func DeleteNestedField(obj map[string]interface{}, fields ...string) { 155 if len(fields) == 0 || obj == nil { 156 return 157 } 158 159 m := obj 160 if len(fields) > 1 { 161 for _, field := range fields[0 : len(fields)-1] { 162 if _, ok := m[field].(map[string]interface{}); !ok { 163 m[field] = make(map[string]interface{}) 164 } 165 m = m[field].(map[string]interface{}) 166 } 167 } 168 delete(m, fields[len(fields)-1]) 169 } 170 171 // SetNestedSlice sets a nested slice from the provided map 172 func SetNestedSlice(obj map[string]interface{}, value []string, fields ...string) { 173 m := make([]interface{}, 0, len(value)) 174 for _, v := range value { 175 m = append(m, v) 176 } 177 SetNestedField(obj, m, fields...) 178 } 179 180 // SetNestedMap sets a nested map from the provided map 181 func SetNestedMap(obj map[string]interface{}, value map[string]string, fields ...string) { 182 m := make(map[string]interface{}, len(value)) 183 for k, v := range value { 184 m[k] = v 185 } 186 SetNestedField(obj, m, fields...) 187 } 188 189 // MergeMapOfStrings will merge the map from src to dest 190 func MergeMapOfStrings(dest map[string]string, src map[string]string) bool { 191 // nil check as storing into a nil map panics 192 if dest == nil { 193 return false 194 } 195 196 for k, v := range src { 197 dest[k] = v 198 } 199 200 return true 201 } 202 203 // MergeMapOfObjects will merge the map from src to dest. It will override 204 // existing keys of the destination 205 func MergeMapOfObjects(dest map[string]interface{}, src map[string]interface{}) bool { 206 // nil check as storing into a nil map panics 207 if dest == nil { 208 return false 209 } 210 211 for k, v := range src { 212 dest[k] = v 213 } 214 215 return true 216 } 217 218 // GetMapOfStrings gets the direct value from the passed obj & the field path 219 // The value returned should be expected of the form map[string]string 220 func GetMapOfStrings(obj map[string]interface{}, field string) map[string]string { 221 if m, ok := obj[field].(map[string]string); ok { 222 return m 223 } 224 return nil 225 }