blob: e6163c0ae78f4cb0938bef363c0d92da65b7f14d [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.metron.rest.service.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;
import oi.thekraken.grok.api.Grok;
import org.adrianwalker.multilinestring.Multiline;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.api.DeleteBuilder;
import org.apache.curator.framework.api.GetChildrenBuilder;
import org.apache.curator.framework.api.GetDataBuilder;
import org.apache.curator.framework.api.SetDataBuilder;
import org.apache.hadoop.conf.Configuration;
import org.apache.metron.common.configuration.ConfigurationType;
import org.apache.metron.common.configuration.ParserConfigurations;
import org.apache.metron.common.configuration.SensorParserConfig;
import org.apache.metron.common.zookeeper.ConfigurationsCache;
import org.apache.metron.rest.RestException;
import org.apache.metron.rest.model.ParseMessageRequest;
import org.apache.metron.rest.service.GrokService;
import org.apache.metron.rest.service.SensorParserConfigService;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.data.Stat;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.core.env.Environment;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import static org.apache.metron.rest.MetronRestConstants.GROK_TEMP_PATH_SPRING_PROPERTY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SuppressWarnings("ALL")
public class SensorParserConfigServiceImplTest {
@Rule
public final ExpectedException exception = ExpectedException.none();
Environment environment;
ObjectMapper objectMapper;
CuratorFramework curatorFramework;
GrokService grokService;
SensorParserConfigService sensorParserConfigService;
/**
{
"parserClassName": "org.apache.metron.parsers.GrokParser",
"sensorTopic": "squid",
"parserConfig": {
"grokPath": "/patterns/squid",
"patternLabel": "SQUID_DELIMITED",
"timestampField": "timestamp"
}
}
*/
@Multiline
public static String squidJson;
/**
{
"parserClassName":"org.apache.metron.parsers.bro.BasicBroParser",
"sensorTopic":"bro",
"parserConfig": {}
}
*/
@Multiline
public static String broJson;
private String user = "user1";
ConfigurationsCache cache;
@Before
public void setUp() throws Exception {
objectMapper = mock(ObjectMapper.class);
curatorFramework = mock(CuratorFramework.class);
Environment environment = mock(Environment.class);
Authentication authentication = mock(Authentication.class);
when(authentication.getName()).thenReturn(user);
SecurityContextHolder.getContext().setAuthentication(authentication);
when(environment.getProperty(GROK_TEMP_PATH_SPRING_PROPERTY)).thenReturn("./target");
grokService = new GrokServiceImpl(environment, mock(Grok.class), new HdfsServiceImpl(new Configuration()));
cache = mock(ConfigurationsCache.class);
sensorParserConfigService = new SensorParserConfigServiceImpl(objectMapper, curatorFramework, grokService, cache);
}
@Test
public void deleteShouldProperlyCatchNoNodeExceptionAndReturnFalse() throws Exception {
DeleteBuilder builder = mock(DeleteBuilder.class);
when(curatorFramework.delete()).thenReturn(builder);
when(builder.forPath(ConfigurationType.PARSER.getZookeeperRoot() + "/bro")).thenThrow(KeeperException.NoNodeException.class);
assertFalse(sensorParserConfigService.delete("bro"));
}
@Test
public void deleteShouldProperlyCatchNonNoNodeExceptionAndThrowRestException() throws Exception {
exception.expect(RestException.class);
DeleteBuilder builder = mock(DeleteBuilder.class);
when(curatorFramework.delete()).thenReturn(builder);
when(builder.forPath(ConfigurationType.PARSER.getZookeeperRoot() + "/bro")).thenThrow(Exception.class);
assertFalse(sensorParserConfigService.delete("bro"));
}
@Test
public void deleteShouldReturnTrueWhenClientSuccessfullyCallsDelete() throws Exception {
DeleteBuilder builder = mock(DeleteBuilder.class);
when(curatorFramework.delete()).thenReturn(builder);
when(builder.forPath(ConfigurationType.PARSER.getZookeeperRoot() + "/bro")).thenReturn(null);
assertTrue(sensorParserConfigService.delete("bro"));
verify(curatorFramework).delete();
}
@Test
public void findOneShouldProperlyReturnSensorParserConfig() throws Exception {
final SensorParserConfig sensorParserConfig = getTestBroSensorParserConfig();
ParserConfigurations configs = new ParserConfigurations(){
@Override
public Map<String, Object> getConfigurations() {
return ImmutableMap.of(ParserConfigurations.getKey("bro"), sensorParserConfig);
}
};
when(cache.get(eq(ParserConfigurations.class)))
.thenReturn(configs);
//We only have bro, so we should expect it to be returned
assertEquals(getTestBroSensorParserConfig(), sensorParserConfigService.findOne("bro"));
//and blah should be a miss.
assertNull(sensorParserConfigService.findOne("blah"));
}
@Test
public void getAllTypesShouldProperlyReturnTypes() throws Exception {
ParserConfigurations configs = new ParserConfigurations(){
@Override
public Map<String, Object> getConfigurations() {
return ImmutableMap.of(ParserConfigurations.getKey("bro"), new HashMap<>()
,ParserConfigurations.getKey("squid"), new HashMap<>()
);
}
};
when(cache.get( eq(ParserConfigurations.class)))
.thenReturn(configs);
assertEquals(new ArrayList() {{
add("bro");
add("squid");
}}, sensorParserConfigService.getAllTypes());
}
@Test
public void getAllShouldProperlyReturnSensorParserConfigs() throws Exception {
final SensorParserConfig broSensorParserConfig = getTestBroSensorParserConfig();
final SensorParserConfig squidSensorParserConfig = getTestSquidSensorParserConfig();
ParserConfigurations configs = new ParserConfigurations(){
@Override
public Map<String, Object> getConfigurations() {
return ImmutableMap.of(ParserConfigurations.getKey("bro"), broSensorParserConfig
,ParserConfigurations.getKey("squid"), squidSensorParserConfig
);
}
};
when(cache.get( eq(ParserConfigurations.class)))
.thenReturn(configs);
assertEquals(new HashMap() {{
put("bro", getTestBroSensorParserConfig());
put("squid", getTestSquidSensorParserConfig());
}}, sensorParserConfigService.getAll());
}
@Test
public void saveShouldWrapExceptionInRestException() throws Exception {
exception.expect(RestException.class);
SetDataBuilder setDataBuilder = mock(SetDataBuilder.class);
when(setDataBuilder.forPath(ConfigurationType.PARSER.getZookeeperRoot() + "/bro", broJson.getBytes())).thenThrow(Exception.class);
when(curatorFramework.setData()).thenReturn(setDataBuilder);
final SensorParserConfig sensorParserConfig = new SensorParserConfig();
sensorParserConfig.setSensorTopic("bro");
sensorParserConfigService.save("bro", sensorParserConfig);
}
@Test
public void saveShouldReturnSameConfigThatIsPassedOnSuccessfulSave() throws Exception {
final SensorParserConfig sensorParserConfig = getTestBroSensorParserConfig();
when(objectMapper.writeValueAsString(sensorParserConfig)).thenReturn(broJson);
SetDataBuilder setDataBuilder = mock(SetDataBuilder.class);
when(setDataBuilder.forPath(ConfigurationType.PARSER.getZookeeperRoot() + "/bro", broJson.getBytes())).thenReturn(new Stat());
when(curatorFramework.setData()).thenReturn(setDataBuilder);
assertEquals(getTestBroSensorParserConfig(), sensorParserConfigService.save("bro", sensorParserConfig));
verify(setDataBuilder).forPath(eq(ConfigurationType.PARSER.getZookeeperRoot() + "/bro"), eq(broJson.getBytes()));
}
@Test
public void reloadAvailableParsersShouldReturnParserClasses() throws Exception {
Map<String, String> availableParsers = sensorParserConfigService.reloadAvailableParsers();
assertTrue(availableParsers.size() > 0);
assertEquals("org.apache.metron.parsers.GrokParser", availableParsers.get("Grok"));
assertEquals("org.apache.metron.parsers.bro.BasicBroParser", availableParsers.get("Bro"));
}
@Test
public void parseMessageShouldProperlyReturnParsedResults() throws Exception {
final SensorParserConfig sensorParserConfig = getTestSquidSensorParserConfig();
String grokStatement = "SQUID_DELIMITED %{NUMBER:timestamp}[^0-9]*%{INT:elapsed} %{IP:ip_src_addr} %{WORD:action}/%{NUMBER:code} %{NUMBER:bytes} %{WORD:method} %{NOTSPACE:url}[^0-9]*(%{IP:ip_dst_addr})?";
String sampleData = "1461576382.642 161 127.0.0.1 TCP_MISS/200 103701 GET http://www.cnn.com/ - DIRECT/199.27.79.73 text/html";
ParseMessageRequest parseMessageRequest = new ParseMessageRequest();
parseMessageRequest.setSensorParserConfig(sensorParserConfig);
parseMessageRequest.setGrokStatement(grokStatement);
parseMessageRequest.setSampleData(sampleData);
File grokRoot = new File("./target", user);
grokRoot.mkdir();
File patternFile = new File(grokRoot, "squid");
FileWriter writer = new FileWriter(patternFile);
writer.write(grokStatement);
writer.close();
assertEquals(new HashMap() {{
put("elapsed", 161);
put("code", 200);
put("ip_dst_addr", "199.27.79.73");
put("ip_src_addr", "127.0.0.1");
put("action", "TCP_MISS");
put("bytes", 103701);
put("method", "GET");
put("url", "http://www.cnn.com/");
put("timestamp", 1461576382642L);
put("original_string", "1461576382.642 161 127.0.0.1 TCP_MISS/200 103701 GET http://www.cnn.com/ - DIRECT/199.27.79.73 text/html");
}}, sensorParserConfigService.parseMessage(parseMessageRequest));
}
@Test
public void missingSensorParserConfigShouldThrowRestException() throws Exception {
exception.expect(RestException.class);
ParseMessageRequest parseMessageRequest = new ParseMessageRequest();
sensorParserConfigService.parseMessage(parseMessageRequest);
}
@Test
public void missingParserClassShouldThrowRestException() throws Exception {
exception.expect(RestException.class);
final SensorParserConfig sensorParserConfig = new SensorParserConfig();
sensorParserConfig.setSensorTopic("squid");
ParseMessageRequest parseMessageRequest = new ParseMessageRequest();
parseMessageRequest.setSensorParserConfig(sensorParserConfig);
sensorParserConfigService.parseMessage(parseMessageRequest);
}
@Test
public void invalidParserClassShouldThrowRestException() throws Exception {
exception.expect(RestException.class);
final SensorParserConfig sensorParserConfig = new SensorParserConfig();
sensorParserConfig.setSensorTopic("squid");
sensorParserConfig.setParserClassName("bad.class.package.BadClassName");
ParseMessageRequest parseMessageRequest = new ParseMessageRequest();
parseMessageRequest.setSensorParserConfig(sensorParserConfig);
sensorParserConfigService.parseMessage(parseMessageRequest);
}
private SensorParserConfig getTestBroSensorParserConfig() {
SensorParserConfig sensorParserConfig = new SensorParserConfig();
sensorParserConfig.setSensorTopic("bro");
sensorParserConfig.setParserClassName("org.apache.metron.parsers.bro.BasicBroParser");
return sensorParserConfig;
}
private SensorParserConfig getTestSquidSensorParserConfig() {
SensorParserConfig sensorParserConfig = new SensorParserConfig();
sensorParserConfig.setSensorTopic("squid");
sensorParserConfig.setParserClassName("org.apache.metron.parsers.GrokParser");
sensorParserConfig.setParserConfig(new HashMap() {{
put("grokPath", "/patterns/squid");
put("patternLabel", "SQUID_DELIMITED");
put("timestampField", "timestamp");
}});
return sensorParserConfig;
}
}