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  }