Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️ : rework of the job management #127

Merged
merged 8 commits into from
Aug 30, 2019
98 changes: 98 additions & 0 deletions src/main/java/io/codeka/gaia/runner/DockerRunner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package io.codeka.gaia.runner;

import com.spotify.docker.client.DockerClient;
import com.spotify.docker.client.exceptions.DockerException;
import com.spotify.docker.client.messages.ContainerConfig;
import io.codeka.gaia.settings.bo.Settings;
import io.codeka.gaia.stacks.workflow.JobWorkflow;
import org.apache.commons.io.output.WriterOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;

/**
* Service to run docker container
*/
@Service
public class DockerRunner {

private static final Logger LOG = LoggerFactory.getLogger(DockerRunner.class);

private DockerClient dockerClient;
private ContainerConfig.Builder containerConfigBuilder;
private HttpHijackWorkaround httpHijackWorkaround;
private Settings settings;

@Autowired
public DockerRunner(DockerClient dockerClient, ContainerConfig.Builder containerConfigBuilder,
HttpHijackWorkaround httpHijackWorkaround, Settings settings) {
this.dockerClient = dockerClient;
this.containerConfigBuilder = containerConfigBuilder;
this.httpHijackWorkaround = httpHijackWorkaround;
this.settings = settings;
}

int runContainerForJob(JobWorkflow jobWorkflow, String script) {
try {
var env = new ArrayList<String>();
env.add("TF_IN_AUTOMATION=true");
env.addAll(settings.env());

var job = jobWorkflow.getJob();

// FIXME This is certainly no thread safe !
var containerConfig = containerConfigBuilder
.env(env)
.image("hashicorp/terraform:" + job.getCliVersion())
.build();

// pull the image
dockerClient.pull("hashicorp/terraform:" + job.getCliVersion());

var containerCreation = dockerClient.createContainer(containerConfig);
var containerId = containerCreation.id();

var dockerContainer = new DockerContainer(containerId, dockerClient, httpHijackWorkaround);

dockerClient.startContainer(containerId);

// attaching the outputs in a background thread
var step = jobWorkflow.getCurrentStep();
CompletableFuture.runAsync(() -> {
try (var writerOutputStream = new WriterOutputStream(step.getLogsWriter(), Charset.defaultCharset())) {
// this code is blocking I/O !
dockerContainer.attach(writerOutputStream, writerOutputStream);
} catch (IOException | StackRunnerException e) {
LOG.error("Unable to attach logs of container", e);
}
});

// write the content of the script to the container's std in
try (WritableByteChannel stdIn = Channels.newChannel(dockerContainer.getStdIn())) {
stdIn.write(ByteBuffer.wrap(script.getBytes()));
}

// wait for the container to exit
var containerExit = dockerClient.waitContainer(containerCreation.id());

dockerClient.removeContainer(containerCreation.id());

return Math.toIntExact(containerExit.statusCode());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return 99;
} catch (DockerException | IOException | StackRunnerException e) {
return 99;
}
}

}
30 changes: 25 additions & 5 deletions src/main/java/io/codeka/gaia/runner/StackCommandBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ private String buildCommand(Stack stack, TerraformModule module, String command)
return String.format("%s %s", command, variablesBuilder.toString());
}

/**
* builds the terraform plan script
*
* @return
*/
String buildPlanScript(Stack stack, TerraformModule module) {
return buildScript(stack, module, this::buildPlanCommand);
}

/**
* builds the terraform apply script
*
Expand All @@ -84,8 +93,8 @@ String buildApplyScript(Stack stack, TerraformModule module) {
*
* @return
*/
String buildPlanScript(Stack stack, TerraformModule module) {
return buildScript(stack, module, this::buildPlanCommand);
String buildPlanDestroyScript(Stack stack, TerraformModule module) {
return buildScript(stack, module, this::buildPlanDestroyCommand);
}

/**
Expand All @@ -97,6 +106,17 @@ String buildDestroyScript(Stack stack, TerraformModule module) {
return buildScript(stack, module, this::buildDestroyCommand);
}

/**
* builds the terraform plan command
*
* @param stack
* @param module
* @return
*/
String buildPlanCommand(Stack stack, TerraformModule module) {
return buildCommand(stack, module, "terraform plan -detailed-exitcode");
}

/**
* builds the terraform apply command
*
Expand All @@ -109,14 +129,14 @@ String buildApplyCommand(Stack stack, TerraformModule module) {
}

/**
* builds the terraform plan command
* builds the terraform plan destroy command
*
* @param stack
* @param module
* @return
*/
String buildPlanCommand(Stack stack, TerraformModule module) {
return buildCommand(stack, module, "terraform plan -detailed-exitcode");
String buildPlanDestroyCommand(Stack stack, TerraformModule module) {
return buildCommand(stack, module, "terraform plan -destroy -detailed-exitcode");
}

/**
Expand Down
Loading