6
6
import com .michelin .ns4kafka .services .KafkaConnectService ;
7
7
import io .micronaut .http .HttpResponse ;
8
8
import io .micronaut .http .HttpStatus ;
9
+ import io .micronaut .http .MutableHttpResponse ;
9
10
import io .micronaut .http .annotation .*;
10
11
import io .micronaut .scheduling .TaskExecutors ;
11
12
import io .micronaut .scheduling .annotation .ExecuteOn ;
13
+ import io .reactivex .Single ;
12
14
import io .swagger .v3 .oas .annotations .tags .Tag ;
13
15
14
16
import javax .inject .Inject ;
17
19
import java .util .Date ;
18
20
import java .util .List ;
19
21
import java .util .Optional ;
20
- import java .util .concurrent .ExecutionException ;
21
- import java .util .concurrent .TimeoutException ;
22
22
import java .util .stream .Collectors ;
23
23
24
24
@ Tag (name = "Connects" )
25
25
@ Controller (value = "/api/namespaces/{namespace}/connects" )
26
26
@ ExecuteOn (TaskExecutors .IO )
27
27
public class ConnectController extends NamespacedResourceController {
28
28
/**
29
- * The connector service
29
+ * Message threw when namespace is not owner of the given connector
30
+ */
31
+ private final String NAMESPACE_NOT_OWNER = "Namespace not owner of this connector %s." ;
32
+
33
+ /**
34
+ * Connector service
30
35
*/
31
36
@ Inject
32
37
KafkaConnectService kafkaConnectService ;
38
+
33
39
/**
34
40
* Get all the connectors by namespace
35
41
* @param namespace The namespace
@@ -60,23 +66,22 @@ public Optional<Connector> getConnector(String namespace, String connector) {
60
66
*/
61
67
@ Status (HttpStatus .NO_CONTENT )
62
68
@ Delete ("/{connector}{?dryrun}" )
63
- public HttpResponse <Void > deleteConnector (String namespace , String connector , @ QueryValue (defaultValue = "false" ) boolean dryrun ) {
69
+ public Single < HttpResponse <Void > > deleteConnector (String namespace , String connector , @ QueryValue (defaultValue = "false" ) boolean dryrun ) {
64
70
Namespace ns = getNamespace (namespace );
65
71
66
72
// Validate ownership
67
73
if (!kafkaConnectService .isNamespaceOwnerOfConnect (ns , connector )) {
68
- throw new ResourceValidationException (List .of ("Invalid value " + connector +
69
- " for name: Namespace not OWNER of this connector" ), " Connector" , connector );
74
+ return Single . error ( new ResourceValidationException (List .of (String . format ( NAMESPACE_NOT_OWNER , connector )),
75
+ "Connector" , connector ) );
70
76
}
71
77
72
78
Optional <Connector > optionalConnector = kafkaConnectService .findByName (ns , connector );
73
-
74
79
if (optionalConnector .isEmpty ()) {
75
- return HttpResponse .notFound ();
80
+ return Single . just ( HttpResponse .notFound () );
76
81
}
77
82
78
83
if (dryrun ) {
79
- return HttpResponse .noContent ();
84
+ return Single . just ( HttpResponse .noContent () );
80
85
}
81
86
82
87
Connector connectorToDelete = optionalConnector .get ();
@@ -86,9 +91,9 @@ public HttpResponse<Void> deleteConnector(String namespace, String connector, @Q
86
91
connectorToDelete .getSpec (),
87
92
null );
88
93
89
- kafkaConnectService . delete ( ns , optionalConnector . get ());
90
-
91
- return HttpResponse .noContent ();
94
+ return kafkaConnectService
95
+ . delete ( ns , optionalConnector . get ())
96
+ . map ( httpResponse -> HttpResponse .noContent () );
92
97
}
93
98
94
99
/**
@@ -99,13 +104,13 @@ public HttpResponse<Void> deleteConnector(String namespace, String connector, @Q
99
104
* @return The created schema
100
105
*/
101
106
@ Post ("{?dryrun}" )
102
- public HttpResponse <Connector > apply (String namespace , @ Valid @ Body Connector connector , @ QueryValue (defaultValue = "false" ) boolean dryrun ) {
107
+ public Single < HttpResponse <Connector > > apply (String namespace , @ Valid @ Body Connector connector , @ QueryValue (defaultValue = "false" ) boolean dryrun ) {
103
108
Namespace ns = getNamespace (namespace );
104
109
105
110
// Validate ownership
106
111
if (!kafkaConnectService .isNamespaceOwnerOfConnect (ns , connector .getMetadata ().getName ())) {
107
- throw new ResourceValidationException (List .of ("Invalid value " + connector .getMetadata ().getName () +
108
- " for name: Namespace not OWNER of this connector" ), connector .getKind (), connector .getMetadata ().getName ());
112
+ return Single . error ( new ResourceValidationException (List .of (String . format ( NAMESPACE_NOT_OWNER , connector .getMetadata ().getName ())),
113
+ connector .getKind (), connector .getMetadata ().getName () ));
109
114
}
110
115
111
116
// Set / Override name in spec.config.name, required for several Kafka Connect API calls
@@ -119,97 +124,107 @@ public HttpResponse<Connector> apply(String namespace, @Valid @Body Connector co
119
124
connector .getSpec ().getConfig ().put ("name" , connector .getMetadata ().getName ());
120
125
121
126
// Validate locally
122
- List <String > validationErrors = kafkaConnectService .validateLocally (ns , connector );
123
- if (!validationErrors .isEmpty ()) {
124
- throw new ResourceValidationException (validationErrors , connector .getKind (), connector .getMetadata ().getName ());
125
- }
126
-
127
- // Validate against connect rest API /validate
128
- validationErrors = kafkaConnectService .validateRemotely (ns , connector );
129
- if (!validationErrors .isEmpty ()) {
130
- throw new ResourceValidationException (validationErrors , connector .getKind (), connector .getMetadata ().getName ());
131
- }
132
-
133
- // Augment with server side fields
134
- connector .getMetadata ().setCreationTimestamp (Date .from (Instant .now ()));
135
- connector .getMetadata ().setCluster (ns .getMetadata ().getCluster ());
136
- connector .getMetadata ().setNamespace (ns .getMetadata ().getName ());
137
- connector .setStatus (Connector .ConnectorStatus .builder ()
138
- .state (Connector .TaskState .UNASSIGNED )
139
- .build ());
140
-
141
- Optional <Connector > existingConnector = kafkaConnectService .findByName (ns , connector .getMetadata ().getName ());
142
- if (existingConnector .isPresent () && existingConnector .get ().equals (connector )) {
143
- return formatHttpResponse (existingConnector .get (), ApplyStatus .unchanged );
144
- }
145
-
146
- ApplyStatus status = existingConnector .isPresent () ? ApplyStatus .changed : ApplyStatus .created ;
147
- if (dryrun ) {
148
- return formatHttpResponse (connector , status );
149
- }
150
-
151
- sendEventLog (connector .getKind (),
152
- connector .getMetadata (),
153
- status ,
154
- existingConnector .<Object >map (Connector ::getSpec ).orElse (null ),
155
- connector .getSpec ());
156
-
157
- return formatHttpResponse (kafkaConnectService .createOrUpdate (connector ), status );
127
+ return kafkaConnectService .validateLocally (ns , connector )
128
+ .flatMap (validationErrors -> {
129
+ if (!validationErrors .isEmpty ()) {
130
+ return Single .error (new ResourceValidationException (validationErrors , connector .getKind (), connector .getMetadata ().getName ()));
131
+ }
132
+
133
+ // Validate against connect rest API /validate
134
+ return kafkaConnectService .validateRemotely (ns , connector )
135
+ .flatMap (remoteValidationErrors -> {
136
+ if (!remoteValidationErrors .isEmpty ()) {
137
+ return Single .error (new ResourceValidationException (remoteValidationErrors , connector .getKind (), connector .getMetadata ().getName ()));
138
+ }
139
+
140
+ // Augment with server side fields
141
+ connector .getMetadata ().setCreationTimestamp (Date .from (Instant .now ()));
142
+ connector .getMetadata ().setCluster (ns .getMetadata ().getCluster ());
143
+ connector .getMetadata ().setNamespace (ns .getMetadata ().getName ());
144
+ connector .setStatus (Connector .ConnectorStatus .builder ()
145
+ .state (Connector .TaskState .UNASSIGNED )
146
+ .build ());
147
+
148
+ Optional <Connector > existingConnector = kafkaConnectService .findByName (ns , connector .getMetadata ().getName ());
149
+ if (existingConnector .isPresent () && existingConnector .get ().equals (connector )) {
150
+ return Single .just (formatHttpResponse (existingConnector .get (), ApplyStatus .unchanged ));
151
+ }
152
+
153
+ ApplyStatus status = existingConnector .isPresent () ? ApplyStatus .changed : ApplyStatus .created ;
154
+ if (dryrun ) {
155
+ return Single .just (formatHttpResponse (connector , status ));
156
+ }
157
+
158
+ sendEventLog (connector .getKind (),
159
+ connector .getMetadata (),
160
+ status ,
161
+ existingConnector .<Object >map (Connector ::getSpec ).orElse (null ),
162
+ connector .getSpec ());
163
+
164
+ return Single .just (formatHttpResponse (kafkaConnectService .createOrUpdate (connector ), status ));
165
+ });
166
+ });
158
167
}
159
168
160
169
/**
161
170
* Change the state of a connector
162
171
* @param namespace The namespace
163
172
* @param connector The connector to update the state
164
173
* @param changeConnectorState The state to set
165
- * @return
174
+ * @return The change connector state response
166
175
*/
167
176
@ Post ("/{connector}/change-state" )
168
- public HttpResponse < ChangeConnectorState > changeState (String namespace , String connector , @ Body @ Valid ChangeConnectorState changeConnectorState ) {
177
+ public Single < MutableHttpResponse < ChangeConnectorState > > changeState (String namespace , String connector , @ Body @ Valid ChangeConnectorState changeConnectorState ) {
169
178
Namespace ns = getNamespace (namespace );
170
179
171
180
if (!kafkaConnectService .isNamespaceOwnerOfConnect (ns , connector )) {
172
- throw new ResourceValidationException (List .of ("Invalid value " + connector +
173
- " for name: Namespace not OWNER of this connector" ), " Connector" , connector );
181
+ return Single . error ( new ResourceValidationException (List .of (String . format ( NAMESPACE_NOT_OWNER , connector )),
182
+ "Connector" , connector ) );
174
183
}
175
184
176
185
Optional <Connector > optionalConnector = kafkaConnectService .findByName (ns , connector );
177
186
178
187
if (optionalConnector .isEmpty ()) {
179
- return HttpResponse .notFound ();
188
+ return Single . just ( HttpResponse .notFound () );
180
189
}
181
190
182
- HttpResponse response ;
183
- try {
184
- switch (changeConnectorState .getSpec ().getAction ()) {
185
- case restart :
186
- response = kafkaConnectService .restart (ns , optionalConnector .get ());
187
- break ;
188
- case pause :
189
- response = kafkaConnectService .pause (ns , optionalConnector .get ());
190
- break ;
191
- case resume :
192
- response = kafkaConnectService .resume (ns , optionalConnector .get ());
193
- break ;
194
- default :
195
- throw new IllegalStateException ("Unspecified Action " +changeConnectorState .getSpec ().getAction ());
196
- }
197
-
198
- changeConnectorState .setStatus (ChangeConnectorState .ChangeConnectorStateStatus .builder ()
199
- .success (true )
200
- .code (response .status ())
201
- .build ());
202
- } catch (Exception e ) {
203
- changeConnectorState .setStatus (ChangeConnectorState .ChangeConnectorStateStatus .builder ()
204
- .success (false )
205
- .code (HttpStatus .INTERNAL_SERVER_ERROR )
206
- .errorMessage (e .getMessage ())
207
- .build ());
191
+ Single <HttpResponse <Void >> response ;
192
+ switch (changeConnectorState .getSpec ().getAction ()) {
193
+ case restart :
194
+ response = kafkaConnectService .restart (ns , optionalConnector .get ());
195
+ break ;
196
+ case pause :
197
+ response = kafkaConnectService .pause (ns , optionalConnector .get ());
198
+ break ;
199
+ case resume :
200
+ response = kafkaConnectService .resume (ns , optionalConnector .get ());
201
+ break ;
202
+ default :
203
+ return Single .error (new IllegalStateException ("Unspecified action " + changeConnectorState .getSpec ().getAction ()));
208
204
}
209
205
210
- changeConnectorState .setMetadata (optionalConnector .get ().getMetadata ());
211
- changeConnectorState .getMetadata ().setCreationTimestamp (Date .from (Instant .now ()));
212
- return HttpResponse .ok (changeConnectorState );
206
+ return response
207
+ .doOnEvent ((httpResponse , httpError ) -> {
208
+ if (httpResponse != null ) {
209
+ changeConnectorState .setStatus (ChangeConnectorState .ChangeConnectorStateStatus .builder ()
210
+ .success (true )
211
+ .code (httpResponse .status ())
212
+ .build ());
213
+ }
214
+
215
+ if (httpError != null ) {
216
+ changeConnectorState .setStatus (ChangeConnectorState .ChangeConnectorStateStatus .builder ()
217
+ .success (false )
218
+ .code (HttpStatus .INTERNAL_SERVER_ERROR )
219
+ .errorMessage (httpError .getMessage ())
220
+ .build ());
221
+ }
222
+
223
+ changeConnectorState .setMetadata (optionalConnector .get ().getMetadata ());
224
+ changeConnectorState .getMetadata ().setCreationTimestamp (Date .from (Instant .now ()));
225
+ })
226
+ .map (httpResponse -> HttpResponse .ok (changeConnectorState ))
227
+ .onErrorReturnItem (HttpResponse .ok (changeConnectorState ));
213
228
}
214
229
215
230
/**
@@ -219,26 +234,27 @@ public HttpResponse<ChangeConnectorState> changeState(String namespace, String c
219
234
* @return The list of imported connectors
220
235
*/
221
236
@ Post ("/_/import{?dryrun}" )
222
- public List <Connector > importResources (String namespace , @ QueryValue (defaultValue = "false" ) boolean dryrun ) {
237
+ public Single < List <Connector > > importResources (String namespace , @ QueryValue (defaultValue = "false" ) boolean dryrun ) {
223
238
Namespace ns = getNamespace (namespace );
224
- List <Connector > unsynchronizedConnectors = kafkaConnectService .listUnsynchronizedConnectors (ns );
225
-
226
- unsynchronizedConnectors .forEach (connector -> {
227
- connector .getMetadata ().setCreationTimestamp (Date .from (Instant .now ()));
228
- connector .getMetadata ().setCluster (ns .getMetadata ().getCluster ());
229
- connector .getMetadata ().setNamespace (ns .getMetadata ().getName ());
230
- });
231
-
232
- if (dryrun ) {
233
- return unsynchronizedConnectors ;
234
- }
235
-
236
- return unsynchronizedConnectors
237
- .stream ()
238
- .map (connector -> {
239
- sendEventLog (connector .getKind (), connector .getMetadata (), ApplyStatus .created , null , connector .getSpec ());
240
- return kafkaConnectService .createOrUpdate (connector );
241
- })
242
- .collect (Collectors .toList ());
239
+ return kafkaConnectService .listUnsynchronizedConnectors (ns )
240
+ .map (unsynchronizedConnectors -> {
241
+ unsynchronizedConnectors .forEach (connector -> {
242
+ connector .getMetadata ().setCreationTimestamp (Date .from (Instant .now ()));
243
+ connector .getMetadata ().setCluster (ns .getMetadata ().getCluster ());
244
+ connector .getMetadata ().setNamespace (ns .getMetadata ().getName ());
245
+ });
246
+
247
+ if (dryrun ) {
248
+ return unsynchronizedConnectors ;
249
+ }
250
+
251
+ return unsynchronizedConnectors
252
+ .stream ()
253
+ .map (connector -> {
254
+ sendEventLog (connector .getKind (), connector .getMetadata (), ApplyStatus .created , null , connector .getSpec ());
255
+ return kafkaConnectService .createOrUpdate (connector );
256
+ })
257
+ .collect (Collectors .toList ());
258
+ });
243
259
}
244
260
}
0 commit comments