Skip to content

Commit 3a45bd0

Browse files
committed
Add docker options for pull retry
1 parent 27fc32e commit 3a45bd0

File tree

7 files changed

+61
-37
lines changed

7 files changed

+61
-37
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ The [`BuildpackConfig`](client/src/main/java/dev/snowdrop/buildpack/BuildConfig.
3333

3434
- run/build/output Image can be specified
3535
- docker can be configured with..
36-
- pull timeout
36+
- pull timeout
37+
- pull retry count (will retry image pull on failure)
38+
- pull retry timeout increase (increases timeout each time pull is retried)
3739
- host
3840
- network
3941
- docker socket path

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ public static DockerConfigBuilder builder() {
1515
public static enum PullPolicy {ALWAYS, IF_NOT_PRESENT};
1616

1717
private static final Integer DEFAULT_PULL_TIMEOUT = 60;
18+
private static final Integer DEFAULT_PULL_RETRY_INCREASE = 15;
1819
private static final Integer DEFAULT_PULL_RETRY_COUNT = 3;
1920
private static final PullPolicy DEFAULT_PULL_POLICY = PullPolicy.IF_NOT_PRESENT;
2021

2122
private Integer pullTimeoutSeconds;
2223
private Integer pullRetryCount;
24+
private Integer pullRetryIncreaseSeconds;
2325
private PullPolicy pullPolicy;
2426
private String dockerHost;
2527
private String dockerSocket;
@@ -30,15 +32,17 @@ public static enum PullPolicy {ALWAYS, IF_NOT_PRESENT};
3032
public DockerConfig(
3133
Integer pullTimeoutSeconds,
3234
Integer pullRetryCount,
35+
Integer pullRetryIncreaseSeconds,
3336
PullPolicy pullPolicy,
3437
String dockerHost,
3538
String dockerSocket,
3639
String dockerNetwork,
3740
Boolean useDaemon,
3841
DockerClient dockerClient
3942
){
40-
this.pullTimeoutSeconds = pullTimeoutSeconds != null ? pullTimeoutSeconds : DEFAULT_PULL_TIMEOUT;
41-
this.pullRetryCount = pullRetryCount != null ? pullRetryCount : DEFAULT_PULL_RETRY_COUNT;
43+
this.pullTimeoutSeconds = pullTimeoutSeconds != null ? Integer.max(0,pullTimeoutSeconds) : DEFAULT_PULL_TIMEOUT;
44+
this.pullRetryCount = pullRetryCount != null ? Integer.max(0,pullRetryCount) : DEFAULT_PULL_RETRY_COUNT;
45+
this.pullRetryIncreaseSeconds = pullRetryIncreaseSeconds != null ? Integer.max(0,pullRetryIncreaseSeconds) : DEFAULT_PULL_RETRY_INCREASE;
4246
this.pullPolicy = pullPolicy != null ? pullPolicy : DEFAULT_PULL_POLICY;
4347
this.dockerHost = dockerHost != null ? dockerHost : DockerClientUtils.getDockerHost();
4448
this.dockerSocket = dockerSocket != null ? dockerSocket : (this.dockerHost.startsWith("unix://") ? this.dockerHost.substring("unix://".length()) : "/var/run/docker.sock");
@@ -61,6 +65,10 @@ public Integer getPullRetryCount(){
6165
return this.pullRetryCount;
6266
}
6367

68+
public Integer getPullRetryIncrease(){
69+
return this.pullRetryIncreaseSeconds;
70+
}
71+
6472
public PullPolicy getPullPolicy(){
6573
return this.pullPolicy;
6674
}

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

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.Map.Entry;
1010
import java.util.Set;
1111
import java.util.concurrent.TimeUnit;
12+
import java.util.stream.Collectors;
1213

1314
import org.slf4j.Logger;
1415
import org.slf4j.LoggerFactory;
@@ -18,6 +19,7 @@
1819
import com.github.dockerjava.api.command.PullImageResultCallback;
1920
import com.github.dockerjava.api.model.Image;
2021
import com.github.dockerjava.api.exception.DockerClientException;
22+
import com.github.dockerjava.api.exception.NotFoundException;
2123

2224
import dev.snowdrop.buildpack.config.DockerConfig;
2325
import dev.snowdrop.buildpack.BuildpackException;
@@ -36,6 +38,7 @@ public static class ImageInfo {
3638
/**
3739
* Util method to pull images, configure behavior via dockerconfig.
3840
*/
41+
@SuppressWarnings("resource")
3942
public static void pullImages(DockerConfig config, String... imageNames) {
4043
Set<String> imageNameSet = new HashSet<>(Arrays.asList(imageNames));
4144

@@ -63,7 +66,7 @@ public static void pullImages(DockerConfig config, String... imageNames) {
6366
}
6467
}
6568

66-
int retriesRemaining = Integer.max(config.getPullRetryCount(), 0);
69+
int retryCount = 0;
6770
Map<String,PullImageResultCallback> pircMap = new HashMap<>();
6871

6972
// pull the images still in set.
@@ -75,20 +78,25 @@ public static void pullImages(DockerConfig config, String... imageNames) {
7578
}
7679

7780
// wait for pulls to complete.
78-
DockerClientException lastSeen = null;
79-
boolean allDone = true;
80-
while(!allDone && retriesRemaining>=0){
81+
RuntimeException lastSeen = null;
82+
boolean allDone = false;
83+
while(!allDone && retryCount<=config.getPullRetryCount()){
8184
allDone = true;
85+
long thisWait = config.getPullTimeout()+(retryCount*config.getPullRetryIncrease());
8286
for (Entry<String, PullImageResultCallback> e : pircMap.entrySet()) {
8387
boolean done = false;
8488
try {
8589
if(e.getValue()==null) continue;
86-
done = e.getValue().awaitCompletion(config.getPullTimeout(), TimeUnit.SECONDS);
90+
log.debug("waiting on image "+e.getKey()+" for "+thisWait+" seconds");
91+
done = e.getValue().awaitCompletion( thisWait, TimeUnit.SECONDS);
92+
log.debug("success for image "+e.getKey());
8793
} catch (InterruptedException ie) {
8894
throw BuildpackException.launderThrowable(ie);
8995
} catch (DockerClientException dce) {
9096
//error occurred during pull for this pirc, need to pause & retry the pull op
9197
lastSeen = dce;
98+
} catch (NotFoundException nfe) {
99+
lastSeen = nfe;
92100
}
93101
if(!done){
94102
String imageName = e.getKey();
@@ -100,10 +108,16 @@ public static void pullImages(DockerConfig config, String... imageNames) {
100108
e.setValue(null);
101109
}
102110
}
103-
retriesRemaining-=1;
111+
retryCount++;
112+
if(retryCount<=config.getPullRetryCount()){
113+
if(lastSeen!=null){
114+
log.debug("Error during pull "+lastSeen.getMessage());
115+
}
116+
log.debug("Retrying ("+retryCount+") for "+pircMap.entrySet().stream().filter(e -> e.getValue()!=null).collect(Collectors.toList()));
117+
}
104118
}
105119

106-
if(lastSeen!=null){
120+
if(lastSeen!=null && !allDone){
107121
throw lastSeen;
108122
}
109123
}

client/src/test/java/dev/snowdrop/buildpack/config/DockerConfigTest.java

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import static org.junit.jupiter.api.Assertions.assertNull;
77
import static org.junit.jupiter.api.Assertions.assertTrue;
88
import static org.mockito.Mockito.lenient;
9-
import static org.mockito.Mockito.when;
109

1110
import org.junit.jupiter.api.Test;
1211
import org.junit.jupiter.api.extension.ExtendWith;
@@ -20,21 +19,21 @@
2019
public class DockerConfigTest {
2120
@Test
2221
void checkTimeout() {
23-
DockerConfig dc1 = new DockerConfig(null, null, null, null, null, null, null, null);
22+
DockerConfig dc1 = new DockerConfig(null, null, null, null, null, null, null, null, null);
2423
assertEquals(60, dc1.getPullTimeout());
2524

26-
DockerConfig dc2 = new DockerConfig(245017, null, null, null, null, null, null, null);
25+
DockerConfig dc2 = new DockerConfig(245017, null, null, null, null, null, null, null, null);
2726
assertEquals(dc2.getPullTimeout(), 245017);
2827
}
2928

3029
@Test
3130
void checkDockerHost(@Mock DockerClient dockerClient, @Mock PingCmd pingCmd) {
3231
lenient().when(dockerClient.pingCmd()).thenReturn(pingCmd);
3332

34-
DockerConfig dc1 = new DockerConfig(null, null, null, null, null, null, null, null);
33+
DockerConfig dc1 = new DockerConfig(null, null, null, null, null, null, null, null, null);
3534
assertNotNull(dc1.getDockerHost());
3635

37-
DockerConfig dc2 = new DockerConfig(null, null, null, "tcp://stilettos", null, null, null, dockerClient);
36+
DockerConfig dc2 = new DockerConfig(null, null, null, null, "tcp://stilettos", null, null, null, dockerClient);
3837
assertEquals("tcp://stilettos", dc2.getDockerHost());
3938
}
4039

@@ -43,62 +42,62 @@ void checkDockerSocket(@Mock DockerClient dockerClient, @Mock PingCmd pingCmd) {
4342

4443
lenient().when(dockerClient.pingCmd()).thenReturn(pingCmd);
4544

46-
DockerConfig dc1 = new DockerConfig(null, null, null, null, null, null, null, null);
45+
DockerConfig dc1 = new DockerConfig(null, null, null, null, null, null, null, null, null);
4746
assertNotNull(dc1.getDockerSocket());
4847

49-
DockerConfig dc2 = new DockerConfig(null, null, null, "unix:///stilettos", null, null, null, dockerClient);
48+
DockerConfig dc2 = new DockerConfig(null, null, null, null, "unix:///stilettos", null, null, null, dockerClient);
5049
assertEquals("/stilettos", dc2.getDockerSocket());
5150

52-
DockerConfig dc3 = new DockerConfig(null, null, null, "tcp://stilettos", null, null, null, dockerClient);
51+
DockerConfig dc3 = new DockerConfig(null, null, null, null, "tcp://stilettos", null, null, null, dockerClient);
5352
assertEquals("/var/run/docker.sock", dc3.getDockerSocket());
5453

55-
DockerConfig dc4 = new DockerConfig(null, null, null, null, "fish", null, null, null);
54+
DockerConfig dc4 = new DockerConfig(null, null, null, null, null, "fish", null, null, null);
5655
assertEquals("fish", dc4.getDockerSocket());
5756
}
5857

5958
@Test
6059
void checkDockerNetwork() {
61-
DockerConfig dc1 = new DockerConfig(null, null, null, null, null, "kitten", null, null);
60+
DockerConfig dc1 = new DockerConfig(null, null, null, null, null, null, "kitten", null, null);
6261
assertEquals("kitten", dc1.getDockerNetwork());
6362

64-
DockerConfig dc2 = new DockerConfig(null, null, null, null, null, null, null, null);
63+
DockerConfig dc2 = new DockerConfig(null, null, null, null, null, null, null, null, null);
6564
assertNull(dc2.getDockerNetwork());
6665
}
6766

6867
@Test
6968
void checkUseDaemon() {
70-
DockerConfig dc1 = new DockerConfig(null, null, null, null, null, null, null, null);
69+
DockerConfig dc1 = new DockerConfig(null, null, null, null, null, null, null, null, null);
7170
assertTrue(dc1.getUseDaemon());
7271

73-
DockerConfig dc2 = new DockerConfig(null, null, null, null, null, null, true, null);
72+
DockerConfig dc2 = new DockerConfig(null, null, null, null, null, null, null, true, null);
7473
assertTrue(dc2.getUseDaemon());
7574

76-
DockerConfig dc3 = new DockerConfig(null, null, null, null, null, null, false, null);
75+
DockerConfig dc3 = new DockerConfig(null, null, null, null, null, null, null, false, null);
7776
assertFalse(dc3.getUseDaemon());
7877
}
7978

8079
@Test
8180
void checkDockerClient(@Mock DockerClient dockerClient, @Mock PingCmd pingCmd){
8281
lenient().when(dockerClient.pingCmd()).thenReturn(pingCmd);
8382

84-
DockerConfig dc1 = new DockerConfig(null, null, null, null, null, null, null, null);
83+
DockerConfig dc1 = new DockerConfig(null, null, null, null, null, null, null, null, null);
8584
assertNotNull(dc1.getDockerClient());
8685

87-
DockerConfig dc2 = new DockerConfig(null, null, null, null, null, null, null, dockerClient);
86+
DockerConfig dc2 = new DockerConfig(null, null, null, null, null, null, null, null, dockerClient);
8887
assertEquals(dockerClient, dc2.getDockerClient());
8988
}
9089

9190
@Test
9291
void checkPullPolicy(@Mock DockerClient dockerClient, @Mock PingCmd pingCmd){
9392
lenient().when(dockerClient.pingCmd()).thenReturn(pingCmd);
9493

95-
DockerConfig dc1 = new DockerConfig(null, null, null, null, null, null, null, dockerClient);
94+
DockerConfig dc1 = new DockerConfig(null, null, null, null, null, null, null, null, dockerClient);
9695
assertEquals(DockerConfig.PullPolicy.IF_NOT_PRESENT, dc1.getPullPolicy());
9796

98-
DockerConfig dc2 = new DockerConfig(null, null, DockerConfig.PullPolicy.IF_NOT_PRESENT, null, null, null, null, dockerClient);
97+
DockerConfig dc2 = new DockerConfig(null, null, null, DockerConfig.PullPolicy.IF_NOT_PRESENT, null, null, null, null, dockerClient);
9998
assertEquals(DockerConfig.PullPolicy.IF_NOT_PRESENT, dc2.getPullPolicy());
10099

101-
DockerConfig dc3 = new DockerConfig(null, null, DockerConfig.PullPolicy.ALWAYS, null, null, null, null, dockerClient);
100+
DockerConfig dc3 = new DockerConfig(null, null, null, DockerConfig.PullPolicy.ALWAYS, null, null, null, null, dockerClient);
102101
assertEquals(DockerConfig.PullPolicy.ALWAYS, dc3.getPullPolicy());
103102
}
104103

@@ -107,13 +106,13 @@ void checkPullPolicy(@Mock DockerClient dockerClient, @Mock PingCmd pingCmd){
107106
void checkPullRetry(@Mock DockerClient dockerClient, @Mock PingCmd pingCmd){
108107
lenient().when(dockerClient.pingCmd()).thenReturn(pingCmd);
109108

110-
DockerConfig dc1 = new DockerConfig(null, null, null, null, null, null, null, dockerClient);
109+
DockerConfig dc1 = new DockerConfig(null, null, null, null, null, null, null, null, dockerClient);
111110
assertEquals(3, dc1.getPullRetryCount());
112111

113-
DockerConfig dc2 = new DockerConfig(null, 5, null, null, null, null, null, dockerClient);
112+
DockerConfig dc2 = new DockerConfig(null, 5, null, null, null, null, null, null, dockerClient);
114113
assertEquals(5, dc2.getPullRetryCount());
115114

116-
DockerConfig dc3 = new DockerConfig(null, 0, null, null, null, null, null, dockerClient);
115+
DockerConfig dc3 = new DockerConfig(null, 0, null, null, null, null, null, null, dockerClient);
117116
assertEquals(0, dc3.getPullRetryCount());
118117
}
119118
}

client/src/test/java/dev/snowdrop/buildpack/docker/ImageUtilsTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import static org.mockito.Mockito.when;
88
import static org.mockito.Mockito.never;
99
import static org.mockito.Mockito.lenient;
10+
import static org.mockito.Mockito.atLeast;
1011

1112
import java.util.ArrayList;
1213
import java.util.HashMap;
@@ -79,7 +80,7 @@ void testPullImageSingleUnknown(@Mock DockerConfig config,
7980

8081
ImageUtils.pullImages(config, imageName);
8182

82-
verify(pic).exec(ArgumentMatchers.any());
83+
verify(pic, atLeast(1)).exec(ArgumentMatchers.any());
8384
}
8485

8586
@Test

samples/hello-quarkus/pack.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ public static void main(String... args) {
2020
System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.lifecycle.phases","debug");
2121

2222
int exitCode = BuildConfig.builder()
23-
.withBuilderImage(new ImageReference("paketocommunity/builder-ubi-base:latest"))
24-
.withOutputImage(new ImageReference("snowdrop/hello-quarkus:latest"))
23+
.withBuilderImage(new ImageReference("docker.io/paketocommunity/builder-ubi-base"))
24+
.withOutputImage(new ImageReference("snowdrop/hello-quarkus"))
2525
.withNewLogConfig()
2626
.withLogger(new SystemLogger())
2727
.withLogLevel("debug")

samples/hello-spring/pack.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ public static void main(String... args) {
2222
HashMap<String,String> env = new HashMap<>();
2323

2424
int exitCode = BuildConfig.builder()
25-
.withBuilderImage(new ImageReference("paketocommunity/builder-ubi-base:latest"))
26-
.withOutputImage(new ImageReference("snowdrop/hello-spring:latest"))
25+
.withBuilderImage(new ImageReference("docker.io/paketocommunity/builder-ubi-base"))
26+
.withOutputImage(new ImageReference("snowdrop/hello-spring"))
2727
.withNewLogConfig()
2828
.withLogger(new SystemLogger())
2929
.withLogLevel("debug")

0 commit comments

Comments
 (0)