Skip to content

Commit 38fc715

Browse files
authored
podman windows and host/socket updates (#104)
* podman windows and host/socket updates * fix merge issue
1 parent 7e895e6 commit 38fc715

8 files changed

Lines changed: 147 additions & 119 deletions

File tree

client/src/main/java/dev/snowdrop/buildpack/BuildpackBuild.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,39 @@ private String selectPlatformLevel(DockerConfig dc, PlatformConfig pc, BuilderIm
6868

6969
}
7070

71+
private void verifyContainerRuntime(DockerConfig dc) {
72+
//check if docker client is available
73+
if(dc.getDockerClient() == null){
74+
throw new BuildpackException("Unable to connect to container runtime, no docker client available", new IllegalStateException());
75+
}
76+
//check if docker host is available
77+
if(dc.getHostAndSocketConfig().getHost().get().isEmpty()){
78+
throw new BuildpackException("Unable to connect to container runtime, no docker host available", new IllegalStateException());
79+
}
80+
//check if docker socket is available
81+
if(dc.getHostAndSocketConfig().getSocket().get().isEmpty()){
82+
throw new BuildpackException("Unable to connect to container runtime, no docker socket available", new IllegalStateException());
83+
}
84+
log.info("Verifying connection to container runtime...");
85+
try{
86+
dc.getDockerClient().pingCmd().exec();
87+
}catch(Exception e){
88+
throw new BuildpackException("Unable to verify containe runtime settings", e);
89+
}
90+
}
91+
7192
public int build(){
7293

7394
log.info("Buildpack build requested with config: \n"+
7495
" - builder "+config.getBuilderImage().getCanonicalReference()+"\n"+
7596
" - output "+config.getOutputImage().getReference()+"\n"+
7697
" - logLevel "+config.getLogConfig().getLogLevel()+"\n"+
77-
" - dockerHost "+config.getDockerConfig().getDockerHost()+"\n"+
78-
" - dockerSocket "+config.getDockerConfig().getDockerSocket()+"\n"+
98+
" - dockerHost "+config.getDockerConfig().getHostAndSocketConfig().getHost().get()+"\n"+
99+
" - dockerSocket "+config.getDockerConfig().getHostAndSocketConfig().getSocket().get()+"\n"+
79100
" - useDaemon "+config.getDockerConfig().getUseDaemon());
80101

102+
verifyContainerRuntime(config.getDockerConfig());
103+
81104
log.info("Pulling Builder image");
82105

83106
//obtain & pull & inspect Builder image.

client/src/main/java/dev/snowdrop/buildpack/config/DockerConfig.java

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@
33
import java.util.ArrayList;
44
import java.util.List;
55

6+
import org.slf4j.Logger;
7+
import org.slf4j.LoggerFactory;
8+
69
import com.github.dockerjava.api.DockerClient;
710

811
import dev.snowdrop.buildpack.BuildpackException;
912
import dev.snowdrop.buildpack.docker.DockerClientUtils;
10-
import dev.snowdrop.buildpack.docker.DockerClientUtils.HostAndSocket;
1113
import io.sundr.builder.annotations.Buildable;
1214

1315
@Buildable(generateBuilderPackage=true, builderPackage="dev.snowdrop.buildpack.builder")
1416
public class DockerConfig {
17+
private static final Logger log = LoggerFactory.getLogger(DockerConfig.class);
18+
1519
public static DockerConfigBuilder builder() {
1620
return new DockerConfigBuilder();
1721
}
@@ -27,8 +31,7 @@ public static enum PullPolicy {ALWAYS, IF_NOT_PRESENT, NEVER};
2731
private Integer pullRetryCount;
2832
private Integer pullRetryIncreaseSeconds;
2933
private PullPolicy pullPolicy;
30-
private String dockerHost;
31-
private String dockerSocket;
34+
private HostAndSocketConfig hostAndSocketConfig;
3235
private String dockerNetwork;
3336
private Boolean useDaemon;
3437
private DockerClient dockerClient;
@@ -39,47 +42,32 @@ public DockerConfig(
3942
Integer pullRetryCount,
4043
Integer pullRetryIncreaseSeconds,
4144
PullPolicy pullPolicy,
42-
String dockerHost,
43-
String dockerSocket,
45+
HostAndSocketConfig hostAndSocketConfig,
4446
String dockerNetwork,
4547
Boolean useDaemon,
4648
DockerClient dockerClient,
4749
List<RegistryAuthConfig> authConfigs
4850
){
51+
log.debug("DockerConfig: pullTimeoutSeconds={}, pullRetryCount={}, pullRetryIncreaseSeconds={}, pullPolicy={}, hostAndSocketConfig={}, dockerNetwork={}, useDaemon={}",
52+
pullTimeoutSeconds, pullRetryCount, pullRetryIncreaseSeconds, pullPolicy, hostAndSocketConfig, dockerNetwork, useDaemon);
53+
4954
this.pullTimeoutSeconds = pullTimeoutSeconds != null ? Integer.max(0,pullTimeoutSeconds) : DEFAULT_PULL_TIMEOUT;
5055
this.pullRetryCount = pullRetryCount != null ? Integer.max(0,pullRetryCount) : DEFAULT_PULL_RETRY_COUNT;
5156
this.pullRetryIncreaseSeconds = pullRetryIncreaseSeconds != null ? Integer.max(0,pullRetryIncreaseSeconds) : DEFAULT_PULL_RETRY_INCREASE;
5257
this.pullPolicy = pullPolicy != null ? pullPolicy : DEFAULT_PULL_POLICY;
5358
this.dockerNetwork = dockerNetwork;
5459
this.useDaemon = useDaemon != null ? useDaemon : Boolean.TRUE; //default daemon to true for back compat.
5560

56-
//take config values, and determine values to use..
57-
HostAndSocket hands = DockerClientUtils.probeContainerRuntime(new DockerClientUtils.HostAndSocket(dockerHost, dockerSocket));
58-
this.dockerHost = hands.host;
59-
this.dockerSocket = hands.socket;
61+
//process host & socket passed, and probe runtime to fill in unset values.
62+
setHostAndSocketConfig(hostAndSocketConfig);
6063

6164
this.authConfigs = authConfigs == null ? new ArrayList<>() : authConfigs;
62-
63-
this.dockerClient = dockerClient != null ? dockerClient : DockerClientUtils.getDockerClient(hands, authConfigs);
64-
65-
try{
66-
this.dockerClient.pingCmd().exec();
67-
}catch(Exception e){
68-
throw new BuildpackException("Unable to verify docker settings", e);
69-
}
65+
this.dockerClient = dockerClient;
66+
7067
}
7168

72-
public void setDockerHost(String dockerHost){
73-
HostAndSocket hands = DockerClientUtils.probeContainerRuntime(new DockerClientUtils.HostAndSocket(dockerHost, this.dockerSocket));
74-
this.dockerHost = hands.host;
75-
this.dockerSocket = hands.socket;
76-
this.dockerClient = dockerClient != null ? dockerClient : DockerClientUtils.getDockerClient(hands);
77-
}
78-
public void setDockerSocket(String dockerSocket){
79-
HostAndSocket hands = DockerClientUtils.probeContainerRuntime(new DockerClientUtils.HostAndSocket(this.dockerHost, dockerSocket));
80-
this.dockerHost = hands.host;
81-
this.dockerSocket = hands.socket;
82-
this.dockerClient = dockerClient != null ? dockerClient : DockerClientUtils.getDockerClient(hands);
69+
public void setHostAndSocketConfig(HostAndSocketConfig hostAndSocketConfig) {
70+
this.hostAndSocketConfig = DockerClientUtils.probeContainerRuntime(hostAndSocketConfig);
8371
}
8472

8573
public Integer getPullTimeoutSeconds(){
@@ -98,19 +86,16 @@ public PullPolicy getPullPolicy(){
9886
return this.pullPolicy;
9987
}
10088

101-
public String getDockerHost(){
102-
return this.dockerHost;
103-
}
104-
105-
public String getDockerSocket(){
106-
return this.dockerSocket;
89+
public HostAndSocketConfig getHostAndSocketConfig(){
90+
return this.hostAndSocketConfig;
10791
}
10892

10993
public String getDockerNetwork(){
11094
return this.dockerNetwork;
11195
}
11296

11397
public DockerClient getDockerClient(){
98+
this.dockerClient = this.dockerClient != null ? this.dockerClient : DockerClientUtils.getDockerClient(this.hostAndSocketConfig, this.authConfigs);
11499
return this.dockerClient;
115100
}
116101

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package dev.snowdrop.buildpack.config;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
6+
import java.util.Optional;
7+
8+
import dev.snowdrop.buildpack.docker.DockerClientUtils;
9+
import io.sundr.builder.annotations.Buildable;
10+
11+
@Buildable(generateBuilderPackage=true, builderPackage="dev.snowdrop.buildpack.builder")
12+
public class HostAndSocketConfig {
13+
private String host;
14+
private String socket;
15+
16+
public static HostAndSocketConfigBuilder builder() {
17+
return new HostAndSocketConfigBuilder();
18+
}
19+
20+
public HostAndSocketConfig(String host,
21+
String socket) {
22+
this.host = host;
23+
this.socket = socket;
24+
}
25+
26+
public Optional<String> getHost() {
27+
return Optional.ofNullable(host);
28+
}
29+
public Optional<String> getSocket() {
30+
return Optional.ofNullable(socket);
31+
}
32+
}

client/src/main/java/dev/snowdrop/buildpack/docker/DockerClientUtils.java

Lines changed: 35 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.slf4j.LoggerFactory;
1414

1515
import dev.snowdrop.buildpack.config.RegistryAuthConfig;
16+
import dev.snowdrop.buildpack.config.HostAndSocketConfig;
1617
import dev.snowdrop.buildpack.utils.OperatingSytem;
1718

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

37-
public static DockerClient getDockerClient(HostAndSocket runtimeInfo) {
38+
public static DockerClient getDockerClient(HostAndSocketConfig runtimeInfo) {
3839
return getDockerClient(runtimeInfo, new ArrayList<RegistryAuthConfig>(){});
3940
}
4041

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

52-
log.debug("Using dockerhost " + runtimeInfo.host);
52+
log.debug("Using dockerhost " + runtimeInfo.getHost().get());
5353
DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
54-
.withDockerHost(runtimeInfo.host)
54+
.withDockerHost(runtimeInfo.getHost().get())
5555
.build();
5656

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

70-
public static class HostAndSocket {
71-
public String host;
72-
public String socket;
73-
public HostAndSocket(String host, String socket){
74-
this.host = host; this.socket = socket;
75-
}
76-
public HostAndSocket(String host){
77-
this.host = host;
78-
this.socket = null;
79-
}
80-
}
81-
82-
public static HostAndSocket probeContainerRuntime(HostAndSocket overrides) {
70+
public static HostAndSocketConfig probeContainerRuntime(HostAndSocketConfig overrides) {
71+
log.debug("Probing for container runtime... "+(overrides!=null ? "H:"+overrides.getHost()+" S:"+overrides.getSocket() : "no overrides"));
8372
if(overrides!=null){
8473
//if user has supplied both host & socket, we don't need to probe at all, use their values.
8574
//(using isEmpty as isBlank is jdk11 onwards)
86-
if(overrides.host!=null && overrides.socket!=null && !overrides.host.isEmpty() && !overrides.socket.isEmpty()){
87-
return overrides;
88-
}
89-
90-
//sanitize blank/empty values to null. (also using isEmpty because isBlank is only from jdk11 onwards)
91-
if(overrides.host!=null && overrides.host.isEmpty()){
92-
overrides.host=null;
93-
}
94-
if(overrides.socket!=null && overrides.socket.isEmpty()){
95-
overrides.socket=null;
75+
if(overrides.getHost().isPresent() && overrides.getSocket().isPresent()){
76+
return new HostAndSocketConfig(overrides.getHost().get(),overrides.getSocket().get());
9677
}
97-
}else{
98-
//no overrides at all? make an empty one.
99-
overrides = new HostAndSocket(null, null);
10078
}
10179

10280
try{
10381
//configure the override values as Optionals.
104-
Optional<String> dockerHost = Optional.ofNullable(overrides.host);
82+
Optional<String> dockerHost = overrides==null ? Optional.empty() : overrides.getHost();
10583
//for dockerhost, if user override was null, try to honor the env var
10684
if(!dockerHost.isPresent()){
10785
dockerHost = Optional.ofNullable(System.getenv("DOCKER_HOST"));
10886
}
109-
Optional<String> dockerSocket = Optional.ofNullable(overrides.socket);
87+
Optional<String> dockerSocket = overrides==null ? Optional.empty() : overrides.getSocket();
88+
89+
//if we now have a host & socket, we are done.
90+
if(dockerHost.isPresent() && dockerSocket.isPresent()){
91+
log.debug("Using docker host "+dockerHost.get()+" and socket "+dockerSocket.get());
92+
return new HostAndSocketConfig(dockerHost.get(),dockerSocket.get());
93+
}
11094

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

104+
//we are still missing a host or socket, so we need to probe for them.
119105
//try to obtain podman socket path..
120106
log.info("Testing for podman/docker...");
121107
DockerClientUtils.CmdResult cr = DockerClientUtils.start(PODMAN_SOCKET);
@@ -124,58 +110,59 @@ public static HostAndSocket probeContainerRuntime(HostAndSocket overrides) {
124110
String socket = cr.output.get(0);
125111
if(socket.startsWith("unix://")){
126112
socket = socket.substring("unix://".length());
127-
}
113+
}
114+
log.debug("Using derived socket path value of "+socket+" from podman cli invocation.");
128115
//podman was present, use podman to retrieve dockerhost value
129116
switch (OperatingSytem.getOperationSystem()) {
130117
case WIN:{
131118
DockerClientUtils.CmdResult scmd = DockerClientUtils.start(WIN_PODMAN_HOST);
132119
if(scmd.rc==0){
133-
String fixedhost = scmd.output.get(0).replaceAll("\\", "/");
134-
return new HostAndSocket(dockerHost.orElse(fixedhost), dockerSocket.orElse(cr.output.get(0)));
120+
String fixedhost = scmd.output.get(0).replaceAll("\\\\", "/");
121+
return new HostAndSocketConfig(dockerHost.orElse(fixedhost), dockerSocket.orElse(socket));
135122
}else{
136123
log.warn("Unable to obtain podman socket path from podman, using internal default");
137-
return new HostAndSocket(dockerHost.orElse("npipe:////./pipe/docker_engine"),dockerSocket.orElse("/var/run/docker.sock"));
124+
return new HostAndSocketConfig(dockerHost.orElse("npipe:////./pipe/docker_engine"),dockerSocket.orElse("/var/run/docker.sock"));
138125
}
139126
}
140127
case LINUX:{
141128
DockerClientUtils.CmdResult scmd = DockerClientUtils.start(LIN_PODMAN_HOST);
142129
if(scmd.rc==0){
143-
return new HostAndSocket(dockerHost.orElse(scmd.output.get(0)), dockerSocket.orElse(socket));
130+
return new HostAndSocketConfig(dockerHost.orElse(scmd.output.get(0)), dockerSocket.orElse(socket));
144131
}else{
145132
log.warn("Unable to obtain podman socket path from podman, using internal default");
146-
return new HostAndSocket(dockerHost.orElse("unix:///var/run/podman.sock"), dockerSocket.orElse("/var/run/podman.sock"));
133+
return new HostAndSocketConfig(dockerHost.orElse("unix:///var/run/podman.sock"), dockerSocket.orElse("/var/run/podman.sock"));
147134
}
148135
}
149136
case MAC:{
150137
DockerClientUtils.CmdResult scmd = DockerClientUtils.start(MAC_PODMAN_HOST);
151138
if(scmd.rc==0){
152-
return new HostAndSocket(dockerHost.orElse(scmd.output.get(0)), dockerSocket.orElse(socket));
139+
return new HostAndSocketConfig(dockerHost.orElse(scmd.output.get(0)), dockerSocket.orElse(socket));
153140
}else{
154141
log.warn("Unable to obtain podman socket path from podman, using internal default");
155-
return new HostAndSocket(dockerHost.orElse("unix:///var/run/podman.sock"), dockerSocket.orElse("/var/run/podman.sock"));
142+
return new HostAndSocketConfig(dockerHost.orElse("unix:///var/run/podman.sock"), dockerSocket.orElse("/var/run/podman.sock"));
156143
}
157144
}
158145
case UNKNOWN:{
159146
log.warn("Unable to identify Operating System, you may need to specify docker host / docker socket manually");
160-
return new HostAndSocket(dockerHost.orElse("unix:///var/run/podman.sock"), dockerSocket.orElse("/var/run/podman.sock"));
147+
return new HostAndSocketConfig(dockerHost.orElse("unix:///var/run/podman.sock"), dockerSocket.orElse("/var/run/podman.sock"));
161148
}
162149
}
163150
}else{
164151
log.info("Assuming docker, configuring.");
165152
//failed to obtain podman socket path, assuming docker..
166153
switch (OperatingSytem.getOperationSystem()) {
167154
case WIN:{
168-
return new HostAndSocket(dockerHost.orElse("npipe:////./pipe/docker_engine"),dockerSocket.orElse("/var/run/docker.sock"));
155+
return new HostAndSocketConfig(dockerHost.orElse("npipe:////./pipe/docker_engine"),dockerSocket.orElse("/var/run/docker.sock"));
169156
}
170157
case LINUX:{
171-
return new HostAndSocket(dockerHost.orElse("unix:///var/run/docker.sock"), dockerSocket.orElse("/var/run/docker.sock"));
158+
return new HostAndSocketConfig(dockerHost.orElse("unix:///var/run/docker.sock"), dockerSocket.orElse("/var/run/docker.sock"));
172159
}
173160
case MAC:{
174-
return new HostAndSocket(dockerHost.orElse("unix:///var/run/docker.sock"), dockerSocket.orElse("/var/run/docker.sock"));
161+
return new HostAndSocketConfig(dockerHost.orElse("unix:///var/run/docker.sock"), dockerSocket.orElse("/var/run/docker.sock"));
175162
}
176163
case UNKNOWN:{
177164
log.warn("Unable to identify Operating System, you may need to specify docker host / docker socket manually");
178-
return new HostAndSocket(dockerHost.orElse("unix:///var/run/docker.sock"), dockerSocket.orElse("/var/run/docker.sock"));
165+
return new HostAndSocketConfig(dockerHost.orElse("unix:///var/run/docker.sock"), dockerSocket.orElse("/var/run/docker.sock"));
179166
}
180167
}
181168
}

client/src/main/java/dev/snowdrop/buildpack/lifecycle/LifecyclePhaseFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public String getContainerForPhase(String args[], Integer runAsId){
9393
));
9494

9595
if(dockerConfig.getUseDaemon())
96-
binds.add(new VolumeBind(dockerConfig.getDockerSocket(), LifecyclePhaseFactory.DOCKER_SOCKET_PATH));
96+
binds.add(new VolumeBind(dockerConfig.getHostAndSocketConfig().getSocket().get(), LifecyclePhaseFactory.DOCKER_SOCKET_PATH));
9797

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

0 commit comments

Comments
 (0)