11package io .javaoperatorsdk .operator .processing .dependent .kubernetes ;
22
3- import java .util .HashMap ;
43import java .util .List ;
54import java .util .Map ;
65
1110
1211import io .fabric8 .kubernetes .api .model .ConfigMap ;
1312import io .fabric8 .kubernetes .api .model .HasMetadata ;
13+ import io .fabric8 .kubernetes .api .model .Secret ;
1414import io .fabric8 .kubernetes .api .model .apps .DaemonSet ;
1515import io .fabric8 .kubernetes .api .model .apps .Deployment ;
1616import io .fabric8 .kubernetes .api .model .apps .ReplicaSet ;
1717import io .fabric8 .kubernetes .api .model .apps .StatefulSet ;
1818import io .javaoperatorsdk .operator .MockKubernetesClient ;
19+ import io .javaoperatorsdk .operator .OperatorException ;
1920import io .javaoperatorsdk .operator .ReconcilerUtils ;
2021import io .javaoperatorsdk .operator .api .config .ConfigurationService ;
2122import io .javaoperatorsdk .operator .api .config .ControllerConfiguration ;
2223import io .javaoperatorsdk .operator .api .reconciler .Context ;
2324
2425import static org .assertj .core .api .Assertions .assertThat ;
26+ import static org .assertj .core .api .Assertions .assertThatThrownBy ;
2527import static org .mockito .ArgumentMatchers .any ;
2628import static org .mockito .Mockito .mock ;
2729import static org .mockito .Mockito .when ;
@@ -47,6 +49,54 @@ void setup() {
4749 when (mockedContext .getControllerConfiguration ()).thenReturn (controllerConfiguration );
4850 }
4951
52+ @ Test
53+ void noMatchWhenNoMatchingController () {
54+ var desired = loadResource ("nginx-deployment.yaml" , Deployment .class );
55+ var actual =
56+ loadResource ("deployment-with-managed-fields-additional-controller.yaml" , Deployment .class );
57+ actual
58+ .getMetadata ()
59+ .getManagedFields ()
60+ .removeIf (managedFieldsEntry -> managedFieldsEntry .getManager ().equals ("controller" ));
61+
62+ assertThat (matcher .matches (actual , desired , mockedContext )).isFalse ();
63+ }
64+
65+ @ Test
66+ void exceptionWhenDuplicateController () {
67+ var desired = loadResource ("nginx-deployment.yaml" , Deployment .class );
68+ var actual =
69+ loadResource ("deployment-with-managed-fields-additional-controller.yaml" , Deployment .class );
70+ actual .getMetadata ().getManagedFields ().stream ()
71+ .filter (managedFieldsEntry -> managedFieldsEntry .getManager ().equals ("controller" ))
72+ .findFirst ()
73+ .ifPresent (
74+ managedFieldsEntry -> actual .getMetadata ().getManagedFields ().add (managedFieldsEntry ));
75+
76+ assertThatThrownBy (() -> matcher .matches (actual , desired , mockedContext ))
77+ .isInstanceOf (OperatorException .class )
78+ .hasMessage (
79+ "More than one field manager exists with name: controller in resource: Deployment with"
80+ + " name: test" );
81+ }
82+
83+ @ Test
84+ void matchWithSensitiveResource () {
85+ var desired = loadResource ("secret-desired.yaml" , Secret .class );
86+ var actual = loadResource ("secret.yaml" , Secret .class );
87+
88+ assertThat (matcher .matches (actual , desired , mockedContext )).isTrue ();
89+ }
90+
91+ @ Test
92+ void noMatchWithSensitiveResource () {
93+ var desired = loadResource ("secret-desired.yaml" , Secret .class );
94+ var actual = loadResource ("secret.yaml" , Secret .class );
95+ actual .getData ().put ("key1" , "dmFsMg==" );
96+
97+ assertThat (matcher .matches (actual , desired , mockedContext )).isFalse ();
98+ }
99+
50100 @ Test
51101 void checksIfAddsNotAddedByController () {
52102 var desired = loadResource ("nginx-deployment.yaml" , Deployment .class );
@@ -56,7 +106,40 @@ void checksIfAddsNotAddedByController() {
56106 assertThat (matcher .matches (actual , desired , mockedContext )).isTrue ();
57107 }
58108
59- // In the example the owner reference in a list is referenced by "k:", while all the fields are
109+ @ Test
110+ void throwExceptionWhenManagedListEntryNotFound () {
111+ var desired = loadResource ("nginx-deployment.yaml" , Deployment .class );
112+ var actual =
113+ loadResource ("deployment-with-managed-fields-additional-controller.yaml" , Deployment .class );
114+ final var container = actual .getSpec ().getTemplate ().getSpec ().getContainers ().get (0 );
115+ container .setName ("foobar" );
116+
117+ assertThatThrownBy (() -> matcher .matches (actual , desired , mockedContext ))
118+ .isInstanceOf (IllegalStateException .class )
119+ .hasMessage (
120+ "Cannot find list element for key: {\" name\" :\" nginx\" } in map: [[image,"
121+ + " imagePullPolicy, name, ports, resources, terminationMessagePath,"
122+ + " terminationMessagePolicy]]" );
123+ }
124+
125+ @ Test
126+ void throwExceptionWhenDuplicateManagedListEntryFound () {
127+ var desired = loadResource ("nginx-deployment.yaml" , Deployment .class );
128+ var actual =
129+ loadResource ("deployment-with-managed-fields-additional-controller.yaml" , Deployment .class );
130+ final var container = actual .getSpec ().getTemplate ().getSpec ().getContainers ().get (0 );
131+ actual .getSpec ().getTemplate ().getSpec ().getContainers ().add (container );
132+
133+ assertThatThrownBy (() -> matcher .matches (actual , desired , mockedContext ))
134+ .isInstanceOf (IllegalStateException .class )
135+ .hasMessage (
136+ "More targets found in list element for key: {\" name\" :\" nginx\" } in map: [[image,"
137+ + " imagePullPolicy, name, ports, resources, terminationMessagePath,"
138+ + " terminationMessagePolicy], [image, imagePullPolicy, name, ports, resources,"
139+ + " terminationMessagePath, terminationMessagePolicy]]" );
140+ }
141+
142+ // in the example the owner reference in a list is referenced by "k:", while all the fields are
60143 // managed but not listed
61144 @ Test
62145 void emptyListElementMatchesAllFields () {
@@ -118,45 +201,11 @@ void addedLabelInDesiredMakesMatchFail() {
118201 }
119202
120203 @ Test
121- @ SuppressWarnings ("unchecked" )
122- void sortListItemsTest () {
123- var nestedMap1 = new HashMap <String , Object >();
124- nestedMap1 .put ("z" , 26 );
125- nestedMap1 .put ("y" , 25 );
126-
127- var nestedMap2 = new HashMap <String , Object >();
128- nestedMap2 .put ("b" , 26 );
129- nestedMap2 .put ("c" , 25 );
130- nestedMap2 .put ("a" , 24 );
131-
132- var unsortedListItems = List .<Object >of (1 , nestedMap1 , nestedMap2 );
133- var sortedListItems = matcher .sortListItems (unsortedListItems );
134- assertThat (sortedListItems ).element (0 ).isEqualTo (1 );
135-
136- var sortedNestedMap1 = (Map <String , Object >) sortedListItems .get (1 );
137- assertThat (sortedNestedMap1 .keySet ()).containsExactly ("y" , "z" );
204+ void withFinalizer () {
205+ var desired = loadResource ("secret-with-finalizer-desired.yaml" , Secret .class );
206+ var actual = loadResource ("secret-with-finalizer.yaml" , Secret .class );
138207
139- var sortedNestedMap2 = (Map <String , Object >) sortedListItems .get (2 );
140- assertThat (sortedNestedMap2 .keySet ()).containsExactly ("a" , "b" , "c" );
141- }
142-
143- @ Test
144- @ SuppressWarnings ("unchecked" )
145- void testSortMapWithNestedMap () {
146- var nestedMap = new HashMap <String , Object >();
147- nestedMap .put ("z" , 26 );
148- nestedMap .put ("y" , 25 );
149-
150- var unsortedMap = new HashMap <String , Object >();
151- unsortedMap .put ("b" , nestedMap );
152- unsortedMap .put ("a" , 1 );
153- unsortedMap .put ("c" , 2 );
154-
155- var sortedMap = matcher .sortMap (unsortedMap );
156- assertThat (sortedMap .keySet ()).containsExactly ("a" , "b" , "c" );
157-
158- var sortedNestedMap = (Map <String , Object >) sortedMap .get ("b" );
159- assertThat (sortedNestedMap .keySet ()).containsExactly ("y" , "z" );
208+ assertThat (matcher .matches (actual , desired , mockedContext )).isTrue ();
160209 }
161210
162211 @ ParameterizedTest
@@ -205,6 +254,23 @@ void testSanitizeState_statefulSetWithResources_withMismatch() {
205254 assertThat (matcher .matches (actualStatefulSet , desiredStatefulSet , mockedContext )).isFalse ();
206255 }
207256
257+ @ Test
258+ void testSanitizeState_statefulSet_withResourceTypeMismatch () {
259+ var desiredReplicaSet = loadResource ("sample-rs-resources-desired.yaml" , ReplicaSet .class );
260+ var actualStatefulSet = loadResource ("sample-sts-resources.yaml" , StatefulSet .class );
261+
262+ assertThat (matcher .matches (actualStatefulSet , desiredReplicaSet , mockedContext )).isFalse ();
263+ }
264+
265+ @ Test
266+ void testSanitizeState_deployment_withResourceTypeMismatch () {
267+ var desiredReplicaSet = loadResource ("sample-rs-resources-desired.yaml" , ReplicaSet .class );
268+ var actualDeployment =
269+ loadResource ("deployment-with-managed-fields-additional-controller.yaml" , Deployment .class );
270+
271+ assertThat (matcher .matches (actualDeployment , desiredReplicaSet , mockedContext )).isFalse ();
272+ }
273+
208274 @ Test
209275 void testSanitizeState_replicaSetWithResources () {
210276 var desiredReplicaSet = loadResource ("sample-rs-resources-desired.yaml" , ReplicaSet .class );
@@ -222,6 +288,14 @@ void testSanitizeState_replicaSetWithResources_withMismatch() {
222288 assertThat (matcher .matches (actualReplicaSet , desiredReplicaSet , mockedContext )).isFalse ();
223289 }
224290
291+ @ Test
292+ void testSanitizeState_replicaSet_withResourceTypeMismatch () {
293+ var desiredDaemonSet = loadResource ("sample-ds-resources-desired.yaml" , DaemonSet .class );
294+ var actualReplicaSet = loadResource ("sample-rs-resources.yaml" , ReplicaSet .class );
295+
296+ assertThat (matcher .matches (actualReplicaSet , desiredDaemonSet , mockedContext )).isFalse ();
297+ }
298+
225299 @ Test
226300 void testSanitizeState_daemonSetWithResources () {
227301 var desiredDaemonSet = loadResource ("sample-ds-resources-desired.yaml" , DaemonSet .class );
@@ -238,6 +312,14 @@ void testSanitizeState_daemonSetWithResources_withMismatch() {
238312 assertThat (matcher .matches (actualDaemonSet , desiredDaemonSet , mockedContext )).isFalse ();
239313 }
240314
315+ @ Test
316+ void testSanitizeState_daemonSet_withResourceTypeMismatch () {
317+ var desiredReplicaSet = loadResource ("sample-rs-resources-desired.yaml" , ReplicaSet .class );
318+ var actualDaemonSet = loadResource ("sample-ds-resources.yaml" , DaemonSet .class );
319+
320+ assertThat (matcher .matches (actualDaemonSet , desiredReplicaSet , mockedContext )).isFalse ();
321+ }
322+
241323 @ ParameterizedTest
242324 @ ValueSource (booleans = {true , false })
243325 void testCustomMatcher_returnsExpectedMatchBasedOnReadOnlyLabel (boolean readOnly ) {
@@ -263,6 +345,52 @@ void testCustomMatcher_returnsExpectedMatchBasedOnReadOnlyLabel(boolean readOnly
263345 .isEqualTo (readOnly );
264346 }
265347
348+ @ Test
349+ void keepOnlyManagedFields_withInvalidManagedFieldsKey () {
350+ assertThatThrownBy (
351+ () ->
352+ SSABasedGenericKubernetesResourceMatcher .keepOnlyManagedFields (
353+ Map .of (),
354+ Map .of (),
355+ Map .of ("invalid" , 1 ),
356+ mockedContext .getClient ().getKubernetesSerialization ())) //
357+ .isInstanceOf (IllegalStateException .class ) //
358+ .hasMessage ("Key: invalid has no prefix: f:" );
359+ }
360+
361+ @ Test
362+ @ SuppressWarnings ("unchecked" )
363+ void testSortMap () {
364+ final var unsortedMap = Map .of ("b" , Map .of ("z" , 26 , "y" , 25 ), "a" , List .of ("w" , "v" ), "c" , 2 );
365+
366+ var sortedMap = SSABasedGenericKubernetesResourceMatcher .sortMap (unsortedMap );
367+ assertThat (sortedMap .keySet ()).containsExactly ("a" , "b" , "c" );
368+
369+ var sortedNestedMap = (Map <String , Object >) sortedMap .get ("b" );
370+ assertThat (sortedNestedMap .keySet ()).containsExactly ("y" , "z" );
371+ }
372+
373+ @ Test
374+ @ SuppressWarnings ("unchecked" )
375+ void testSortListItems () {
376+ final var unsortedList =
377+ List .of (1 , Map .of ("z" , 26 , "y" , 25 ), Map .of ("b" , 26 , "c" , 25 , "a" , 24 ), List .of ("w" , "v" ));
378+
379+ var sortedListItems = SSABasedGenericKubernetesResourceMatcher .sortListItems (unsortedList );
380+ assertThat (sortedListItems ).element (0 ).isEqualTo (1 );
381+
382+ var sortedNestedMap1 = (Map <String , Object >) sortedListItems .get (1 );
383+ assertThat (sortedNestedMap1 .keySet ()).containsExactly ("y" , "z" );
384+
385+ var sortedNestedMap2 = (Map <String , Object >) sortedListItems .get (2 );
386+ assertThat (sortedNestedMap2 .keySet ()).containsExactly ("a" , "b" , "c" );
387+ }
388+
389+ private static <R > R loadResource (String fileName , Class <R > clazz ) {
390+ return ReconcilerUtils .loadYaml (
391+ clazz , SSABasedGenericKubernetesResourceMatcherTest .class , fileName );
392+ }
393+
266394 private static class ConfigMapDR extends KubernetesDependentResource <ConfigMap , ConfigMap > {
267395 public ConfigMapDR () {
268396 super (ConfigMap .class );
@@ -285,9 +413,4 @@ protected boolean matches(
285413 return actualMap .equals (desiredMap );
286414 }
287415 }
288-
289- private static <R > R loadResource (String fileName , Class <R > clazz ) {
290- return ReconcilerUtils .loadYaml (
291- clazz , SSABasedGenericKubernetesResourceMatcherTest .class , fileName );
292- }
293416}
0 commit comments