20
20
21
21
import java .util .function .Function ;
22
22
import java .util .stream .Collectors ;
23
+
23
24
import org .apache .commons .lang3 .StringUtils ;
25
+ import org .apache .http .Header ;
26
+ import org .apache .http .client .ClientProtocolException ;
27
+ import org .apache .http .client .methods .CloseableHttpResponse ;
28
+ import org .apache .http .client .methods .HttpPost ;
29
+ import org .apache .http .client .methods .HttpUriRequest ;
30
+ import org .apache .http .client .protocol .HttpClientContext ;
31
+ import org .apache .http .impl .client .CloseableHttpClient ;
32
+ import org .apache .http .message .BasicHeader ;
33
+ import org .apache .http .protocol .HttpContext ;
34
+ import org .apache .http .util .EntityUtils ;
24
35
import org .apache .kafka .common .config .Config ;
25
36
import org .apache .kafka .common .config .ConfigDef ;
37
+ import org .apache .kafka .common .config .ConfigException ;
26
38
import org .apache .kafka .common .config .ConfigValue ;
27
39
import org .apache .kafka .connect .connector .Task ;
28
40
import org .apache .kafka .connect .sink .SinkConnector ;
29
41
42
+ import java .io .IOException ;
30
43
import java .util .ArrayList ;
31
44
import java .util .List ;
32
45
import java .util .Map ;
33
46
34
47
import org .slf4j .Logger ;
35
48
import org .slf4j .LoggerFactory ;
36
49
50
+ import com .splunk .hecclient .Event ;
51
+ import com .splunk .hecclient .EventBatch ;
52
+ import com .splunk .hecclient .JsonEvent ;
53
+ import com .splunk .hecclient .JsonEventBatch ;
54
+
37
55
public final class SplunkSinkConnector extends SinkConnector {
38
56
private static final Logger log = LoggerFactory .getLogger (SplunkSinkConnector .class );
39
57
private Map <String , String > taskConfig ;
40
58
private Map <String , ConfigValue > values ;
41
59
private List <ConfigValue > validations ;
60
+ private AbstractClientWrapper abstractClientWrapper = new HecClientWrapper ();
61
+
62
+
63
+ public void setHecInstance (AbstractClientWrapper abstractClientWrapper ) {
64
+ this .abstractClientWrapper = abstractClientWrapper ;
65
+ }
42
66
43
67
@ Override
44
68
public void start (Map <String , String > taskConfig ) {
@@ -76,14 +100,15 @@ public ConfigDef config() {
76
100
return SplunkSinkConnectorConfig .conf ();
77
101
}
78
102
79
-
103
+
80
104
@ Override
81
105
public Config validate (final Map <String , String > connectorConfigs ) {
82
106
Config config = super .validate (connectorConfigs );
83
107
validations = config .configValues ();
84
108
values = validations .stream ().collect (Collectors .toMap (ConfigValue ::name , Function .identity ()));
85
109
86
110
validateKerberosConfigs (connectorConfigs );
111
+ validateSplunkConfigurations (connectorConfigs );
87
112
return new Config (validations );
88
113
}
89
114
@@ -100,9 +125,9 @@ void validateKerberosConfigs(final Map<String, String> configs) {
100
125
}
101
126
102
127
String errorMessage = String .format (
103
- "Either both or neither '%s' and '%s' must be set for Kerberos authentication. " ,
104
- KERBEROS_KEYTAB_PATH_CONF ,
105
- KERBEROS_USER_PRINCIPAL_CONF
128
+ "Either both or neither '%s' and '%s' must be set for Kerberos authentication. " ,
129
+ KERBEROS_KEYTAB_PATH_CONF ,
130
+ KERBEROS_USER_PRINCIPAL_CONF
106
131
);
107
132
addErrorMessage (KERBEROS_KEYTAB_PATH_CONF , errorMessage );
108
133
addErrorMessage (KERBEROS_USER_PRINCIPAL_CONF , errorMessage );
@@ -111,4 +136,76 @@ void validateKerberosConfigs(final Map<String, String> configs) {
111
136
private void addErrorMessage (String property , String error ) {
112
137
values .get (property ).addErrorMessage (error );
113
138
}
114
- }
139
+
140
+ private static String [] split (String data , String sep ) {
141
+ if (data != null && !data .trim ().isEmpty ()) {
142
+ return data .trim ().split (sep );
143
+ }
144
+ return null ;
145
+ }
146
+
147
+
148
+ private void validateSplunkConfigurations (final Map <String , String > configs ) throws ConfigException {
149
+ SplunkSinkConnectorConfig connectorConfig = new SplunkSinkConnectorConfig (configs );
150
+ String [] indexes = split (connectorConfig .indexes , "," );
151
+ if (indexes == null || indexes .length == 0 ) {
152
+ preparePayloadAndExecuteRequest (connectorConfig , "" );
153
+ } else {
154
+ for (String index : indexes ) {
155
+ preparePayloadAndExecuteRequest (connectorConfig , index );
156
+ }
157
+ }
158
+ }
159
+
160
+ private void preparePayloadAndExecuteRequest (SplunkSinkConnectorConfig connectorConfig , String index ) throws ConfigException {
161
+ Header [] headers = new Header []{new BasicHeader ("Authorization" , String .format ("Splunk %s" , connectorConfig .splunkToken ))};
162
+ String endpoint = "/services/collector" ;
163
+ String url = connectorConfig .splunkURI + endpoint ;
164
+ final HttpPost httpPost = new HttpPost (url );
165
+ httpPost .setHeaders (headers );
166
+ EventBatch batch = new JsonEventBatch ();
167
+ Event event = new JsonEvent ("Splunk HEC Configuration Check" , null );
168
+ event .setIndex (index );
169
+ event .setSource ("kafka-connect" );
170
+ event .setSourcetype ("kafka-connect" );
171
+ batch .add (event );
172
+ httpPost .setEntity (batch .getHttpEntity ());
173
+ CloseableHttpClient httpClient = abstractClientWrapper .getClient (connectorConfig .getHecConfig ());
174
+ executeHttpRequest (httpPost , httpClient );
175
+ }
176
+
177
+
178
+
179
+ private void executeHttpRequest (final HttpUriRequest req , CloseableHttpClient httpClient ) throws ConfigException {
180
+ CloseableHttpResponse resp = null ;
181
+ HttpContext context ;
182
+ context = HttpClientContext .create ();
183
+ try {
184
+ resp = httpClient .execute (req , context );
185
+ int status = resp .getStatusLine ().getStatusCode ();
186
+
187
+ String respPayload = EntityUtils .toString (resp .getEntity (), "utf-8" );
188
+ if (status > 299 ){
189
+ throw new ConfigException (String .format ("Bad splunk configurations with status code:%s response:%s" ,status ,respPayload ));
190
+ }
191
+ } catch (ClientProtocolException ex ) {
192
+ throw new ConfigException ("Invalid splunk SSL configuration detected while validating configuration" ,ex );
193
+ } catch (IOException ex ) {
194
+ throw new ConfigException ("Invalid Splunk Configurations" ,ex );
195
+ } catch (ConfigException ex ) {
196
+ throw ex ;
197
+ } catch (Exception ex ) {
198
+ throw new ConfigException ("failed to process http payload" ,ex );
199
+ } finally {
200
+ try {
201
+ if (resp != null ) {
202
+ resp .close ();
203
+ }
204
+ } catch (Exception ex ) {
205
+ throw new ConfigException ("failed to close http response" ,ex );
206
+ }
207
+ }
208
+ }
209
+
210
+
211
+ }
0 commit comments