22
33import  java .util .List ;
44import  java .util .Map ;
5+ import  java .util .Objects ;
56import  java .util .Optional ;
67
78import  io .fabric8 .kubernetes .api .model .Container ;
9+ import  io .fabric8 .kubernetes .api .model .EnvVar ;
810import  io .fabric8 .kubernetes .api .model .GenericKubernetesResource ;
911import  io .fabric8 .kubernetes .api .model .PodTemplateSpec ;
1012import  io .fabric8 .kubernetes .api .model .Quantity ;
1113import  io .fabric8 .kubernetes .api .model .ResourceRequirements ;
1214
1315/** 
14-  * Sanitizes the {@link ResourceRequirements} in the containers of a pair of {@link PodTemplateSpec}  
15-  * instances. 
16+  * Sanitizes the {@link ResourceRequirements} and the {@link EnvVar}  in the containers of a pair of 
17+  * {@link PodTemplateSpec}  instances. 
1618 * 
1719 * <p>When the sanitizer finds a mismatch in the structure of the given templates, before it gets to 
18-  * the nested resource limits and requests , it returns early without fixing the actual map. This is 
19-  * an optimization  because the given templates will anyway differ at this point. This means we do 
20-  * not have to  attempt to sanitize the resources  for these use cases, since there will anyway be an 
21-  * update of the  K8s resource. 
20+  * the nested fields , it returns early without fixing the actual map. This is an optimization  
21+  * because the given templates will anyway differ at this point. This means we do not have to  
22+  * attempt to sanitize the fields  for these use cases, since there will anyway be an update of the  
23+  * K8s resource. 
2224 * 
2325 * <p>The algorithm traverses the whole template structure because we need the actual and desired 
24-  * {@link Quantity} instances to compare their numerical amount . Using the {@link 
26+  * {@link Quantity} and {@link EnvVar} instances . Using the {@link 
2527 * GenericKubernetesResource#get(Map, Object...)} shortcut would need to create new instances just 
2628 * for the sanitization check. 
2729 */ 
28- class  ResourceRequirementsSanitizer  {
30+ class  PodTemplateSpecSanitizer  {
2931
30-   static  void  sanitizeResourceRequirements (
32+   static  void  sanitizePodTemplateSpec (
3133      final  Map <String , Object > actualMap ,
3234      final  PodTemplateSpec  actualTemplate ,
3335      final  PodTemplateSpec  desiredTemplate ) {
@@ -37,31 +39,37 @@ static void sanitizeResourceRequirements(
3739    if  (actualTemplate .getSpec () == null  || desiredTemplate .getSpec () == null ) {
3840      return ;
3941    }
40-     sanitizeResourceRequirements (
42+     sanitizePodTemplateSpec (
4143        actualMap ,
4244        actualTemplate .getSpec ().getInitContainers (),
4345        desiredTemplate .getSpec ().getInitContainers (),
4446        "initContainers" );
45-     sanitizeResourceRequirements (
47+     sanitizePodTemplateSpec (
4648        actualMap ,
4749        actualTemplate .getSpec ().getContainers (),
4850        desiredTemplate .getSpec ().getContainers (),
4951        "containers" );
5052  }
5153
52-   private  static  void  sanitizeResourceRequirements (
54+   private  static  void  sanitizePodTemplateSpec (
5355      final  Map <String , Object > actualMap ,
5456      final  List <Container > actualContainers ,
5557      final  List <Container > desiredContainers ,
5658      final  String  containerPath ) {
5759    int  containers  = desiredContainers .size ();
5860    if  (containers  == actualContainers .size ()) {
5961      for  (int  containerIndex  = 0 ; containerIndex  < containers ; containerIndex ++) {
60-         var  desiredContainer  = desiredContainers .get (containerIndex );
61-         var  actualContainer  = actualContainers .get (containerIndex );
62+         final   var  desiredContainer  = desiredContainers .get (containerIndex );
63+         final   var  actualContainer  = actualContainers .get (containerIndex );
6264        if  (!desiredContainer .getName ().equals (actualContainer .getName ())) {
6365          return ;
6466        }
67+         sanitizeEnvVars (
68+             actualMap ,
69+             actualContainer .getEnv (),
70+             desiredContainer .getEnv (),
71+             containerPath ,
72+             containerIndex );
6573        sanitizeResourceRequirements (
6674            actualMap ,
6775            actualContainer .getResources (),
@@ -121,7 +129,7 @@ private static void sanitizeQuantities(
121129            m  ->
122130                actualResource .forEach (
123131                    (key , actualQuantity ) -> {
124-                       var  desiredQuantity  = desiredResource .get (key );
132+                       final   var  desiredQuantity  = desiredResource .get (key );
125133                      if  (desiredQuantity  == null ) {
126134                        return ;
127135                      }
@@ -138,4 +146,53 @@ private static void sanitizeQuantities(
138146                      }
139147                    }));
140148  }
149+ 
150+   @ SuppressWarnings ("unchecked" )
151+   private  static  void  sanitizeEnvVars (
152+       final  Map <String , Object > actualMap ,
153+       final  List <EnvVar > actualEnvVars ,
154+       final  List <EnvVar > desiredEnvVars ,
155+       final  String  containerPath ,
156+       final  int  containerIndex ) {
157+     if  (desiredEnvVars .isEmpty () || actualEnvVars .isEmpty ()) {
158+       return ;
159+     }
160+     Optional .ofNullable (
161+             GenericKubernetesResource .get (
162+                 actualMap , "spec" , "template" , "spec" , containerPath , containerIndex , "env" ))
163+         .map (List .class ::cast )
164+         .ifPresent (
165+             envVars  ->
166+                 actualEnvVars .forEach (
167+                     actualEnvVar  -> {
168+                       final  var  actualEnvVarName  = actualEnvVar .getName ();
169+                       final  var  actualEnvVarValue  = actualEnvVar .getValue ();
170+                       // check if the actual EnvVar value string is not null or the desired EnvVar 
171+                       // already contains the same EnvVar name with a non empty EnvVar value 
172+                       final  var  isDesiredEnvVarEmpty  =
173+                           hasEnvVarNoEmptyValue (actualEnvVarName , desiredEnvVars );
174+                       if  (actualEnvVarValue  != null  || isDesiredEnvVarEmpty ) {
175+                         return ;
176+                       }
177+                       envVars .stream ()
178+                           .filter (
179+                               envVar  ->
180+                                   ((Map <String , String >) envVar )
181+                                       .get ("name" )
182+                                       .equals (actualEnvVarName ))
183+                           // add the actual EnvVar value with an empty string to prevent a 
184+                           // resource update 
185+                           .forEach (envVar  -> ((Map <String , String >) envVar ).put ("value" , "" ));
186+                     }));
187+   }
188+ 
189+   private  static  boolean  hasEnvVarNoEmptyValue (
190+       final  String  envVarName , final  List <EnvVar > envVars ) {
191+     return  envVars .stream ()
192+         .anyMatch (
193+             envVar  ->
194+                 Objects .equals (envVarName , envVar .getName ())
195+                     && envVar .getValue () != null 
196+                     && !envVar .getValue ().isEmpty ());
197+   }
141198}
0 commit comments