11package io .javaoperatorsdk .operator .processing .dependent .kubernetes ;
22
3- import java .util .List ;
4- import java .util .Map ;
5- import java .util .Optional ;
6-
73import io .fabric8 .kubernetes .api .model .Container ;
4+ import io .fabric8 .kubernetes .api .model .EnvVar ;
85import io .fabric8 .kubernetes .api .model .GenericKubernetesResource ;
96import io .fabric8 .kubernetes .api .model .PodTemplateSpec ;
107import io .fabric8 .kubernetes .api .model .Quantity ;
118import io .fabric8 .kubernetes .api .model .ResourceRequirements ;
9+ import java .util .List ;
10+ import java .util .Map ;
11+ import java .util .Objects ;
12+ import java .util .Optional ;
1213
1314/**
14- * Sanitizes the {@link ResourceRequirements} in the containers of a pair of {@link PodTemplateSpec}
15- * instances.
15+ * Sanitizes the {@link ResourceRequirements} and the {@link EnvVar} in the containers of a pair of
16+ * {@link PodTemplateSpec} instances.
1617 *
1718 * <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.
19+ * the nested fields , it returns early without fixing the actual map. This is an optimization
20+ * because the given templates will anyway differ at this point. This means we do not have to
21+ * attempt to sanitize the fields for these use cases, since there will anyway be an update of the
22+ * K8s resource.
2223 *
2324 * <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
25+ * {@link Quantity} and {@link EnvVar} instances . Using the {@link
2526 * GenericKubernetesResource#get(Map, Object...)} shortcut would need to create new instances just
2627 * for the sanitization check.
2728 */
28- class ResourceRequirementsSanitizer {
29+ class PodTemplateSpecSanitizer {
2930
30- static void sanitizeResourceRequirements (
31+ static void sanitizePodTemplateSpec (
3132 final Map <String , Object > actualMap ,
3233 final PodTemplateSpec actualTemplate ,
3334 final PodTemplateSpec desiredTemplate ) {
@@ -37,31 +38,37 @@ static void sanitizeResourceRequirements(
3738 if (actualTemplate .getSpec () == null || desiredTemplate .getSpec () == null ) {
3839 return ;
3940 }
40- sanitizeResourceRequirements (
41+ sanitizePodTemplateSpec (
4142 actualMap ,
4243 actualTemplate .getSpec ().getInitContainers (),
4344 desiredTemplate .getSpec ().getInitContainers (),
4445 "initContainers" );
45- sanitizeResourceRequirements (
46+ sanitizePodTemplateSpec (
4647 actualMap ,
4748 actualTemplate .getSpec ().getContainers (),
4849 desiredTemplate .getSpec ().getContainers (),
4950 "containers" );
5051 }
5152
52- private static void sanitizeResourceRequirements (
53+ private static void sanitizePodTemplateSpec (
5354 final Map <String , Object > actualMap ,
5455 final List <Container > actualContainers ,
5556 final List <Container > desiredContainers ,
5657 final String containerPath ) {
5758 int containers = desiredContainers .size ();
5859 if (containers == actualContainers .size ()) {
5960 for (int containerIndex = 0 ; containerIndex < containers ; containerIndex ++) {
60- var desiredContainer = desiredContainers .get (containerIndex );
61- var actualContainer = actualContainers .get (containerIndex );
61+ final var desiredContainer = desiredContainers .get (containerIndex );
62+ final var actualContainer = actualContainers .get (containerIndex );
6263 if (!desiredContainer .getName ().equals (actualContainer .getName ())) {
6364 return ;
6465 }
66+ sanitizeEnvVars (
67+ actualMap ,
68+ actualContainer .getEnv (),
69+ desiredContainer .getEnv (),
70+ containerPath ,
71+ containerIndex );
6572 sanitizeResourceRequirements (
6673 actualMap ,
6774 actualContainer .getResources (),
@@ -121,7 +128,7 @@ private static void sanitizeQuantities(
121128 m ->
122129 actualResource .forEach (
123130 (key , actualQuantity ) -> {
124- var desiredQuantity = desiredResource .get (key );
131+ final var desiredQuantity = desiredResource .get (key );
125132 if (desiredQuantity == null ) {
126133 return ;
127134 }
@@ -138,4 +145,53 @@ private static void sanitizeQuantities(
138145 }
139146 }));
140147 }
148+
149+ @ SuppressWarnings ("unchecked" )
150+ private static void sanitizeEnvVars (
151+ final Map <String , Object > actualMap ,
152+ final List <EnvVar > actualEnvVars ,
153+ final List <EnvVar > desiredEnvVars ,
154+ final String containerPath ,
155+ final int containerIndex ) {
156+ if (desiredEnvVars .isEmpty () || actualEnvVars .isEmpty ()) {
157+ return ;
158+ }
159+ Optional .ofNullable (
160+ GenericKubernetesResource .get (
161+ actualMap , "spec" , "template" , "spec" , containerPath , containerIndex , "env" ))
162+ .map (List .class ::cast )
163+ .ifPresent (
164+ envVars ->
165+ actualEnvVars .forEach (
166+ actualEnvVar -> {
167+ final var actualEnvVarName = actualEnvVar .getName ();
168+ final var actualEnvVarValue = actualEnvVar .getValue ();
169+ // check if the actual EnvVar value string is not null or the desired EnvVar
170+ // already contains the same EnvVar name with a non empty EnvVar value
171+ final var isDesiredEnvVarEmpty =
172+ hasEnvVarNoEmptyValue (actualEnvVarName , desiredEnvVars );
173+ if (actualEnvVarValue != null || isDesiredEnvVarEmpty ) {
174+ return ;
175+ }
176+ envVars .stream ()
177+ .filter (
178+ envVar ->
179+ ((Map <String , String >) envVar )
180+ .get ("name" )
181+ .equals (actualEnvVarName ))
182+ // add the actual EnvVar value with an empty string to prevent a
183+ // resource update
184+ .forEach (envVar -> ((Map <String , String >) envVar ).put ("value" , "" ));
185+ }));
186+ }
187+
188+ private static boolean hasEnvVarNoEmptyValue (
189+ final String envVarName , final List <EnvVar > envVars ) {
190+ return envVars .stream ()
191+ .anyMatch (
192+ envVar ->
193+ Objects .equals (envVarName , envVar .getName ())
194+ && envVar .getValue () != null
195+ && !envVar .getValue ().isEmpty ());
196+ }
141197}
0 commit comments