mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-03-10 01:19:29 +00:00
Several packages are only used while running the e2e suite. These packages are less important to update, as the they can not influence the final executable that is part of the Ceph-CSI container-image. By moving these dependencies out of the main Ceph-CSI go.mod, it is easier to identify if a reported CVE affects Ceph-CSI, or only the testing (like most of the Kubernetes CVEs). Signed-off-by: Niels de Vos <ndevos@ibm.com>
122 lines
3.4 KiB
Go
122 lines
3.4 KiB
Go
/*
|
|
Copyright 2024 The Kubernetes 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 clientcmd
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
// recursively merges src into dst:
|
|
// - non-pointer struct fields with any exported fields are recursively merged
|
|
// - non-pointer struct fields with only unexported fields prefer src if the field is non-zero
|
|
// - maps are shallow merged with src keys taking priority over dst
|
|
// - non-zero src fields encountered during recursion that are not maps or structs overwrite and recursion stops
|
|
func merge[T any](dst, src *T) error {
|
|
if dst == nil {
|
|
return fmt.Errorf("cannot merge into nil pointer")
|
|
}
|
|
if src == nil {
|
|
return nil
|
|
}
|
|
return mergeValues(nil, reflect.ValueOf(dst).Elem(), reflect.ValueOf(src).Elem())
|
|
}
|
|
|
|
func mergeValues(fieldNames []string, dst, src reflect.Value) error {
|
|
dstType := dst.Type()
|
|
// no-op if we can't read the src
|
|
if !src.IsValid() {
|
|
return nil
|
|
}
|
|
// sanity check types match
|
|
if srcType := src.Type(); dstType != srcType {
|
|
return fmt.Errorf("cannot merge mismatched types (%s, %s) at %s", dstType, srcType, strings.Join(fieldNames, "."))
|
|
}
|
|
|
|
switch dstType.Kind() {
|
|
case reflect.Struct:
|
|
if hasExportedField(dstType) {
|
|
// recursively merge
|
|
for i, n := 0, dstType.NumField(); i < n; i++ {
|
|
if err := mergeValues(append(fieldNames, dstType.Field(i).Name), dst.Field(i), src.Field(i)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
} else if dst.CanSet() {
|
|
// If all fields are unexported, overwrite with src.
|
|
// Using src.IsZero() would make more sense but that's not what mergo did.
|
|
dst.Set(src)
|
|
}
|
|
|
|
case reflect.Map:
|
|
if dst.CanSet() && !src.IsZero() {
|
|
// initialize dst if needed
|
|
if dst.IsZero() {
|
|
dst.Set(reflect.MakeMap(dstType))
|
|
}
|
|
// shallow-merge overwriting dst keys with src keys
|
|
for _, mapKey := range src.MapKeys() {
|
|
dst.SetMapIndex(mapKey, src.MapIndex(mapKey))
|
|
}
|
|
}
|
|
|
|
case reflect.Slice:
|
|
if dst.CanSet() && src.Len() > 0 {
|
|
// overwrite dst with non-empty src slice
|
|
dst.Set(src)
|
|
}
|
|
|
|
case reflect.Pointer:
|
|
if dst.CanSet() && !src.IsZero() {
|
|
// overwrite dst with non-zero values for other types
|
|
if dstType.Elem().Kind() == reflect.Struct {
|
|
// use struct pointer as-is
|
|
dst.Set(src)
|
|
} else {
|
|
// shallow-copy non-struct pointer (interfaces, primitives, etc)
|
|
dst.Set(reflect.New(dstType.Elem()))
|
|
dst.Elem().Set(src.Elem())
|
|
}
|
|
}
|
|
|
|
default:
|
|
if dst.CanSet() && !src.IsZero() {
|
|
// overwrite dst with non-zero values for other types
|
|
dst.Set(src)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// hasExportedField returns true if the given type has any exported fields,
|
|
// or if it has any anonymous/embedded struct fields with exported fields
|
|
func hasExportedField(dstType reflect.Type) bool {
|
|
for i, n := 0, dstType.NumField(); i < n; i++ {
|
|
field := dstType.Field(i)
|
|
if field.Anonymous && field.Type.Kind() == reflect.Struct {
|
|
if hasExportedField(dstType.Field(i).Type) {
|
|
return true
|
|
}
|
|
} else if len(field.PkgPath) == 0 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|