-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
648 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package io.codeka.gaia.bo; | ||
|
||
import com.fasterxml.jackson.annotation.JsonIgnore; | ||
|
||
import java.io.StringWriter; | ||
import java.io.Writer; | ||
|
||
/** | ||
* A job is the instanciation of a stack | ||
*/ | ||
public class Job { | ||
|
||
private String id; | ||
|
||
private StringWriter stringWriter = new StringWriter(); | ||
|
||
private String logs; | ||
|
||
private JobStatus jobStatus; | ||
|
||
public String getId() { | ||
return id; | ||
} | ||
|
||
public void setId(String id) { | ||
this.id = id; | ||
} | ||
|
||
public String getLogs() { | ||
if(jobStatus == JobStatus.FINISHED){ | ||
return logs; | ||
} | ||
return stringWriter.toString(); | ||
} | ||
|
||
@JsonIgnore | ||
public Writer getLogsWriter(){ | ||
return stringWriter; | ||
} | ||
|
||
public JobStatus getStatus(){ | ||
return this.jobStatus; | ||
} | ||
|
||
public void start() { | ||
this.jobStatus = JobStatus.RUNNING; | ||
} | ||
|
||
public void end(){ | ||
this.jobStatus = JobStatus.FINISHED; | ||
// getting final logs | ||
this.logs = this.stringWriter.toString(); | ||
} | ||
|
||
public void fail() { | ||
this.jobStatus = JobStatus.FAILED; | ||
// getting final logs | ||
this.logs = this.stringWriter.toString(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package io.codeka.gaia.bo; | ||
|
||
public enum JobStatus { | ||
RUNNING, FINISHED, FAILED | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package io.codeka.gaia.config; | ||
|
||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.scheduling.annotation.EnableAsync; | ||
|
||
@EnableAsync | ||
@Configuration | ||
public class AsyncConfig { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
src/main/java/io/codeka/gaia/runner/HttpHijackWorkaround.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package io.codeka.gaia.runner; | ||
|
||
import com.spotify.docker.client.LogReader; | ||
import com.spotify.docker.client.LogStream; | ||
import jnr.unixsocket.UnixSocketChannel; | ||
import org.apache.http.conn.EofSensorInputStream; | ||
import org.apache.http.entity.BasicHttpEntity; | ||
import org.apache.http.entity.HttpEntityWrapper; | ||
import org.apache.http.impl.io.IdentityInputStream; | ||
import org.apache.http.impl.io.SessionInputBufferImpl; | ||
import org.glassfish.jersey.message.internal.EntityInputStream; | ||
|
||
import java.io.FilterInputStream; | ||
import java.io.InputStream; | ||
import java.lang.reflect.Field; | ||
import java.net.Socket; | ||
import java.nio.channels.Channels; | ||
import java.nio.channels.WritableByteChannel; | ||
import java.util.LinkedList; | ||
import java.util.List; | ||
|
||
public class HttpHijackWorkaround { | ||
public static WritableByteChannel getOutputStream(final LogStream stream, final String uri) throws Exception { | ||
// @formatter:off | ||
final String[] fields = | ||
new String[] {"reader", | ||
"stream", | ||
"original", | ||
"input", | ||
"in", | ||
"in", | ||
"in", | ||
"eofWatcher", | ||
"wrappedEntity", | ||
"content", | ||
"in", | ||
"inStream"}; | ||
|
||
final String[] containingClasses = | ||
new String[] {"com.spotify.docker.client.DefaultLogStream", | ||
LogReader.class.getName(), | ||
"org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream", | ||
EntityInputStream.class.getName(), | ||
FilterInputStream.class.getName(), | ||
FilterInputStream.class.getName(), | ||
FilterInputStream.class.getName(), | ||
EofSensorInputStream.class.getName(), | ||
HttpEntityWrapper.class.getName(), | ||
BasicHttpEntity.class.getName(), | ||
IdentityInputStream.class.getName(), | ||
SessionInputBufferImpl.class.getName()}; | ||
// @formatter:on | ||
|
||
final List<String[]> fieldClassTuples = new LinkedList<>(); | ||
for (int i = 0; i < fields.length; i++) { | ||
fieldClassTuples.add(new String[] {containingClasses[i], fields[i]}); | ||
} | ||
|
||
if (uri.startsWith("unix:")) { | ||
//fieldClassTuples.add(new String[] {"org.apache.http.impl.conn.LoggingInputStream", "in"}); | ||
fieldClassTuples.add(new String[] {"sun.nio.ch.ChannelInputStream", "ch"}); | ||
} else if (uri.startsWith("https:")) { | ||
final float jvmVersion = Float.parseFloat(System.getProperty("java.specification.version")); | ||
fieldClassTuples.add(new String[] {"sun.security.ssl.AppInputStream", jvmVersion < 1.9f ? "c" : "socket"}); | ||
} else { | ||
fieldClassTuples.add(new String[] {"java.net.SocketInputStream", "socket"}); | ||
} | ||
|
||
final Object res = getInternalField(stream, fieldClassTuples); | ||
if (res instanceof WritableByteChannel) { | ||
return (WritableByteChannel) res; | ||
} else if (res instanceof Socket) { | ||
return Channels.newChannel(((Socket) res).getOutputStream()); | ||
} else { | ||
throw new AssertionError("Expected " + WritableByteChannel.class.getName() + " or " + Socket.class.getName() + " but found: " + | ||
res.getClass().getName()); | ||
} | ||
} | ||
|
||
public static InputStream getInputStream(LogStream stream) { | ||
final String[] fields = new String[] { "reader", "stream" }; //$NON-NLS-1$ //$NON-NLS-2$ | ||
final String[] declared = new String[] { "com.spotify.docker.client.DefaultLogStream", LogReader.class.getName()}; | ||
|
||
List<String[]> list = new LinkedList<>(); | ||
for (int i = 0; i < fields.length; i++) { | ||
list.add(new String[] { declared[i], fields[i] }); | ||
} | ||
return (InputStream) getInternalField(stream, list); | ||
} | ||
|
||
/** | ||
* Recursively traverse a hierarchy of fields in classes, obtain their value and continue the traversing on the optained object | ||
* | ||
* @param fieldContent current object to operate on | ||
* @param classFieldTupels the class/field hierarchy | ||
* | ||
* @return the content of the leaf in the traversed hierarchy path | ||
*/ | ||
private static Object getInternalField(final Object fieldContent, final List<String[]> classFieldTupels) { | ||
Object curr = fieldContent; | ||
for (final String[] classFieldTuple : classFieldTupels) { | ||
//noinspection ConstantConditions | ||
final Field field; | ||
try { | ||
field = Class.forName(classFieldTuple[0]).getDeclaredField(classFieldTuple[1]); | ||
field.setAccessible(true); | ||
curr = field.get(curr); | ||
} catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
return curr; | ||
} | ||
} |
Oops, something went wrong.