github.com/grahambrereton-form3/tilt@v0.10.18/internal/k8s/extract.go (about)

     1  package k8s
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  
     7  	v1 "k8s.io/api/core/v1"
     8  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
     9  )
    10  
    11  func ExtractPods(obj interface{}) ([]*v1.PodSpec, error) {
    12  	extracted, err := newExtractor(reflect.TypeOf(v1.PodSpec{})).extractPointersFrom(obj)
    13  	if err != nil {
    14  		return nil, err
    15  	}
    16  
    17  	result := make([]*v1.PodSpec, len(extracted))
    18  	for i, e := range extracted {
    19  		c, ok := e.(*v1.PodSpec)
    20  		if !ok {
    21  			return nil, fmt.Errorf("extractPods: expected Pod, actual %T", e)
    22  		}
    23  		result[i] = c
    24  	}
    25  	return result, nil
    26  }
    27  
    28  func ExtractPodTemplateSpec(obj interface{}) ([]*v1.PodTemplateSpec, error) {
    29  	extracted, err := newExtractor(reflect.TypeOf(v1.PodTemplateSpec{})).extractPointersFrom(obj)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	result := make([]*v1.PodTemplateSpec, len(extracted))
    35  	for i, e := range extracted {
    36  		c, ok := e.(*v1.PodTemplateSpec)
    37  		if !ok {
    38  			return nil, fmt.Errorf("extractPods: expected Pod, actual %T", e)
    39  		}
    40  		result[i] = c
    41  	}
    42  	return result, nil
    43  }
    44  
    45  func extractObjectMetas(obj interface{}, filter func(v reflect.Value) bool) ([]*metav1.ObjectMeta, error) {
    46  	extracted, err := newExtractor(reflect.TypeOf(metav1.ObjectMeta{})).
    47  		withFilter(filter).
    48  		extractPointersFrom(obj)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	result := make([]*metav1.ObjectMeta, len(extracted))
    54  	for i, e := range extracted {
    55  		c, ok := e.(*metav1.ObjectMeta)
    56  		if !ok {
    57  			return nil, fmt.Errorf("ExtractObjectMetas: expected ObjectMeta, actual %T", e)
    58  		}
    59  		result[i] = c
    60  	}
    61  	return result, nil
    62  }
    63  
    64  func extractSelectors(obj interface{}, filter func(v reflect.Value) bool) ([]*metav1.LabelSelector, error) {
    65  	extracted, err := newExtractor(reflect.TypeOf(metav1.LabelSelector{})).
    66  		withFilter(filter).
    67  		extractPointersFrom(obj)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  
    72  	result := make([]*metav1.LabelSelector, len(extracted))
    73  	for i, e := range extracted {
    74  		c, ok := e.(*metav1.LabelSelector)
    75  		if !ok {
    76  			return nil, fmt.Errorf("ExtractSelectors: expected LabelSelector, actual %T", e)
    77  		}
    78  		result[i] = c
    79  	}
    80  	return result, nil
    81  }
    82  
    83  func extractEnvVars(obj interface{}) ([]*v1.EnvVar, error) {
    84  	extracted, err := newExtractor(reflect.TypeOf(v1.EnvVar{})).extractPointersFrom(obj)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	result := make([]*v1.EnvVar, len(extracted))
    90  	for i, e := range extracted {
    91  		ev, ok := e.(*v1.EnvVar)
    92  		if !ok {
    93  			return nil, fmt.Errorf("extractEnvVars: expected %T, actual %T", v1.EnvVar{}, e)
    94  		}
    95  		result[i] = ev
    96  	}
    97  	return result, nil
    98  }
    99  
   100  func extractContainers(obj interface{}) ([]*v1.Container, error) {
   101  	extracted, err := newExtractor(reflect.TypeOf(v1.Container{})).extractPointersFrom(obj)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	result := make([]*v1.Container, len(extracted))
   107  	for i, e := range extracted {
   108  		c, ok := e.(*v1.Container)
   109  		if !ok {
   110  			return nil, fmt.Errorf("extractContainers: expected Container, actual %T", e)
   111  		}
   112  		result[i] = c
   113  	}
   114  	return result, nil
   115  }
   116  
   117  type extractor struct {
   118  	// The type we want to return pointers to
   119  	pType reflect.Type
   120  
   121  	// Return true to visit the value, or false to skip it.
   122  	filter func(v reflect.Value) bool
   123  }
   124  
   125  func newExtractor(pType reflect.Type) extractor {
   126  	return extractor{
   127  		pType:  pType,
   128  		filter: NoFilter,
   129  	}
   130  }
   131  
   132  func (e extractor) withFilter(f func(v reflect.Value) bool) extractor {
   133  	e.filter = f
   134  	return e
   135  }
   136  
   137  // Get pointers to all the pType structs in this object.
   138  func (e extractor) extractPointersFrom(obj interface{}) ([]interface{}, error) {
   139  	v := reflect.ValueOf(obj)
   140  	result := make([]interface{}, 0)
   141  
   142  	// Recursively iterate over the struct fields.
   143  	var extract func(v reflect.Value) error
   144  	extract = func(v reflect.Value) error {
   145  		if !e.filter(v) {
   146  			return nil
   147  		}
   148  
   149  		switch v.Kind() {
   150  		case reflect.Ptr, reflect.Interface:
   151  			if v.IsNil() {
   152  				return nil
   153  			}
   154  			return extract(v.Elem())
   155  
   156  		case reflect.Struct:
   157  			if v.Type() == e.pType {
   158  				if !v.CanAddr() {
   159  					return fmt.Errorf("Error addressing: %v", v)
   160  				}
   161  				result = append(result, v.Addr().Interface())
   162  				return nil
   163  			}
   164  
   165  			for i := 0; i < v.NumField(); i++ {
   166  				field := v.Field(i)
   167  				err := extract(field)
   168  				if err != nil {
   169  					return err
   170  				}
   171  			}
   172  			return nil
   173  
   174  		case reflect.Slice:
   175  			for i := 0; i < v.Len(); i++ {
   176  				field := v.Index(i)
   177  				err := extract(field)
   178  				if err != nil {
   179  					return err
   180  				}
   181  			}
   182  			return nil
   183  
   184  		}
   185  		return nil
   186  	}
   187  
   188  	err := extract(v)
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  	return result, nil
   193  }
   194  
   195  var NoFilter = func(v reflect.Value) bool {
   196  	return true
   197  }