9
9
import io .fabric8 .kubernetes .api .model .*;
10
10
import io .fabric8 .kubernetes .api .model .apps .Deployment ;
11
11
import io .fabric8 .kubernetes .client .KubernetesClient ;
12
+ import io .javaoperatorsdk .operator .ReconcilerUtils ;
13
+ import io .javaoperatorsdk .operator .api .config .informer .InformerConfiguration ;
12
14
import io .javaoperatorsdk .operator .api .reconciler .*;
13
15
import io .javaoperatorsdk .operator .api .reconciler .Context ;
14
- import io .javaoperatorsdk .operator .api .reconciler .dependent .Updater ;
15
- import io .javaoperatorsdk .operator .processing .dependent .kubernetes .KubernetesDependentResource ;
16
- import io .javaoperatorsdk .operator .processing .event .ResourceID ;
17
- import io .javaoperatorsdk .operator .processing .event .source .AssociatedSecondaryResourceIdentifier ;
18
16
import io .javaoperatorsdk .operator .processing .event .source .EventSource ;
17
+ import io .javaoperatorsdk .operator .processing .event .source .informer .InformerEventSource ;
19
18
20
- import static io .javaoperatorsdk .operator .ReconcilerUtils .loadYaml ;
21
19
import static io .javaoperatorsdk .operator .api .reconciler .Constants .NO_FINALIZER ;
22
20
23
- @ ControllerConfiguration (finalizerName = NO_FINALIZER )
21
+ /** Shows how to implement reconciler using the low level api directly. */
22
+ @ ControllerConfiguration (
23
+ finalizerName = NO_FINALIZER ,
24
+ labelSelector = WebPageReconciler .LOW_LEVEL_LABEL_KEY )
24
25
public class WebPageReconciler
25
26
implements Reconciler <WebPage >, ErrorStatusHandler <WebPage >, EventSourceInitializer <WebPage > {
26
27
27
- private final Logger log = LoggerFactory .getLogger (getClass ());
28
+ public static final String LOW_LEVEL_LABEL_KEY = "low-level" ;
29
+ public static final String INDEX_HTML = "index.html" ;
28
30
29
- private final KubernetesClient kubernetesClient ;
31
+ private static final Logger log = LoggerFactory . getLogger ( WebPageReconciler . class ) ;
30
32
31
- private KubernetesDependentResource <ConfigMap , WebPage > configMapDR ;
32
- private KubernetesDependentResource <Deployment , WebPage > deploymentDR ;
33
- private KubernetesDependentResource <Service , WebPage > serviceDR ;
33
+ private final KubernetesClient kubernetesClient ;
34
34
35
35
public WebPageReconciler (KubernetesClient kubernetesClient ) {
36
36
this .kubernetesClient = kubernetesClient ;
37
- createDependentResources (kubernetesClient );
38
37
}
39
38
39
+ InformerEventSource configMapEventSource ;
40
+
40
41
@ Override
41
42
public List <EventSource > prepareEventSources (EventSourceContext <WebPage > context ) {
42
- return List .of (
43
- configMapDR .eventSource (context ),
44
- deploymentDR .eventSource (context ),
45
- serviceDR .eventSource (context ));
43
+ configMapEventSource =
44
+ new InformerEventSource <>(InformerConfiguration .from (context , ConfigMap .class )
45
+ .withLabelSelector (LOW_LEVEL_LABEL_KEY )
46
+ .build (), context );
47
+ var deploymentEventSource =
48
+ new InformerEventSource <>(InformerConfiguration .from (context , Deployment .class )
49
+ .withLabelSelector (LOW_LEVEL_LABEL_KEY )
50
+ .build (), context );
51
+ var serviceEventSource =
52
+ new InformerEventSource <>(InformerConfiguration .from (context , Service .class )
53
+ .withLabelSelector (LOW_LEVEL_LABEL_KEY )
54
+ .build (), context );
55
+ return List .of (configMapEventSource , deploymentEventSource , serviceEventSource );
46
56
}
47
57
48
58
@ Override
49
59
public UpdateControl <WebPage > reconcile (WebPage webPage , Context context ) {
60
+ log .info ("Reconciling web page: {}" , webPage );
50
61
if (webPage .getSpec ().getHtml ().contains ("error" )) {
51
62
throw new ErrorSimulationException ("Simulating error" );
52
63
}
64
+ String ns = webPage .getMetadata ().getNamespace ();
65
+ String configMapName = configMapName (webPage );
66
+ String deploymentName = deploymentName (webPage );
67
+
68
+
69
+ ConfigMap desiredHtmlConfigMap = makeDesiredHtmlConfigMap (ns , configMapName , webPage );
70
+ Deployment desiredDeployment =
71
+ makeDesiredDeployment (webPage , deploymentName , ns , configMapName );
72
+ Service desiredService = makeDesiredService (webPage , ns , desiredDeployment );
73
+
74
+ var previousConfigMap = context .getSecondaryResource (ConfigMap .class ).orElse (null );
75
+ if (!match (desiredHtmlConfigMap , previousConfigMap )) {
76
+ log .info (
77
+ "Creating or updating ConfigMap {} in {}" ,
78
+ desiredHtmlConfigMap .getMetadata ().getName (),
79
+ ns );
80
+ kubernetesClient .configMaps ().inNamespace (ns ).createOrReplace (desiredHtmlConfigMap );
81
+ }
53
82
54
- configMapDR .reconcile (webPage , context );
55
- deploymentDR .reconcile (webPage , context );
56
- serviceDR .reconcile (webPage , context );
83
+ var existingDeployment = context .getSecondaryResource (Deployment .class ).orElse (null );
84
+ if (!match (desiredDeployment , existingDeployment )) {
85
+ log .info (
86
+ "Creating or updating Deployment {} in {}" ,
87
+ desiredDeployment .getMetadata ().getName (),
88
+ ns );
89
+ kubernetesClient .apps ().deployments ().inNamespace (ns ).createOrReplace (desiredDeployment );
90
+ }
57
91
58
- WebPageStatus status = new WebPageStatus ();
92
+ var existingService = context .getSecondaryResource (Service .class ).orElse (null );
93
+ if (!match (desiredService , existingService )) {
94
+ log .info (
95
+ "Creating or updating Deployment {} in {}" ,
96
+ desiredDeployment .getMetadata ().getName (),
97
+ ns );
98
+ kubernetesClient .services ().inNamespace (ns ).createOrReplace (desiredService );
99
+ }
100
+
101
+ if (previousConfigMap != null && !StringUtils .equals (
102
+ previousConfigMap .getData ().get (INDEX_HTML ),
103
+ desiredHtmlConfigMap .getData ().get (INDEX_HTML ))) {
104
+ log .info ("Restarting pods because HTML has changed in {}" , ns );
105
+ kubernetesClient .pods ().inNamespace (ns ).withLabel ("app" , deploymentName (webPage )).delete ();
106
+ }
107
+ webPage .setStatus (createStatus (desiredHtmlConfigMap .getMetadata ().getName ()));
108
+ return UpdateControl .updateStatus (webPage );
109
+ }
59
110
60
- status .setHtmlConfigMap (configMapDR .getResource (webPage ).orElseThrow ().getMetadata ().getName ());
111
+ private WebPageStatus createStatus (String configMapName ) {
112
+ WebPageStatus status = new WebPageStatus ();
113
+ status .setHtmlConfigMap (configMapName );
61
114
status .setAreWeGood ("Yes!" );
62
115
status .setErrorMessage (null );
63
- webPage .setStatus (status );
116
+ return status ;
117
+ }
64
118
65
- return UpdateControl .updateStatus (webPage );
119
+ private boolean match (Deployment desiredDeployment , Deployment deployment ) {
120
+ if (deployment == null ) {
121
+ return false ;
122
+ } else {
123
+ return desiredDeployment .getSpec ().getReplicas ().equals (deployment .getSpec ().getReplicas ()) &&
124
+ desiredDeployment .getSpec ().getTemplate ().getSpec ().getContainers ().get (0 ).getImage ()
125
+ .equals (
126
+ deployment .getSpec ().getTemplate ().getSpec ().getContainers ().get (0 ).getImage ());
127
+ }
66
128
}
67
129
68
- @ Override
69
- public Optional < WebPage > updateErrorStatus (
70
- WebPage resource , RetryInfo retryInfo , RuntimeException e ) {
71
- resource . getStatus (). setErrorMessage ( "Error: " + e . getMessage ());
72
- return Optional . of ( resource );
130
+ private boolean match ( Service desiredService , Service service ) {
131
+ if ( service == null ) {
132
+ return false ;
133
+ }
134
+ return desiredService . getSpec (). getSelector (). equals ( service . getSpec (). getSelector () );
73
135
}
74
136
75
- private void createDependentResources (KubernetesClient client ) {
76
- this .configMapDR = new ConfigMapDependentResource ();
77
-
78
- this .deploymentDR =
79
- new KubernetesDependentResource <>() {
80
-
81
- @ Override
82
- protected Deployment desired (WebPage webPage , Context context ) {
83
- var deploymentName = deploymentName (webPage );
84
- Deployment deployment = loadYaml (Deployment .class , getClass (), "deployment.yaml" );
85
- deployment .getMetadata ().setName (deploymentName );
86
- deployment .getMetadata ().setNamespace (webPage .getMetadata ().getNamespace ());
87
- deployment .getSpec ().getSelector ().getMatchLabels ().put ("app" , deploymentName );
88
-
89
- deployment
90
- .getSpec ()
91
- .getTemplate ()
92
- .getMetadata ()
93
- .getLabels ()
94
- .put ("app" , deploymentName );
95
- deployment
96
- .getSpec ()
97
- .getTemplate ()
98
- .getSpec ()
99
- .getVolumes ()
100
- .get (0 )
101
- .setConfigMap (
102
- new ConfigMapVolumeSourceBuilder ().withName (configMapName (webPage )).build ());
103
- return deployment ;
104
- }
105
-
106
- @ Override
107
- protected Class <Deployment > resourceType () {
108
- return Deployment .class ;
109
- }
110
- };
111
-
112
- this .serviceDR =
113
- new KubernetesDependentResource <>() {
114
-
115
- @ Override
116
- protected Service desired (WebPage webPage , Context context ) {
117
- Service service = loadYaml (Service .class , getClass (), "service.yaml" );
118
- service .getMetadata ().setName (serviceName (webPage ));
119
- service .getMetadata ().setNamespace (webPage .getMetadata ().getNamespace ());
120
- Map <String , String > labels = new HashMap <>();
121
- labels .put ("app" , deploymentName (webPage ));
122
- service .getSpec ().setSelector (labels );
123
- return service ;
124
- }
125
-
126
- @ Override
127
- protected Class <Service > resourceType () {
128
- return Service .class ;
129
- }
130
- };
137
+ private boolean match (ConfigMap desiredHtmlConfigMap , ConfigMap existingConfigMap ) {
138
+ if (existingConfigMap == null ) {
139
+ return false ;
140
+ } else {
141
+ return desiredHtmlConfigMap .getData ().equals (existingConfigMap .getData ());
142
+ }
143
+ }
144
+
145
+ private Service makeDesiredService (WebPage webPage , String ns , Deployment desiredDeployment ) {
146
+ Service desiredService = ReconcilerUtils .loadYaml (Service .class , getClass (), "service.yaml" );
147
+ desiredService .getMetadata ().setName (serviceName (webPage ));
148
+ desiredService .getMetadata ().setNamespace (ns );
149
+ desiredService .getMetadata ().setLabels (lowLevelLabel ());
150
+ desiredService
151
+ .getSpec ()
152
+ .setSelector (desiredDeployment .getSpec ().getTemplate ().getMetadata ().getLabels ());
153
+ desiredService .addOwnerReference (webPage );
154
+ return desiredService ;
155
+ }
156
+
157
+ private Deployment makeDesiredDeployment (WebPage webPage , String deploymentName , String ns ,
158
+ String configMapName ) {
159
+ Deployment desiredDeployment =
160
+ ReconcilerUtils .loadYaml (Deployment .class , getClass (), "deployment.yaml" );
161
+ desiredDeployment .getMetadata ().setName (deploymentName );
162
+ desiredDeployment .getMetadata ().setNamespace (ns );
163
+ desiredDeployment .getMetadata ().setLabels (lowLevelLabel ());
164
+ desiredDeployment .getSpec ().getSelector ().getMatchLabels ().put ("app" , deploymentName );
165
+ desiredDeployment .getSpec ().getTemplate ().getMetadata ().getLabels ().put ("app" , deploymentName );
166
+ desiredDeployment
167
+ .getSpec ()
168
+ .getTemplate ()
169
+ .getSpec ()
170
+ .getVolumes ()
171
+ .get (0 )
172
+ .setConfigMap (new ConfigMapVolumeSourceBuilder ().withName (configMapName ).build ());
173
+ desiredDeployment .addOwnerReference (webPage );
174
+ return desiredDeployment ;
175
+ }
176
+
177
+ private ConfigMap makeDesiredHtmlConfigMap (String ns , String configMapName , WebPage webPage ) {
178
+ Map <String , String > data = new HashMap <>();
179
+ data .put ("index.html" , webPage .getSpec ().getHtml ());
180
+ ConfigMap configMap =
181
+ new ConfigMapBuilder ()
182
+ .withMetadata (
183
+ new ObjectMetaBuilder ()
184
+ .withName (configMapName )
185
+ .withNamespace (ns )
186
+ .withLabels (lowLevelLabel ())
187
+ .build ())
188
+ .withData (data )
189
+ .build ();
190
+ configMap .addOwnerReference (webPage );
191
+ return configMap ;
192
+ }
193
+
194
+ private Map <String , String > lowLevelLabel () {
195
+ Map <String , String > labels = new HashMap <>();
196
+ labels .put (LOW_LEVEL_LABEL_KEY , "true" );
197
+ return labels ;
131
198
}
132
199
133
200
private static String configMapName (WebPage nginx ) {
@@ -142,45 +209,10 @@ private static String serviceName(WebPage nginx) {
142
209
return nginx .getMetadata ().getName ();
143
210
}
144
211
145
- private class ConfigMapDependentResource extends KubernetesDependentResource <ConfigMap , WebPage >
146
- implements
147
- AssociatedSecondaryResourceIdentifier <WebPage >, Updater <ConfigMap , WebPage > {
148
-
149
- @ Override
150
- protected ConfigMap desired (WebPage webPage , Context context ) {
151
- Map <String , String > data = new HashMap <>();
152
- data .put ("index.html" , webPage .getSpec ().getHtml ());
153
- return new ConfigMapBuilder ()
154
- .withMetadata (
155
- new ObjectMetaBuilder ()
156
- .withName (WebPageReconciler .configMapName (webPage ))
157
- .withNamespace (webPage .getMetadata ().getNamespace ())
158
- .build ())
159
- .withData (data )
160
- .build ();
161
- }
162
-
163
- @ Override
164
- public boolean match (ConfigMap actual , ConfigMap target , Context context ) {
165
- return StringUtils .equals (
166
- actual .getData ().get ("index.html" ), target .getData ().get ("index.html" ));
167
- }
168
-
169
- @ Override
170
- public void update (ConfigMap actual , ConfigMap target , WebPage primary , Context context ) {
171
- super .update (actual , target , primary , context );
172
- var ns = actual .getMetadata ().getNamespace ();
173
- log .info ("Restarting pods because HTML has changed in {}" , ns );
174
- kubernetesClient
175
- .pods ()
176
- .inNamespace (ns )
177
- .withLabel ("app" , deploymentName (primary ))
178
- .delete ();
179
- }
180
-
181
- @ Override
182
- public ResourceID associatedSecondaryID (WebPage primary ) {
183
- return new ResourceID (configMapName (primary ), primary .getMetadata ().getNamespace ());
184
- }
212
+ @ Override
213
+ public Optional <WebPage > updateErrorStatus (
214
+ WebPage resource , RetryInfo retryInfo , RuntimeException e ) {
215
+ resource .getStatus ().setErrorMessage ("Error: " + e .getMessage ());
216
+ return Optional .of (resource );
185
217
}
186
218
}
0 commit comments