github.com/tilt-dev/tilt@v0.36.0/internal/k8s/extract.go (about)

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