/*
* The MIT License
*
* Copyright (c) 2015, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package it.dockins.dockerslaves;
import it.dockins.dockerslaves.spec.ContainerDefinition;
import it.dockins.dockerslaves.spi.DockerDriver;
import it.dockins.dockerslaves.spec.ContainerSetDefinition;
import hudson.Launcher;
import hudson.Proc;
import hudson.model.TaskListener;
import hudson.slaves.SlaveComputer;
import it.dockins.dockerslaves.spec.SideContainerDefinition;
import it.dockins.dockerslaves.spi.DockerProvisioner;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
/**
* Provision {@link Container}s based on ${@link ContainerSetDefinition} to provide a queued task
* an executor.
*/
public class DefaultDockerProvisioner extends DockerProvisioner {
protected final ContainersContext context;
protected final DockerDriver driver;
protected final ContainerSetDefinition spec;
protected final String remotingImage;
protected final String scmImage;
public DefaultDockerProvisioner(ContainersContext context, DockerDriver driver, ContainerSetDefinition spec, String remotingImage, String scmImage) throws IOException, InterruptedException {
this.context = context;
this.driver = driver;
this.spec = spec;
this.remotingImage = remotingImage;
this.scmImage = scmImage;
}
@Override
public ContainersContext getContext() {
return context;
}
@Override
public Container launchRemotingContainer(final DockerComputer computer, TaskListener listener) throws IOException, InterruptedException {
// if remoting container already exists, we reuse it
final Container existing = context.getRemotingContainer();
if (existing != null) {
if (driver.hasContainer(listener, existing.getId())) {
return existing;
}
}
String volume = context.getWorkdirVolume();
if (!driver.hasVolume(listener, volume)) {
volume = driver.createVolume(listener);
context.setWorkdirVolume(volume);
}
final Container remotingContainer = driver.launchRemotingContainer(listener, remotingImage, volume, computer);
context.setRemotingContainer(remotingContainer);
return remotingContainer;
}
@Override
public Container launchBuildContainers(Launcher.ProcStarter starter, TaskListener listener) throws IOException, InterruptedException {
if (spec.getSideContainers().size() > 0 && context.getSideContainers().size() == 0) {
// In a ideal world we would run side containers when DockerSlave.DockerSlaveSCMListener detect scm checkout completed
// but then we don't have a ProcStarter reference. So do it first time a command is ran during the build
// after scm checkout completed. We detect this is the first time as spec > context
createSideContainers(starter, listener);
}
final ContainerDefinition build = spec.getBuildHostImage();
String buildImage = build.getImage(driver, starter.pwd(), listener);
final Container buildContainer = driver.launchBuildContainer(listener, buildImage, context.getRemotingContainer(), build.getHints());
context.setBuildContainer(buildContainer);
return buildContainer;
}
@Override
public Container launchScmContainer(TaskListener listener) throws IOException, InterruptedException {
final Container scmContainer = driver.launchBuildContainer(listener, scmImage, context.getRemotingContainer(), Collections.EMPTY_LIST);
context.setBuildContainer(scmContainer);
return scmContainer;
}
private void createSideContainers(Launcher.ProcStarter starter, TaskListener listener) throws IOException, InterruptedException {
for (SideContainerDefinition definition : spec.getSideContainers()) {
final String name = definition.getName();
final ContainerDefinition sidecar = definition.getSpec();
final String image = sidecar.getImage(driver, starter.pwd(), listener);
listener.getLogger().println("Starting " + name + " container");
Container container = driver.launchSideContainer(listener, image, context.getRemotingContainer(), sidecar.getHints());
context.getSideContainers().put(name, container);
}
}
@Override
public Proc launchBuildProcess(Launcher.ProcStarter procStarter, TaskListener listener) throws IOException, InterruptedException {
Container targetContainer = null;
if (context.isPreScm()) {
targetContainer = context.getScmContainer();
if (targetContainer == null) {
targetContainer = launchScmContainer(listener);
}
} else {
targetContainer = context.getBuildContainer();
if (targetContainer == null) {
targetContainer = launchBuildContainers(procStarter, listener);
}
}
return driver.execInContainer(listener, targetContainer.getId(), procStarter);
}
@Override
public void clean(TaskListener listener) throws IOException, InterruptedException {
for (Container instance : context.getSideContainers().values()) {
driver.removeContainer(listener, instance);
}
if (context.getBuildContainer() != null) {
driver.removeContainer(listener, context.getBuildContainer());
}
if (context.getScmContainer() != null) {
driver.removeContainer(listener, context.getScmContainer());
}
driver.close();
}
}