Skip to content

Commit

Permalink
+agent module (it proxy docker calls to http port, relieves user from…
Browse files Browse the repository at this point in the history
… publish api docker on tcp port - which is unsafe)
  • Loading branch information
wayerr committed Apr 6, 2017
1 parent f46f09c commit d8a7fe0
Show file tree
Hide file tree
Showing 6 changed files with 315 additions and 0 deletions.
21 changes: 21 additions & 0 deletions agent/Dockerfile
@@ -0,0 +1,21 @@
FROM debian:stretch

MAINTAINER docker@codeabovelab.com

RUN apt-get update && apt-get install -y --no-install-recommends \
openjdk-8-jdk-headless procps \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

LABEL service-type=system
LABEL arg.memory=512M
LABEL arg.restart=always
LABEL arg.ports=8771:8771

ENV JAVA_OPTS=" -Xms64M -Xmx512M -Xss256k -XX:+HeapDumpOnOutOfMemoryError "

EXPOSE 8771

ADD ./@artifactId@-*-boot.jar /@artifactId@-@version@.jar
ENTRYPOINT java -server -noverify $JAVA_OPTS -Djava.security.egd=file:/dev/urandom -jar /@artifactId@-@version@.jar \
--spring.profiles.active=staging --dm.swarm-exec.path=/gocode/bin/swarm
57 changes: 57 additions & 0 deletions agent/pom.xml
@@ -0,0 +1,57 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dm-parent</artifactId>
<groupId>com.codeabovelab.dm</groupId>
<version>1.2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>agent</artifactId>
<packaging>jar</packaging>
<description>Agent module. Proxy 'docker.sock' to http port.</description>
<properties>
<start-class>com.codeabovelab.dm.agent.Application</start-class>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

<dependencies>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>com.codeabovelab.dm</groupId>
<artifactId>gateway-common</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>



</dependencies>
</project>
48 changes: 48 additions & 0 deletions agent/src/main/java/com/codeabovelab/dm/agent/Application.java
@@ -0,0 +1,48 @@
/*
* Copyright 2017 Code Above Lab LLC
*
* Licensed 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 com.codeabovelab.dm.agent;

import com.codeabovelab.dm.agent.dp.ProxyServlet;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;

import javax.servlet.Servlet;

/**
*/
@Import({
UndertowEmbeddedServletContainerFactory.class,
PropertySourcesPlaceholderConfigurer.class
})
@Configuration
public class Application extends SpringBootServletInitializer {

public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).run(args);
}

@Bean
public Servlet dispatcherServlet() {
return new ProxyServlet();
}

}
168 changes: 168 additions & 0 deletions agent/src/main/java/com/codeabovelab/dm/agent/dp/ProxyServlet.java
@@ -0,0 +1,168 @@
/*
* Copyright 2017 Code Above Lab LLC
*
* Licensed 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 com.codeabovelab.dm.agent.dp;


import com.codeabovelab.dm.common.utils.Closeables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.UnmodifiableIterator;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.epoll.EpollDomainSocketChannel;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.unix.DomainSocketAddress;
import io.netty.channel.unix.DomainSocketChannel;
import io.netty.handler.codec.http.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;

/**
* Rest controller which serves proxy requests
*/
@Slf4j
public class ProxyServlet extends GenericServlet {


private final Bootstrap bootstrap;
private final EpollEventLoopGroup group;

@Autowired
public ProxyServlet() {
this.bootstrap = new Bootstrap();
this.group = new EpollEventLoopGroup();
bootstrap.group(group)
.channel(EpollDomainSocketChannel.class)
.handler(new ChannelInitializer<DomainSocketChannel>() {
@Override
protected void initChannel(DomainSocketChannel channel) throws Exception {
channel.pipeline().addLast(new HttpClientCodec());
}
}
);
}

@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
try {
System.out.println("BEFORE CONNECT");
ChannelFuture cf = bootstrap.connect(new DomainSocketAddress("/var/run/docker.sock")).sync();
Channel channel = cf.channel();
channel.pipeline().addLast(new Handler(response));
DefaultFullHttpRequest backendReq = buildRequest(request);
System.out.println("BEFORE SEND " + backendReq);
channel.writeAndFlush(backendReq).sync();
channel.closeFuture().sync();
} catch (Exception e) {
log.error("Error", e);
}
}

private DefaultFullHttpRequest buildRequest(HttpServletRequest request) {
String uri = request.getRequestURI();
HttpMethod method = HttpMethod.valueOf(request.getMethod());
DefaultFullHttpRequest br = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, method, uri);
Enumeration<String> headers = request.getHeaderNames();
HttpHeaders bh = br.headers();
while(headers.hasMoreElements()) {
String header = headers.nextElement();
Iterable<String> iter = () -> Iterators.forEnumeration(request.getHeaders(header));
bh.add(header, iter);
}
return br;
}

@Override
public void destroy() {
group.shutdownGracefully();
}

// TODO add identifier for each request (for logging purposes)
// implement web socket & chunked streaming
private static class Handler extends ChannelInboundHandlerAdapter {
private final HttpServletResponse frontResp;
private volatile ServletOutputStream stream;
private volatile boolean hasError;

Handler(HttpServletResponse frontResp) {
this.frontResp = frontResp;
}

@Override
public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
System.out.println("READ " + msg);
if(msg instanceof HttpResponse) {
HttpResponse backendResp = (HttpResponse) msg;
HttpResponseStatus status = backendResp.status();
frontResp.setStatus(status.code());
} else if(msg instanceof HttpContent) {
HttpContent backendResp = (HttpContent) msg;
if(backendResp == LastHttpContent.EMPTY_LAST_CONTENT) {
ctx.close();
return;
}
ServletOutputStream sos = getStream();
if(sos == null) {
log.warn("Stream null on non closed handler.");
return;
}
ByteBuf buf = backendResp.content();
try {
byte arr[] = new byte[1024];
int end;
while((end = buf.readableBytes()) > 0) {
int len = Math.min(end, arr.length);
buf.readBytes(arr, 0, len);
sos.write(arr, 0, len);
}
} finally {
buf.release();
}
}
}

private synchronized ServletOutputStream getStream() throws IOException {
if(stream == null && !hasError) {
stream = frontResp.getOutputStream();
}
return stream;
}

@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("COMPLETE");
Closeables.close(this.stream);
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
this.hasError = true;
log.error("Error in pipeline: ", cause);
ctx.close();
frontResp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, cause.toString());
}
}
}
20 changes: 20 additions & 0 deletions agent/src/main/java/com/codeabovelab/dm/agent/dp/package-info.java
@@ -0,0 +1,20 @@
/*
* Copyright 2017 Code Above Lab LLC
*
* Licensed 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.
*/

/**
* Docker proxy
*/
package com.codeabovelab.dm.agent.dp;
1 change: 1 addition & 0 deletions pom.xml
Expand Up @@ -77,6 +77,7 @@
<modules>
<module>common</module>
<module>cluster-manager</module>
<module>agent</module>
<!--<module>balancer-web</module>-->
</modules>

Expand Down

0 comments on commit d8a7fe0

Please sign in to comment.