github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/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 extractServiceSpecs(obj interface{}) ([]*v1.ServiceSpec, error) {
    84  	extracted, err := newExtractor(reflect.TypeOf(v1.ServiceSpec{})).
    85  		extractPointersFrom(obj)
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  
    90  	result := make([]*v1.ServiceSpec, len(extracted))
    91  	for i, e := range extracted {
    92  		c, ok := e.(*v1.ServiceSpec)
    93  		if !ok {
    94  			return nil, fmt.Errorf("ExtractSelectors: expected ServiceSpec, actual %T", e)
    95  		}
    96  		result[i] = c
    97  	}
    98  	return result, nil
    99  }
   100  
   101  func extractEnvVars(obj interface{}) ([]*v1.EnvVar, error) {
   102  	extracted, err := newExtractor(reflect.TypeOf(v1.EnvVar{})).extractPointersFrom(obj)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  
   107  	result := make([]*v1.EnvVar, len(extracted))
   108  	for i, e := range extracted {
   109  		ev, ok := e.(*v1.EnvVar)
   110  		if !ok {
   111  			return nil, fmt.Errorf("extractEnvVars: expected %T, actual %T", v1.EnvVar{}, e)
   112  		}
   113  		result[i] = ev
   114  	}
   115  	return result, nil
   116  }
   117  
   118  func extractContainers(obj interface{}) ([]*v1.Container, error) {
   119  	extracted, err := newExtractor(reflect.TypeOf(v1.Container{})).extractPointersFrom(obj)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	result := make([]*v1.Container, len(extracted))
   125  	for i, e := range extracted {
   126  		c, ok := e.(*v1.Container)
   127  		if !ok {
   128  			return nil, fmt.Errorf("extractContainers: expected Container, actual %T", e)
   129  		}
   130  		result[i] = c
   131  	}
   132  	return result, nil
   133  }
   134  
   135  type extractor struct {
   136  	// The type we want to return pointers to
   137  	pType reflect.Type
   138  
   139  	// Return true to visit the value, or false to skip it.
   140  	filter func(v reflect.Value) bool
   141  }
   142  
   143  func newExtractor(pType reflect.Type) extractor {
   144  	return extractor{
   145  		pType:  pType,
   146  		filter: NoFilter,
   147  	}
   148  }
   149  
   150  func (e extractor) withFilter(f func(v reflect.Value) bool) extractor {
   151  	e.filter = f
   152  	return e
   153  }
   154  
   155  // Get pointers to all the pType structs in this object.
   156  func (e extractor) extractPointersFrom(obj interface{}) ([]interface{}, error) {
   157  	v := reflect.ValueOf(obj)
   158  	result := make([]interface{}, 0)
   159  
   160  	// Recursively iterate over the struct fields.
   161  	var extract func(v reflect.Value) error
   162  	extract = func(v reflect.Value) error {
   163  		if !e.filter(v) {
   164  			return nil
   165  		}
   166  
   167  		switch v.Kind() {
   168  		case reflect.Ptr, reflect.Interface:
   169  			if v.IsNil() {
   170  				return nil
   171  			}
   172  			return extract(v.Elem())
   173  
   174  		case reflect.Struct:
   175  			if v.Type() == e.pType {
   176  				if !v.CanAddr() {
   177  					return fmt.Errorf("Error addressing: %v", v)
   178  				}
   179  				result = append(result, v.Addr().Interface())
   180  				return nil
   181  			}
   182  
   183  			for i := 0; i < v.NumField(); i++ {
   184  				field := v.Field(i)
   185  				err := extract(field)
   186  				if err != nil {
   187  					return err
   188  				}
   189  			}
   190  			return nil
   191  
   192  		case reflect.Slice:
   193  			for i := 0; i < v.Len(); i++ {
   194  				field := v.Index(i)
   195  				err := extract(field)
   196  				if err != nil {
   197  					return err
   198  				}
   199  			}
   200  			return nil
   201  
   202  		}
   203  		return nil
   204  	}
   205  
   206  	err := extract(v)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  	return result, nil
   211  }
   212  
   213  var NoFilter = func(v reflect.Value) bool {
   214  	return true
   215  }