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 }