...

Source file src/github.com/chaos-mesh/chaos-mesh/api/genericwebhook/bfs.go

Documentation: github.com/chaos-mesh/chaos-mesh/api/genericwebhook

     1  // Copyright 2021 Chaos Mesh Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  
    16  package genericwebhook
    17  
    18  import (
    19  	"container/list"
    20  	"reflect"
    21  
    22  	"k8s.io/apimachinery/pkg/util/validation/field"
    23  )
    24  
    25  type fieldCallback func(path *field.Path, obj interface{}, field *reflect.StructField) bool
    26  
    27  type FieldWalker struct {
    28  	obj      interface{}
    29  	callback fieldCallback
    30  }
    31  
    32  func NewFieldWalker(obj interface{}, callback fieldCallback) *FieldWalker {
    33  	return &FieldWalker{
    34  		obj:      obj,
    35  		callback: callback,
    36  	}
    37  }
    38  
    39  type iterateNode struct {
    40  	Val   reflect.Value
    41  	Path  *field.Path
    42  	Field *reflect.StructField
    43  }
    44  
    45  func (w *FieldWalker) Walk() {
    46  	objVal := reflect.ValueOf(w.obj)
    47  
    48  	items := list.New()
    49  	items.PushBack(iterateNode{
    50  		Val:  objVal,
    51  		Path: nil,
    52  	})
    53  
    54  	for {
    55  		if items.Len() == 0 {
    56  			break
    57  		}
    58  
    59  		item := items.Front()
    60  		items.Remove(item)
    61  
    62  		node := item.Value.(iterateNode)
    63  		// If node is not the root node, then we need to check whether
    64  		// we need to iterate its children.
    65  		if node.Path != nil {
    66  			val := node.Val
    67  			if val.Kind() != reflect.Ptr {
    68  				// If it's not a pointer or a slice, then we need to
    69  				// take the address of it, to be able to modify it.
    70  				val = val.Addr()
    71  			}
    72  			if !w.callback(node.Path, val.Interface(), node.Field) {
    73  				continue
    74  			}
    75  		}
    76  
    77  		if node.Val.Kind() == reflect.Ptr && node.Val.IsZero() {
    78  			continue
    79  		}
    80  		objVal = reflect.Indirect(node.Val)
    81  		objType := objVal.Type()
    82  		switch objType.Kind() {
    83  		case reflect.Struct:
    84  			for i := 0; i < objVal.NumField(); i++ {
    85  				field := objType.Field(i)
    86  				fieldVal := objVal.Field(i)
    87  
    88  				// The field should be exported
    89  				if fieldVal.CanInterface() {
    90  					items.PushBack(iterateNode{
    91  						Val:   fieldVal,
    92  						Path:  node.Path.Child(field.Name),
    93  						Field: &field,
    94  					})
    95  				}
    96  			}
    97  		case reflect.Slice:
    98  			for i := 0; i < objVal.Len(); i++ {
    99  				items.PushBack(iterateNode{
   100  					Val:   objVal.Index(i),
   101  					Path:  node.Path.Index(i),
   102  					Field: nil,
   103  				})
   104  			}
   105  		}
   106  
   107  	}
   108  }
   109