pkg/model/k8s/configsource/reflect.go (82 lines of code) (raw):
// Copyright 2024 Google LLC
//
// 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 configsource
import (
"fmt"
"reflect"
"slices"
"strings"
"github.com/GoogleCloudPlatform/khi/pkg/log/structure/merger"
)
const MAXIMUM_STRUCTURE_DEPTH = 100
func FromResourceTypeReflection(resourceType interface{}) (*merger.MergeConfigResolver, error) {
result := &merger.MergeConfigResolver{
MergeStrategies: make(map[string]merger.MergeArrayStrategy),
MergeKeys: map[string]string{},
}
refType := reflect.TypeOf(resourceType)
err := resolveTypeRecursive("", refType, result)
if err != nil {
return nil, err
}
return result, nil
}
func resolveTypeRecursive(path string, current reflect.Type, resolver *merger.MergeConfigResolver) error {
if strings.Count(path, ".") > MAXIMUM_STRUCTURE_DEPTH {
return fmt.Errorf("maximum structure depth reached. is this a recursive structure?")
}
kind := current.Kind()
switch kind {
case reflect.Struct:
fieldCount := current.NumField()
for i := 0; i < fieldCount; i++ {
field := current.Field(i)
fieldKind := field.Type.Kind()
json, ok := field.Tag.Lookup("json")
if !ok {
continue
}
jsonSegments := strings.Split(json, ",")
if slices.Contains[[]string](jsonSegments, "inline") {
err := resolveTypeRecursive(path, field.Type, resolver)
if err != nil {
return err
}
}
jsonName := jsonSegments[0]
jsonFieldPath := fmt.Sprintf("%s.%s", path, jsonName)
if fieldKind == reflect.Slice || fieldKind == reflect.Array {
patchStrategy, ok := field.Tag.Lookup("patchStrategy")
if !ok || patchStrategy != "merge" {
resolver.MergeStrategies[jsonFieldPath] = merger.MergeStrategyReplace
} else {
resolver.MergeStrategies[jsonFieldPath] = merger.MergeStrategyMerge
patchMergeKey, ok := field.Tag.Lookup("patchMergeKey")
if ok {
resolver.MergeKeys[jsonFieldPath] = patchMergeKey
} else {
resolver.MergeKeys[jsonFieldPath] = ""
}
}
err := resolveTypeRecursive(jsonFieldPath+"[]", field.Type.Elem(), resolver)
if err != nil {
return err
}
} else if fieldKind == reflect.Struct || fieldKind == reflect.Ptr {
fieldType := field.Type
if fieldKind == reflect.Ptr {
fieldType = field.Type.Elem()
}
err := resolveTypeRecursive(jsonFieldPath, fieldType, resolver)
if err != nil {
return err
}
}
}
return nil
case reflect.Chan:
return fmt.Errorf("unsupported kind %s", current.Kind())
case reflect.Ptr:
return fmt.Errorf("unsupported kind %s", current.Kind())
default:
return nil
}
}