github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/strategicpatch/meta.go (about)

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     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 strategicpatch
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"reflect"
    23  
    24  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/mergepatch"
    25  	forkedjson "github.com/spotmaxtech/k8s-apimachinery-v0260/third_party/forked/golang/json"
    26  	openapi "k8s.io/kube-openapi/pkg/util/proto"
    27  )
    28  
    29  type PatchMeta struct {
    30  	patchStrategies []string
    31  	patchMergeKey   string
    32  }
    33  
    34  func (pm *PatchMeta) GetPatchStrategies() []string {
    35  	if pm.patchStrategies == nil {
    36  		return []string{}
    37  	}
    38  	return pm.patchStrategies
    39  }
    40  
    41  func (pm *PatchMeta) SetPatchStrategies(ps []string) {
    42  	pm.patchStrategies = ps
    43  }
    44  
    45  func (pm *PatchMeta) GetPatchMergeKey() string {
    46  	return pm.patchMergeKey
    47  }
    48  
    49  func (pm *PatchMeta) SetPatchMergeKey(pmk string) {
    50  	pm.patchMergeKey = pmk
    51  }
    52  
    53  type LookupPatchMeta interface {
    54  	// LookupPatchMetadataForStruct gets subschema and the patch metadata (e.g. patch strategy and merge key) for map.
    55  	LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error)
    56  	// LookupPatchMetadataForSlice get subschema and the patch metadata for slice.
    57  	LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error)
    58  	// Get the type name of the field
    59  	Name() string
    60  }
    61  
    62  type PatchMetaFromStruct struct {
    63  	T reflect.Type
    64  }
    65  
    66  func NewPatchMetaFromStruct(dataStruct interface{}) (PatchMetaFromStruct, error) {
    67  	t, err := getTagStructType(dataStruct)
    68  	return PatchMetaFromStruct{T: t}, err
    69  }
    70  
    71  var _ LookupPatchMeta = PatchMetaFromStruct{}
    72  
    73  func (s PatchMetaFromStruct) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) {
    74  	fieldType, fieldPatchStrategies, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadataForStruct(s.T, key)
    75  	if err != nil {
    76  		return nil, PatchMeta{}, err
    77  	}
    78  
    79  	return PatchMetaFromStruct{T: fieldType},
    80  		PatchMeta{
    81  			patchStrategies: fieldPatchStrategies,
    82  			patchMergeKey:   fieldPatchMergeKey,
    83  		}, nil
    84  }
    85  
    86  func (s PatchMetaFromStruct) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) {
    87  	subschema, patchMeta, err := s.LookupPatchMetadataForStruct(key)
    88  	if err != nil {
    89  		return nil, PatchMeta{}, err
    90  	}
    91  	elemPatchMetaFromStruct := subschema.(PatchMetaFromStruct)
    92  	t := elemPatchMetaFromStruct.T
    93  
    94  	var elemType reflect.Type
    95  	switch t.Kind() {
    96  	// If t is an array or a slice, get the element type.
    97  	// If element is still an array or a slice, return an error.
    98  	// Otherwise, return element type.
    99  	case reflect.Array, reflect.Slice:
   100  		elemType = t.Elem()
   101  		if elemType.Kind() == reflect.Array || elemType.Kind() == reflect.Slice {
   102  			return nil, PatchMeta{}, errors.New("unexpected slice of slice")
   103  		}
   104  	// If t is an pointer, get the underlying element.
   105  	// If the underlying element is neither an array nor a slice, the pointer is pointing to a slice,
   106  	// e.g. https://github.com/kubernetes/kubernetes/blob/bc22e206c79282487ea0bf5696d5ccec7e839a76/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch_test.go#L2782-L2822
   107  	// If the underlying element is either an array or a slice, return its element type.
   108  	case reflect.Pointer:
   109  		t = t.Elem()
   110  		if t.Kind() == reflect.Array || t.Kind() == reflect.Slice {
   111  			t = t.Elem()
   112  		}
   113  		elemType = t
   114  	default:
   115  		return nil, PatchMeta{}, fmt.Errorf("expected slice or array type, but got: %s", s.T.Kind().String())
   116  	}
   117  
   118  	return PatchMetaFromStruct{T: elemType}, patchMeta, nil
   119  }
   120  
   121  func (s PatchMetaFromStruct) Name() string {
   122  	return s.T.Kind().String()
   123  }
   124  
   125  func getTagStructType(dataStruct interface{}) (reflect.Type, error) {
   126  	if dataStruct == nil {
   127  		return nil, mergepatch.ErrBadArgKind(struct{}{}, nil)
   128  	}
   129  
   130  	t := reflect.TypeOf(dataStruct)
   131  	// Get the underlying type for pointers
   132  	if t.Kind() == reflect.Pointer {
   133  		t = t.Elem()
   134  	}
   135  
   136  	if t.Kind() != reflect.Struct {
   137  		return nil, mergepatch.ErrBadArgKind(struct{}{}, dataStruct)
   138  	}
   139  
   140  	return t, nil
   141  }
   142  
   143  func GetTagStructTypeOrDie(dataStruct interface{}) reflect.Type {
   144  	t, err := getTagStructType(dataStruct)
   145  	if err != nil {
   146  		panic(err)
   147  	}
   148  	return t
   149  }
   150  
   151  type PatchMetaFromOpenAPI struct {
   152  	Schema openapi.Schema
   153  }
   154  
   155  func NewPatchMetaFromOpenAPI(s openapi.Schema) PatchMetaFromOpenAPI {
   156  	return PatchMetaFromOpenAPI{Schema: s}
   157  }
   158  
   159  var _ LookupPatchMeta = PatchMetaFromOpenAPI{}
   160  
   161  func (s PatchMetaFromOpenAPI) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) {
   162  	if s.Schema == nil {
   163  		return nil, PatchMeta{}, nil
   164  	}
   165  	kindItem := NewKindItem(key, s.Schema.GetPath())
   166  	s.Schema.Accept(kindItem)
   167  
   168  	err := kindItem.Error()
   169  	if err != nil {
   170  		return nil, PatchMeta{}, err
   171  	}
   172  	return PatchMetaFromOpenAPI{Schema: kindItem.subschema},
   173  		kindItem.patchmeta, nil
   174  }
   175  
   176  func (s PatchMetaFromOpenAPI) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) {
   177  	if s.Schema == nil {
   178  		return nil, PatchMeta{}, nil
   179  	}
   180  	sliceItem := NewSliceItem(key, s.Schema.GetPath())
   181  	s.Schema.Accept(sliceItem)
   182  
   183  	err := sliceItem.Error()
   184  	if err != nil {
   185  		return nil, PatchMeta{}, err
   186  	}
   187  	return PatchMetaFromOpenAPI{Schema: sliceItem.subschema},
   188  		sliceItem.patchmeta, nil
   189  }
   190  
   191  func (s PatchMetaFromOpenAPI) Name() string {
   192  	schema := s.Schema
   193  	return schema.GetName()
   194  }