Skip to content
This repository has been archived by the owner on May 12, 2021. It is now read-only.

METRON-503: Metron REST API #316

Closed
wants to merge 72 commits into from
Closed

Conversation

merrimanr
Copy link
Contributor

This pull request is the first version of the REST API. I intentionally kept it simple by including only one well understood service: CRUD interface for SensorParserConfigs. While I feel it's solid (100% test coverage, integration tests that leverages in-memory component testing infrastructure, no PMD warnings, etc), there are several areas that should be reviewed:

  • Maven POM files
    • Is it consistent with other Metron components?
    • Are any plugins missing?
  • API structure
    • what should the paths look like?
  • Project structure
    • Is the code in the right place?
  • Response behavior
    • What should clients see on failures? Should 500 codes be returned?

I'm sure there are other issues I'm not thinking of. Once we can come to a consensus on this initial PR, other services can be added that follow the same patterns we establish here.

This can be tested by either running the unit/integration tests or running the service against a vagrant environment. To do the latter:

  • Build the quick-dev environment
  • Run "mvn clean package" from within metron-rest
  • Start the service by running "java -jar target/metron-rest-0.2.1BETA.jar"
  • Test the REST service (running "curl http://localhost:8080/api/v1/sensorParserConfigs" should return all the parser configs loaded during the quick-dev build)

There is much more to do (deployment, packaging, etc) but this should get us started.

@nickwallen
Copy link
Contributor

@merrimanr On this one, we need to address a licensing issue.

Traceback (most recent call last):
  File "build_utils/verify_license.py", line 41, in <module>
    raise ValueError("Unable to find " + component + " in acceptable list of components: " + sys.argv[1])
ValueError: Unable to find com.fasterxml:classmate:jar:1.3.1:compile in acceptable list of components: ./dependencies_with_url.csv
``

@merrimanr merrimanr changed the title Initial implementation of REST service METRON-503: Metron REST API Dec 9, 2016
@merrimanr
Copy link
Contributor Author

merrimanr commented Dec 9, 2016

Just updated this PR to include several changes/improvements:

  • Licensing documentation
  • Expanded to include more services: Kafka, SensorEnrichmentConfig, GlobalConfig, Storm, Grok, Stellar Transformations
  • Basic security
  • Swagger for testing and interacting with the API
  • Docker Spring profile for running against a Metron Docker environment

Documentation for installing the REST service is included in the README. To test in an IDE against a Metron Docker environment (assuming that is running):

  • change the MySQL and Hibernate dependencies from provided to compile
  • Use org.apache.metron.rest.MetronRestApplication as the main class
  • pass in "--spring.profiles.active=docker" as an argument or set the Spring profile to "docker" in your IDE

I am planning on making this more straightforward with a Maven profile soon.

During a review of the licenses we learned that the Hibernate license is not Apache friendly. I am planning to substitute EclipseLink as the JPA implementation in the future. This is why the Hibernate installation is a separate install step.

Copy link
Member

@cestella cestella left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, first VERY quick pass at this. It's a lot of code and expect a few more passes.

I'd like to request kindly that the single +1 rule be abbreviated for this PR and the UI PR. They're really big and I think at least 2 committers should give their blessing before they get in.

As always, thanks for the contribution. A middle tier is very welcome addition. :)

auth
.jdbcAuthentication()
.dataSource(dataSource)
.withUser("user").password("password").roles("USER").and()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these hard coded users? Are there instructions on adding new users?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instructions have been added to the README.

