Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions client/src/main/java/dev/snowdrop/buildpack/BuildpackBuild.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,39 @@ private String selectPlatformLevel(DockerConfig dc, PlatformConfig pc, BuilderIm

}

private void verifyContainerRuntime(DockerConfig dc) {
//check if docker client is available
if(dc.getDockerClient() == null){
throw new BuildpackException("Unable to connect to container runtime, no docker client available", new IllegalStateException());
}
//check if docker host is available
if(dc.getHostAndSocketConfig().getHost().get().isEmpty()){
throw new BuildpackException("Unable to connect to container runtime, no docker host available", new IllegalStateException());
}
//check if docker socket is available
if(dc.getHostAndSocketConfig().getSocket().get().isEmpty()){
throw new BuildpackException("Unable to connect to container runtime, no docker socket available", new IllegalStateException());
}
log.info("Verifying connection to container runtime...");
try{
dc.getDockerClient().pingCmd().exec();
}catch(Exception e){
throw new BuildpackException("Unable to verify containe runtime settings", e);
}
}

public int build(){

log.info("Buildpack build requested with config: \n"+
" - builder "+config.getBuilderImage().getCanonicalReference()+"\n"+
" - output "+config.getOutputImage().getReference()+"\n"+
" - logLevel "+config.getLogConfig().getLogLevel()+"\n"+
" - dockerHost "+config.getDockerConfig().getDockerHost()+"\n"+
" - dockerSocket "+config.getDockerConfig().getDockerSocket()+"\n"+
" - dockerHost "+config.getDockerConfig().getHostAndSocketConfig().getHost().get()+"\n"+
" - dockerSocket "+config.getDockerConfig().getHostAndSocketConfig().getSocket().get()+"\n"+
" - useDaemon "+config.getDockerConfig().getUseDaemon());

verifyContainerRuntime(config.getDockerConfig());

log.info("Pulling Builder image");

//obtain & pull & inspect Builder image.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.dockerjava.api.DockerClient;

import dev.snowdrop.buildpack.BuildpackException;
import dev.snowdrop.buildpack.docker.DockerClientUtils;
import dev.snowdrop.buildpack.docker.DockerClientUtils.HostAndSocket;
import io.sundr.builder.annotations.Buildable;

@Buildable(generateBuilderPackage=true, builderPackage="dev.snowdrop.buildpack.builder")
public class DockerConfig {
private static final Logger log = LoggerFactory.getLogger(DockerConfig.class);

public static DockerConfigBuilder builder() {
return new DockerConfigBuilder();
}
Expand All @@ -27,8 +31,7 @@ public static enum PullPolicy {ALWAYS, IF_NOT_PRESENT, NEVER};
private Integer pullRetryCount;
private Integer pullRetryIncreaseSeconds;
private PullPolicy pullPolicy;
private String dockerHost;
private String dockerSocket;
private HostAndSocketConfig hostAndSocketConfig;
private String dockerNetwork;
private Boolean useDaemon;
private DockerClient dockerClient;
Expand All @@ -39,47 +42,32 @@ public DockerConfig(
Integer pullRetryCount,
Integer pullRetryIncreaseSeconds,
PullPolicy pullPolicy,
String dockerHost,
String dockerSocket,
HostAndSocketConfig hostAndSocketConfig,
String dockerNetwork,
Boolean useDaemon,
DockerClient dockerClient,
List<RegistryAuthConfig> authConfigs
){
log.debug("DockerConfig: pullTimeoutSeconds={}, pullRetryCount={}, pullRetryIncreaseSeconds={}, pullPolicy={}, hostAndSocketConfig={}, dockerNetwork={}, useDaemon={}",
pullTimeoutSeconds, pullRetryCount, pullRetryIncreaseSeconds, pullPolicy, hostAndSocketConfig, dockerNetwork, useDaemon);

this.pullTimeoutSeconds = pullTimeoutSeconds != null ? Integer.max(0,pullTimeoutSeconds) : DEFAULT_PULL_TIMEOUT;
this.pullRetryCount = pullRetryCount != null ? Integer.max(0,pullRetryCount) : DEFAULT_PULL_RETRY_COUNT;
this.pullRetryIncreaseSeconds = pullRetryIncreaseSeconds != null ? Integer.max(0,pullRetryIncreaseSeconds) : DEFAULT_PULL_RETRY_INCREASE;
this.pullPolicy = pullPolicy != null ? pullPolicy : DEFAULT_PULL_POLICY;
this.dockerNetwork = dockerNetwork;
this.useDaemon = useDaemon != null ? useDaemon : Boolean.TRUE; //default daemon to true for back compat.

//take config values, and determine values to use..
HostAndSocket hands = DockerClientUtils.probeContainerRuntime(new DockerClientUtils.HostAndSocket(dockerHost, dockerSocket));
this.dockerHost = hands.host;
this.dockerSocket = hands.socket;
//process host & socket passed, and probe runtime to fill in unset values.
setHostAndSocketConfig(hostAndSocketConfig);

this.authConfigs = authConfigs == null ? new ArrayList<>() : authConfigs;

this.dockerClient = dockerClient != null ? dockerClient : DockerClientUtils.getDockerClient(hands, authConfigs);

try{
this.dockerClient.pingCmd().exec();
}catch(Exception e){
throw new BuildpackException("Unable to verify docker settings", e);
}
this.dockerClient = dockerClient;

}

public void setDockerHost(String dockerHost){
HostAndSocket hands = DockerClientUtils.probeContainerRuntime(new DockerClientUtils.HostAndSocket(dockerHost, this.dockerSocket));
this.dockerHost = hands.host;
this.dockerSocket = hands.socket;
this.dockerClient = dockerClient != null ? dockerClient : DockerClientUtils.getDockerClient(hands);
}
public void setDockerSocket(String dockerSocket){
HostAndSocket hands = DockerClientUtils.probeContainerRuntime(new DockerClientUtils.HostAndSocket(this.dockerHost, dockerSocket));
this.dockerHost = hands.host;
this.dockerSocket = hands.socket;
this.dockerClient = dockerClient != null ? dockerClient : DockerClientUtils.getDockerClient(hands);
public void setHostAndSocketConfig(HostAndSocketConfig hostAndSocketConfig) {
this.hostAndSocketConfig = DockerClientUtils.probeContainerRuntime(hostAndSocketConfig);
}

public Integer getPullTimeoutSeconds(){
Expand All @@ -98,19 +86,16 @@ public PullPolicy getPullPolicy(){
return this.pullPolicy;
}

public String getDockerHost(){
return this.dockerHost;
}

public String getDockerSocket(){
return this.dockerSocket;
public HostAndSocketConfig getHostAndSocketConfig(){
return this.hostAndSocketConfig;
}

public String getDockerNetwork(){
return this.dockerNetwork;
}

public DockerClient getDockerClient(){
this.dockerClient = this.dockerClient != null ? this.dockerClient : DockerClientUtils.getDockerClient(this.hostAndSocketConfig, this.authConfigs);
return this.dockerClient;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package dev.snowdrop.buildpack.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Optional;

import dev.snowdrop.buildpack.docker.DockerClientUtils;
import io.sundr.builder.annotations.Buildable;

@Buildable(generateBuilderPackage=true, builderPackage="dev.snowdrop.buildpack.builder")
public class HostAndSocketConfig {
private String host;
private String socket;

public static HostAndSocketConfigBuilder builder() {
return new HostAndSocketConfigBuilder();
}

public HostAndSocketConfig(String host,
String socket) {
this.host = host;
this.socket = socket;
}

public Optional<String> getHost() {
return Optional.ofNullable(host);
}
public Optional<String> getSocket() {
return Optional.ofNullable(socket);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.slf4j.LoggerFactory;

import dev.snowdrop.buildpack.config.RegistryAuthConfig;
import dev.snowdrop.buildpack.config.HostAndSocketConfig;
import dev.snowdrop.buildpack.utils.OperatingSytem;

import com.github.dockerjava.api.DockerClient;
Expand All @@ -34,24 +35,23 @@ public static DockerClient getDockerClient() {
return getDockerClient(probeContainerRuntime(null));
}

public static DockerClient getDockerClient(HostAndSocket runtimeInfo) {
public static DockerClient getDockerClient(HostAndSocketConfig runtimeInfo) {
return getDockerClient(runtimeInfo, new ArrayList<RegistryAuthConfig>(){});
}

/**
* Simple util to get a DockerClient for the platform. probably needs more work
* for other platforms, and we may want a way to configure authentication etc.
*/
public static DockerClient getDockerClient(HostAndSocket runtimeInfo, List<RegistryAuthConfig> authConfigs) {
if (runtimeInfo == null || runtimeInfo.host == null || runtimeInfo.host.isEmpty() ||
runtimeInfo.socket == null || runtimeInfo.socket.isEmpty()) {
public static DockerClient getDockerClient(HostAndSocketConfig runtimeInfo, List<RegistryAuthConfig> authConfigs) {
if(runtimeInfo == null || !runtimeInfo.getHost().isPresent() || !runtimeInfo.getSocket().isPresent()) {
log.warn("Supplied host/socket was null, attempting to use auto-configured defaults");
return getDockerClient(probeContainerRuntime(runtimeInfo), authConfigs);
}

log.debug("Using dockerhost " + runtimeInfo.host);
log.debug("Using dockerhost " + runtimeInfo.getHost().get());
DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
.withDockerHost(runtimeInfo.host)
.withDockerHost(runtimeInfo.getHost().get())
.build();

AuthDelegatingDockerClientConfig addcc = new AuthDelegatingDockerClientConfig(config);
Expand All @@ -67,55 +67,41 @@ public static DockerClient getDockerClient(HostAndSocket runtimeInfo, List<Regis
return dockerClient;
}

public static class HostAndSocket {
public String host;
public String socket;
public HostAndSocket(String host, String socket){
this.host = host; this.socket = socket;
}
public HostAndSocket(String host){
this.host = host;
this.socket = null;
}
}

public static HostAndSocket probeContainerRuntime(HostAndSocket overrides) {
public static HostAndSocketConfig probeContainerRuntime(HostAndSocketConfig overrides) {
log.debug("Probing for container runtime... "+(overrides!=null ? "H:"+overrides.getHost()+" S:"+overrides.getSocket() : "no overrides"));
if(overrides!=null){
//if user has supplied both host & socket, we don't need to probe at all, use their values.
//(using isEmpty as isBlank is jdk11 onwards)
if(overrides.host!=null && overrides.socket!=null && !overrides.host.isEmpty() && !overrides.socket.isEmpty()){
return overrides;
}

//sanitize blank/empty values to null. (also using isEmpty because isBlank is only from jdk11 onwards)
if(overrides.host!=null && overrides.host.isEmpty()){
overrides.host=null;
}
if(overrides.socket!=null && overrides.socket.isEmpty()){
overrides.socket=null;
if(overrides.getHost().isPresent() && overrides.getSocket().isPresent()){
return new HostAndSocketConfig(overrides.getHost().get(),overrides.getSocket().get());
}
}else{
//no overrides at all? make an empty one.
overrides = new HostAndSocket(null, null);
}

try{
//configure the override values as Optionals.
Optional<String> dockerHost = Optional.ofNullable(overrides.host);
Optional<String> dockerHost = overrides==null ? Optional.empty() : overrides.getHost();
//for dockerhost, if user override was null, try to honor the env var
if(!dockerHost.isPresent()){
dockerHost = Optional.ofNullable(System.getenv("DOCKER_HOST"));
}
Optional<String> dockerSocket = Optional.ofNullable(overrides.socket);
Optional<String> dockerSocket = overrides==null ? Optional.empty() : overrides.getSocket();

//if we now have a host & socket, we are done.
if(dockerHost.isPresent() && dockerSocket.isPresent()){
log.debug("Using docker host "+dockerHost.get()+" and socket "+dockerSocket.get());
return new HostAndSocketConfig(dockerHost.get(),dockerSocket.get());
}

//if dockerhost is specified, but docker socket is not, test if dockerhost is podman rootful,
//and autoconfigure dockersocket.. otherwise invoking podman as the user may result in the
//user socket being selected for use with the rootful host, leading to failure.
if ( dockerHost.isPresent() && !dockerSocket.isPresent() &&
( "unix:///var/run/podman/podman.sock".equals(dockerHost.get()) || "unix:///run/podman/podman.sock".equals(dockerHost.get()) )){
return new HostAndSocket(dockerHost.get(), dockerHost.get().substring("unix://".length()));
log.debug("Using podman rootful host "+dockerHost.get()+" and socket "+dockerHost.get().substring("unix://".length()));
return new HostAndSocketConfig(dockerHost.get(), dockerHost.get().substring("unix://".length()));
}

//we are still missing a host or socket, so we need to probe for them.
//try to obtain podman socket path..
log.info("Testing for podman/docker...");
DockerClientUtils.CmdResult cr = DockerClientUtils.start(PODMAN_SOCKET);
Expand All @@ -124,58 +110,59 @@ public static HostAndSocket probeContainerRuntime(HostAndSocket overrides) {
String socket = cr.output.get(0);
if(socket.startsWith("unix://")){
socket = socket.substring("unix://".length());
}
}
log.debug("Using derived socket path value of "+socket+" from podman cli invocation.");
//podman was present, use podman to retrieve dockerhost value
switch (OperatingSytem.getOperationSystem()) {
case WIN:{
DockerClientUtils.CmdResult scmd = DockerClientUtils.start(WIN_PODMAN_HOST);
if(scmd.rc==0){
String fixedhost = scmd.output.get(0).replaceAll("\\", "/");
return new HostAndSocket(dockerHost.orElse(fixedhost), dockerSocket.orElse(cr.output.get(0)));
String fixedhost = scmd.output.get(0).replaceAll("\\\\", "/");
return new HostAndSocketConfig(dockerHost.orElse(fixedhost), dockerSocket.orElse(socket));
}else{
log.warn("Unable to obtain podman socket path from podman, using internal default");
return new HostAndSocket(dockerHost.orElse("npipe:////./pipe/docker_engine"),dockerSocket.orElse("/var/run/docker.sock"));
return new HostAndSocketConfig(dockerHost.orElse("npipe:////./pipe/docker_engine"),dockerSocket.orElse("/var/run/docker.sock"));
}
}
case LINUX:{
DockerClientUtils.CmdResult scmd = DockerClientUtils.start(LIN_PODMAN_HOST);
if(scmd.rc==0){
return new HostAndSocket(dockerHost.orElse(scmd.output.get(0)), dockerSocket.orElse(socket));
return new HostAndSocketConfig(dockerHost.orElse(scmd.output.get(0)), dockerSocket.orElse(socket));
}else{
log.warn("Unable to obtain podman socket path from podman, using internal default");
return new HostAndSocket(dockerHost.orElse("unix:///var/run/podman.sock"), dockerSocket.orElse("/var/run/podman.sock"));
return new HostAndSocketConfig(dockerHost.orElse("unix:///var/run/podman.sock"), dockerSocket.orElse("/var/run/podman.sock"));
}
}
case MAC:{
DockerClientUtils.CmdResult scmd = DockerClientUtils.start(MAC_PODMAN_HOST);
if(scmd.rc==0){
return new HostAndSocket(dockerHost.orElse(scmd.output.get(0)), dockerSocket.orElse(socket));
return new HostAndSocketConfig(dockerHost.orElse(scmd.output.get(0)), dockerSocket.orElse(socket));
}else{
log.warn("Unable to obtain podman socket path from podman, using internal default");
return new HostAndSocket(dockerHost.orElse("unix:///var/run/podman.sock"), dockerSocket.orElse("/var/run/podman.sock"));
return new HostAndSocketConfig(dockerHost.orElse("unix:///var/run/podman.sock"), dockerSocket.orElse("/var/run/podman.sock"));
}
}
case UNKNOWN:{
log.warn("Unable to identify Operating System, you may need to specify docker host / docker socket manually");
return new HostAndSocket(dockerHost.orElse("unix:///var/run/podman.sock"), dockerSocket.orElse("/var/run/podman.sock"));
return new HostAndSocketConfig(dockerHost.orElse("unix:///var/run/podman.sock"), dockerSocket.orElse("/var/run/podman.sock"));
}
}
}else{
log.info("Assuming docker, configuring.");
//failed to obtain podman socket path, assuming docker..
switch (OperatingSytem.getOperationSystem()) {
case WIN:{
return new HostAndSocket(dockerHost.orElse("npipe:////./pipe/docker_engine"),dockerSocket.orElse("/var/run/docker.sock"));
return new HostAndSocketConfig(dockerHost.orElse("npipe:////./pipe/docker_engine"),dockerSocket.orElse("/var/run/docker.sock"));
}
case LINUX:{
return new HostAndSocket(dockerHost.orElse("unix:///var/run/docker.sock"), dockerSocket.orElse("/var/run/docker.sock"));
return new HostAndSocketConfig(dockerHost.orElse("unix:///var/run/docker.sock"), dockerSocket.orElse("/var/run/docker.sock"));
}
case MAC:{
return new HostAndSocket(dockerHost.orElse("unix:///var/run/docker.sock"), dockerSocket.orElse("/var/run/docker.sock"));
return new HostAndSocketConfig(dockerHost.orElse("unix:///var/run/docker.sock"), dockerSocket.orElse("/var/run/docker.sock"));
}
case UNKNOWN:{
log.warn("Unable to identify Operating System, you may need to specify docker host / docker socket manually");
return new HostAndSocket(dockerHost.orElse("unix:///var/run/docker.sock"), dockerSocket.orElse("/var/run/docker.sock"));
return new HostAndSocketConfig(dockerHost.orElse("unix:///var/run/docker.sock"), dockerSocket.orElse("/var/run/docker.sock"));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public String getContainerForPhase(String args[], Integer runAsId){
));

if(dockerConfig.getUseDaemon())
binds.add(new VolumeBind(dockerConfig.getDockerSocket(), LifecyclePhaseFactory.DOCKER_SOCKET_PATH));
binds.add(new VolumeBind(dockerConfig.getHostAndSocketConfig().getSocket().get(), LifecyclePhaseFactory.DOCKER_SOCKET_PATH));

// create a container using builderImage that will invoke the creator process
String id = ContainerUtils.createContainer(dockerConfig.getDockerClient(),
Expand All @@ -111,7 +111,7 @@ public String getContainerForPhase(String args[], Integer runAsId){
log.debug("- mounted " + applicationVolume + " at " + WORKSPACE_VOL_PATH);
log.debug("- mounted " + platformVolume + " at " + PLATFORM_VOL_PATH);
if(dockerConfig.getUseDaemon())
log.debug("- mounted " + dockerConfig.getDockerSocket() + " at " + LifecyclePhaseFactory.DOCKER_SOCKET_PATH);
log.debug("- mounted " + dockerConfig.getHostAndSocketConfig().getSocket().get() + " at " + LifecyclePhaseFactory.DOCKER_SOCKET_PATH);
log.debug("- mounted " + outputVolume + " at " + LAYERS_VOL_PATH);
log.debug("- container id " + id);
log.debug("- image reference "+builder.getImage().getCanonicalReference());
Expand Down
Loading