@@ -18,6 +18,7 @@ package installer
1818
1919import (
2020 "context"
21+ "errors"
2122 "fmt"
2223 "strings"
2324
@@ -104,6 +105,18 @@ func SetupProxyController(mgr ctrl.Manager, trackingCache managedcache.TrackingC
104105 return nil
105106}
106107
108+ // workloadItem collects the fields needed to reconcile proxy env vars on a
109+ // single Deployment, DaemonSet, or StatefulSet without storing a reference to
110+ // the concrete typed object.
111+ type workloadItem struct {
112+ apiVersion string
113+ kind string
114+ name string
115+ namespace string
116+ podAnnotations map [string ]string
117+ containers []corev1.Container
118+ }
119+
107120// Reconcile reads the cluster-wide proxy configuration and applies the proxy
108121// environment variables to all managed workloads that carry the inject-proxy
109122// annotation.
@@ -116,131 +129,158 @@ func (c *ProxyController) Reconcile(ctx context.Context, _ ctrl.Request) (ctrl.R
116129 return ctrl.Result {}, fmt .Errorf ("fetching cluster-wide proxy: %w" , err )
117130 }
118131
119- var errs []error
120-
121- for _ , applyFn := range []func (context.Context , []corev1.EnvVar ) error {
122- c .applyToDeployments ,
123- c .applyToDaemonSets ,
124- c .applyToStatefulSets ,
125- } {
126- if err := applyFn (ctx , proxyVars ); err != nil {
127- errs = append (errs , err )
128- }
129- }
130-
131- if len (errs ) > 0 {
132- combined := errs [0 ]
133- for _ , e := range errs [1 :] {
134- combined = fmt .Errorf ("%w; %w" , combined , e )
135- }
136-
137- return ctrl.Result {}, combined
132+ if err := c .applyToAllWorkloads (ctx , proxyVars ); err != nil {
133+ return ctrl.Result {}, err
138134 }
139135
140136 log .Info ("Proxy environment variables reconciled" )
141137
142138 return ctrl.Result {}, nil
143139}
144140
145- func (c * ProxyController ) applyToDeployments (ctx context.Context , proxyVars []corev1.EnvVar ) error {
146- list := & appsv1.DeploymentList {}
147- if err := c .trackingCache .List (ctx , list ); err != nil {
148- return fmt .Errorf ("listing managed deployments: %w" , err )
149- }
150-
151- for i := range list .Items {
152- deploy := & list .Items [i ]
153- annotation , ok := deploy .Spec .Template .Annotations [ProxyInjectAnnotation ]
141+ // containerNamesFromAnnotation parses the comma-separated inject-proxy annotation value.
142+ func containerNamesFromAnnotation (annotation string ) []string {
143+ var names []string
154144
155- if ! ok {
156- continue
145+ for _ , name := range strings .Split (annotation , "," ) {
146+ if n := strings .TrimSpace (name ); n != "" {
147+ names = append (names , n )
157148 }
149+ }
158150
159- if err := c .applyProxyVars (ctx , "apps/v1" , "Deployment" , deploy .Name , deploy .Namespace , annotation , proxyVars ); err != nil {
160- return err
161- }
151+ return names
152+ }
153+
154+ // containerNamesFromSpec returns the names of all containers in a pod spec.
155+ func containerNamesFromSpec (containers []corev1.Container ) []string {
156+ names := make ([]string , len (containers ))
157+ for i , c := range containers {
158+ names [i ] = c .Name
162159 }
163160
164- return nil
161+ return names
165162}
166163
167- func (c * ProxyController ) applyToDaemonSets (ctx context.Context , proxyVars []corev1.EnvVar ) error {
168- list := & appsv1.DaemonSetList {}
169- if err := c .trackingCache .List (ctx , list ); err != nil {
170- return fmt .Errorf ("listing managed daemonsets: %w" , err )
164+ // applyToAllWorkloads collects all managed Deployments, DaemonSets, and
165+ // StatefulSets from the tracking cache and reconciles their proxy env vars.
166+ // It continues past per-object errors and returns all failures combined.
167+ func (c * ProxyController ) applyToAllWorkloads (ctx context.Context , proxyVars []corev1.EnvVar ) error {
168+ items , err := c .collectWorkloads (ctx )
169+ if err != nil {
170+ return err
171171 }
172172
173- for i := range list .Items {
174- ds := & list .Items [i ]
173+ var errs []error
174+
175+ for _ , item := range items {
176+ var containers []string
175177
176- annotation , ok := ds . Spec . Template . Annotations [ ProxyInjectAnnotation ]
178+ effectiveVars := proxyVars
177179
178- if ! ok {
179- continue
180+ annotation , hasAnnotation := item .podAnnotations [ProxyInjectAnnotation ]
181+ if hasAnnotation {
182+ containers = containerNamesFromAnnotation (annotation )
183+ } else {
184+ // No annotation: clear any stale proxy env vars we previously owned
185+ // so that opting out removes the env vars instead of leaving them.
186+ containers = containerNamesFromSpec (item .containers )
187+ effectiveVars = nil
180188 }
181189
182- if err := c .applyProxyVars (ctx , "apps/v1" , "DaemonSet" , ds . Name , ds . Namespace , annotation , proxyVars ); err != nil {
183- return err
190+ if err := c .applyProxyVars (ctx , item . apiVersion , item . kind , item . name , item . namespace , containers , effectiveVars ); err != nil {
191+ errs = append ( errs , err )
184192 }
185193 }
186194
187- return nil
195+ return errors . Join ( errs ... )
188196}
189197
190- func (c * ProxyController ) applyToStatefulSets (ctx context.Context , proxyVars []corev1.EnvVar ) error {
191- list := & appsv1.StatefulSetList {}
192- if err := c .trackingCache .List (ctx , list ); err != nil {
193- return fmt .Errorf ("listing managed statefulsets: %w" , err )
198+ // collectWorkloads lists all managed Deployments, DaemonSets, and StatefulSets
199+ // from the tracking cache and returns them as workloadItems.
200+ func (c * ProxyController ) collectWorkloads (ctx context.Context ) ([]workloadItem , error ) {
201+ var items []workloadItem
202+
203+ deployList := & appsv1.DeploymentList {}
204+ if err := c .trackingCache .List (ctx , deployList ); err != nil {
205+ return nil , fmt .Errorf ("listing managed deployments: %w" , err )
194206 }
195207
196- for i := range list .Items {
197- ss := & list .Items [i ]
208+ for i := range deployList .Items {
209+ d := & deployList .Items [i ]
210+ items = append (items , workloadItem {
211+ apiVersion : "apps/v1" ,
212+ kind : "Deployment" ,
213+ name : d .Name ,
214+ namespace : d .Namespace ,
215+ podAnnotations : d .Spec .Template .Annotations ,
216+ containers : d .Spec .Template .Spec .Containers ,
217+ })
218+ }
198219
199- annotation , ok := ss .Spec .Template .Annotations [ProxyInjectAnnotation ]
220+ dsList := & appsv1.DaemonSetList {}
221+ if err := c .trackingCache .List (ctx , dsList ); err != nil {
222+ return nil , fmt .Errorf ("listing managed daemonsets: %w" , err )
223+ }
200224
201- if ! ok {
202- continue
203- }
225+ for i := range dsList .Items {
226+ ds := & dsList .Items [i ]
227+ items = append (items , workloadItem {
228+ apiVersion : "apps/v1" ,
229+ kind : "DaemonSet" ,
230+ name : ds .Name ,
231+ namespace : ds .Namespace ,
232+ podAnnotations : ds .Spec .Template .Annotations ,
233+ containers : ds .Spec .Template .Spec .Containers ,
234+ })
235+ }
204236
205- if err := c . applyProxyVars ( ctx , "apps/v1" , "StatefulSet" , ss . Name , ss . Namespace , annotation , proxyVars ); err != nil {
206- return err
207- }
237+ ssList := & appsv1. StatefulSetList {}
238+ if err := c . trackingCache . List ( ctx , ssList ); err != nil {
239+ return nil , fmt . Errorf ( "listing managed statefulsets: %w" , err )
208240 }
209241
210- return nil
242+ for i := range ssList .Items {
243+ ss := & ssList .Items [i ]
244+ items = append (items , workloadItem {
245+ apiVersion : "apps/v1" ,
246+ kind : "StatefulSet" ,
247+ name : ss .Name ,
248+ namespace : ss .Namespace ,
249+ podAnnotations : ss .Spec .Template .Annotations ,
250+ containers : ss .Spec .Template .Spec .Containers ,
251+ })
252+ }
253+
254+ return items , nil
211255}
212256
213- // applyProxyVars SSA-applies the proxy env vars to each container named in the
214- // comma-separated annotation value. The proxy controller owns only the three
215- // proxy env var entries; all other fields remain owned by boxcutter.
257+ // applyProxyVars SSA-applies proxy env vars to each named container on the
258+ // workload object. When proxyVars is empty/nil (proxy removed or no annotation),
259+ // it applies an empty env list, which removes any env var entries previously
260+ // owned by the proxy controller's SSA field manager. Only the three proxy env
261+ // var entries are owned; all other fields remain owned by boxcutter.
216262func (c * ProxyController ) applyProxyVars (
217263 ctx context.Context ,
218- apiVersion , kind , name , namespace , annotation string ,
264+ apiVersion , kind , name , namespace string ,
265+ containerNames []string ,
219266 proxyVars []corev1.EnvVar ,
220267) error {
221268 log := ctrl .LoggerFrom (ctx ).WithName (proxyControllerName )
222269
223- containerNames := strings .Split (annotation , "," )
224- containerPatches := make ([]map [string ]interface {}, 0 , len (containerNames ))
270+ if len (containerNames ) == 0 {
271+ return nil
272+ }
225273
274+ containerPatches := make ([]map [string ]interface {}, 0 , len (containerNames ))
226275 envEntries := proxyEnvEntries (proxyVars )
227276
228277 for _ , containerName := range containerNames {
229- containerName = strings .TrimSpace (containerName )
230- if containerName == "" {
231- continue
232- }
233-
234278 containerPatches = append (containerPatches , map [string ]interface {}{
235279 "name" : containerName ,
236280 "env" : envEntries ,
237281 })
238282 }
239283
240- if len (containerPatches ) == 0 {
241- return nil
242- }
243-
244284 patch := & unstructured.Unstructured {
245285 Object : map [string ]interface {}{
246286 "apiVersion" : apiVersion ,
0 commit comments