1
1
package io .javaoperatorsdk .operator .processing .dependent .kubernetes ;
2
2
3
- import java .util .List ;
4
- import java .util .Map ;
5
- import java .util .Optional ;
6
-
7
3
import io .fabric8 .kubernetes .api .model .Container ;
4
+ import io .fabric8 .kubernetes .api .model .EnvVar ;
8
5
import io .fabric8 .kubernetes .api .model .GenericKubernetesResource ;
9
6
import io .fabric8 .kubernetes .api .model .PodTemplateSpec ;
10
7
import io .fabric8 .kubernetes .api .model .Quantity ;
11
8
import 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 ;
12
13
13
14
/**
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.
16
17
*
17
18
* <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.
22
23
*
23
24
* <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
25
26
* GenericKubernetesResource#get(Map, Object...)} shortcut would need to create new instances just
26
27
* for the sanitization check.
27
28
*/
28
- class ResourceRequirementsSanitizer {
29
+ class PodTemplateSpecSanitizer {
29
30
30
- static void sanitizeResourceRequirements (
31
+ static void sanitizePodTemplateSpec (
31
32
final Map <String , Object > actualMap ,
32
33
final PodTemplateSpec actualTemplate ,
33
34
final PodTemplateSpec desiredTemplate ) {
@@ -37,31 +38,37 @@ static void sanitizeResourceRequirements(
37
38
if (actualTemplate .getSpec () == null || desiredTemplate .getSpec () == null ) {
38
39
return ;
39
40
}
40
- sanitizeResourceRequirements (
41
+ sanitizePodTemplateSpec (
41
42
actualMap ,
42
43
actualTemplate .getSpec ().getInitContainers (),
43
44
desiredTemplate .getSpec ().getInitContainers (),
44
45
"initContainers" );
45
- sanitizeResourceRequirements (
46
+ sanitizePodTemplateSpec (
46
47
actualMap ,
47
48
actualTemplate .getSpec ().getContainers (),
48
49
desiredTemplate .getSpec ().getContainers (),
49
50
"containers" );
50
51
}
51
52
52
- private static void sanitizeResourceRequirements (
53
+ private static void sanitizePodTemplateSpec (
53
54
final Map <String , Object > actualMap ,
54
55
final List <Container > actualContainers ,
55
56
final List <Container > desiredContainers ,
56
57
final String containerPath ) {
57
58
int containers = desiredContainers .size ();
58
59
if (containers == actualContainers .size ()) {
59
60
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 );
62
63
if (!desiredContainer .getName ().equals (actualContainer .getName ())) {
63
64
return ;
64
65
}
66
+ sanitizeEnvVars (
67
+ actualMap ,
68
+ actualContainer .getEnv (),
69
+ desiredContainer .getEnv (),
70
+ containerPath ,
71
+ containerIndex );
65
72
sanitizeResourceRequirements (
66
73
actualMap ,
67
74
actualContainer .getResources (),
@@ -121,7 +128,7 @@ private static void sanitizeQuantities(
121
128
m ->
122
129
actualResource .forEach (
123
130
(key , actualQuantity ) -> {
124
- var desiredQuantity = desiredResource .get (key );
131
+ final var desiredQuantity = desiredResource .get (key );
125
132
if (desiredQuantity == null ) {
126
133
return ;
127
134
}
@@ -138,4 +145,53 @@ private static void sanitizeQuantities(
138
145
}
139
146
}));
140
147
}
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
+ }
141
197
}
0 commit comments