public class GrokService {

public static final String GROK_PATH_SPRING_PROPERTY = "grok.path";
public static final String GROK_CLASS_NAME = "org.apache.metron.parsers.GrokParser";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do GrokParser.class.toString() here instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is done with the assumption you meant "GrokParser.class.getName()".

if (!isTemporary) {
hdfsService.write(new Path(grokPath), fullGrokStatement.getBytes());
} else {
FileWriter fileWriter = new FileWriter(new File(grokPath));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if multiple users are using this simultaneously? Won't these temp files conflict?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a property in the config to set the temporary path and appended a "user" subdirectory.

add("geo");
add("host");
add("whois");
add("sample");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's sample?

}

@Autowired
private GrokService grokService;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have a GrokService in a more general SensorParserConfigService? Is there something coupled between grok and the general parser?

Half of this class seems grok specific and the rest is generic to BasicParser, so I'm wondering if this could be abstracted differently.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the code in the GrokService class handles reading and writing Grok statements from/to HDFS. If we merge the PR to keep grok statements in Zookeeper this class can go away.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The grok service should be replaced with a generic component interface that handles configurations to/from X, with hdfs and zookeeper implementations in general, but this is the wrong pr


protected String[] getParserStartCommand(String name) {
String[] command = new String[7];
command[0] = environment.getProperty(PARSER_SCRIPT_PATH_SPRING_PROPERTY);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these services have to be running on a node with the metron scripts? It appears so from this, but I wanted to make sure. If it is, can we make sure we have that spelled out specifically in the README.md

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been added to the prerequisites section of the README.

return stellarFunctionDescriptions;
}

private List<String> simpleFunctionNames = Arrays.asList(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you pull these from the classpath based on the Stellar annotation? I presume "simple" in this case are stellar functions with one argument (that might be a relevant comment in the code actually). You should be able to get the list of stellar functions via reflection and then get the number of params to filter them out.

1. Start the UI with this command:
```
./bin/start.sh /path/to/application.yml
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we get a section laying out and documenting all of the API endpoints for these services, split by service?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added annotations to all the Controller methods, much like the Stellar functions. I also created a script that parses the annotations and generates the README from a velocity template. If we decide we like that approach and want to add it to Stellar, we can look at incorporating it into our Maven build process.

rmerriman added 3 commits December 12, 2016 13:20
- hbase config is now read from Storm classpath instead of hbase jar
- removed global config push from kafkazk init script
- added license to hbase/bin/init-commands.txt
- improved data generator script and updated documentation
@merrimanr
Copy link
Contributor Author

Just pushed out commits to address these bugs. In regards to this:

"Another thing that may be nice is to describe what is all needed for the REST API to run. For example, I believe it needs access to storm cli, some scripts, and some other things."

This is included at the top of the metron-rest README in the Prerequisites section. It's changed a couple times so you may have just missed it. If there are other dependencies you think we should call out, let me know.

Thanks for the feedback. I think we're almost there.

@merrimanr
Copy link
Contributor Author

Removing the org.reflections dependency from the metron-rest pom and inheriting it from metron-common seems to have resolved the "java.lang.NoSuchMethodError" issue with the Stellar endpoints.

Changing the metron-common shade plugin to not install the metron-common uber jar by default also solved the issue but that belongs in another Jira.

@jjmeyer0
Copy link
Contributor

@merrimanr For some reason I am still getting the following error on the endpoints. Also, the no such method error turned into this as well. How are you running it? I'm running it using java cli. Are you using IntelliJ or something else?

Feb 23, 2017 12:51:24 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.NoSuchMethodError: org.reflections.util.ConfigurationBuilder.filterInputsBy(Lorg/apache/metron/guava/base/Predicate;)Lorg/reflections/util/ConfigurationBuilder;] with root cause
java.lang.NoSuchMethodError: org.reflections.util.ConfigurationBuilder.filterInputsBy(Lorg/apache/metron/guava/base/Predicate;)Lorg/reflections/util/ConfigurationBuilder;
        at org.apache.metron.common.dsl.functions.resolver.ClasspathFunctionResolver.resolvables(ClasspathFunctionResolver.java:167)
        at org.apache.metron.common.dsl.functions.resolver.BaseFunctionResolver.resolveFunctions(BaseFunctionResolver.java:119)
        at org.apache.metron.guava.base.Suppliers$MemoizingSupplier.get(Suppliers.java:125)
        at org.apache.metron.common.dsl.functions.resolver.BaseFunctionResolver.getFunctionInfo(BaseFunctionResolver.java:77)
        at org.apache.metron.rest.service.impl.StellarServiceImpl.getStellarFunctions(StellarServiceImpl.java:74)
        at org.apache.metron.rest.controller.StellarController.listFunctions(StellarController.java:71)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:215)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:121)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:89)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:784)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:802)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1410)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:745)

@merrimanr
Copy link
Contributor Author

Just merged in the latest from master including the maxmind fix that was breaking the build.

I am not able to recreate the NoSuchError exception. Here is what I'm doing to test:
//from project root
mvn clean install -DskipTests
java -jar ./metron-interface/metron-rest/target/metron-rest-0.3.1.jar --spring.profiles.active=docker,dev

When I hit the stellar endpoints, the functions come back without error. How are you testing it?

@jjmeyer0
Copy link
Contributor

@merrimanr I'm using quick-dev to do my tests. I built everything from metron root with mvn clean install -DskipTests. Then I setup quick-dev. Extracted the tar and ran the following:

java -jar ./lib/metron-rest-$METRON_VERSION.jar --spring.profiles.active=vagrant,dev --server.port=8082

@merrimanr
Copy link
Contributor Author

Hmm I just tried deploying on quick-dev the same way and was able to hit the stellar endpoints without issue. I will continue testing to see if I can get it to break tomorrow.

@jjmeyer0
Copy link
Contributor

@merrimanr I had a chance to try this again. If I build from incubator-metron it doesn't work. If I build from metron-interface everything seems to be working just fine.

@merrimanr
Copy link
Contributor Author

Finally figured this one out. I wasn't able to reproduce the issue because my maven version was a couple minor releases behind (on 3.3.9 now for what it's worth). Once I got past that the problem was obvious: metron-common was bringing in shaded org.reflections classes directly and regular org.reflections classes transitively which are not compatible because guava is rewritten in the shaded classes, hence the NoSuchMethodError. So for now the fix is to exclude org.reflections from the metron-common dependency and only depend on the shaded version.

I had to merge several commits in so I'm still testing for regressions but this issue should be resolved.

@jjmeyer0
Copy link
Contributor

@merrimanr Good catch on this. I'll take a look again, but I think it's looking good!

@jjmeyer0
Copy link
Contributor

jjmeyer0 commented Mar 1, 2017

First off, I'd like to apologize for it taking me a little longer to retest this.

I went through and tested the issues I saw again. Everything seems to be working. I think this is ready to go. It's a great feature. Thanks for implementing it!

Also, since this is a big PR, don't worry about giving me credit on my commits. Especially if it makes your life easier when squashing everything. If that's allowed anyway.

+1

@cestella
Copy link
Member

cestella commented Mar 2, 2017

Ok, this is a big one and has been around for a looooong time now in Metron-years. Before we go ahead and pull the trigger on commit, I want to make sure there are no outstanding comments that need to be addressed.

@cestella
Copy link
Member

cestella commented Mar 2, 2017

+1 by the way ;)

@cestella
Copy link
Member

cestella commented Mar 2, 2017

@merrimanr @jjmeyer0 I think we should make sure JJ gets credit for his contribution on this. We should squash the commits into the minimal possible each named "METRON-503: Metron REST API this closes #316" with the appropriate authorship.

cestella pushed a commit to cestella/incubator-metron that referenced this pull request Mar 2, 2017
cestella pushed a commit to cestella/incubator-metron that referenced this pull request Mar 2, 2017
cestella pushed a commit to cestella/incubator-metron that referenced this pull request Mar 2, 2017
@cestella
Copy link
Member

cestella commented Mar 2, 2017

For convenience, I staged the commit in a remote branch: https://github.com/cestella/incubator-metron/tree/METRON-503_sandbox . It'll run in my personal travis account. I'd ask that @merrimanr take it for a test drive.

@merrimanr
Copy link
Contributor Author

All integration tests are passing. I was able to start up the application against quick dev and all the endpoints look like they are working.

cestella pushed a commit to cestella/incubator-metron that referenced this pull request Mar 2, 2017
cestella pushed a commit to cestella/incubator-metron that referenced this pull request Mar 2, 2017
@asfgit asfgit closed this in 55b3e7e Mar 2, 2017
lucesape pushed a commit to repairnator/repairnator-experiments that referenced this pull request Mar 9, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
5 participants