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 }