Skip to content

Commit 8e17682

Browse files
Merge pull request #22736 from anshlykov
* gh-22736: Polish "Add pullPolicy option for image building" Add pullPolicy option for image building Closes gh-22736
2 parents b35cfb7 + 6b15822 commit 8e17682

File tree

19 files changed

+426
-38
lines changed

19 files changed

+426
-38
lines changed

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/AbstractBuildLog.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
*
3131
* @author Phillip Webb
3232
* @author Scott Frederick
33+
* @author Andrey Shlykov
3334
* @since 2.3.0
3435
*/
3536
public abstract class AbstractBuildLog implements BuildLog {
@@ -41,23 +42,37 @@ public void start(BuildRequest request) {
4142
}
4243

4344
@Override
45+
@Deprecated
4446
public Consumer<TotalProgressEvent> pullingBuilder(BuildRequest request, ImageReference imageReference) {
45-
return getProgressConsumer(" > Pulling builder image '" + imageReference + "'");
47+
return pullingImage(imageReference, ImageType.BUILDER);
4648
}
4749

4850
@Override
51+
@Deprecated
4952
public void pulledBuilder(BuildRequest request, Image image) {
50-
log(" > Pulled builder image '" + getDigest(image) + "'");
53+
pulledImage(image, ImageType.BUILDER);
5154
}
5255

5356
@Override
57+
@Deprecated
5458
public Consumer<TotalProgressEvent> pullingRunImage(BuildRequest request, ImageReference imageReference) {
55-
return getProgressConsumer(" > Pulling run image '" + imageReference + "'");
59+
return pullingImage(imageReference, ImageType.RUNNER);
5660
}
5761

5862
@Override
63+
@Deprecated
5964
public void pulledRunImage(BuildRequest request, Image image) {
60-
log(" > Pulled run image '" + getDigest(image) + "'");
65+
pulledImage(image, ImageType.RUNNER);
66+
}
67+
68+
@Override
69+
public Consumer<TotalProgressEvent> pullingImage(ImageReference imageReference, ImageType imageType) {
70+
return getProgressConsumer(String.format(" > Pulling %s '%s'", imageType.getDescription(), imageReference));
71+
}
72+
73+
@Override
74+
public void pulledImage(Image image, ImageType imageType) {
75+
log(String.format(" > Pulled %s '%s'", imageType.getDescription(), getDigest(image)));
6176
}
6277

6378
@Override

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildLog.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
*
3131
* @author Phillip Webb
3232
* @author Scott Frederick
33+
* @author Andrey Shlykov
3334
* @since 2.3.0
3435
* @see #toSystemOut()
3536
*/
@@ -46,31 +47,56 @@ public interface BuildLog {
4647
* @param request the build request
4748
* @param imageReference the builder image reference
4849
* @return a consumer for progress update events
50+
* @deprecated since 2.4.0 in favor of
51+
* {@link #pullingImage(ImageReference, ImageType)}
4952
*/
53+
@Deprecated
5054
Consumer<TotalProgressEvent> pullingBuilder(BuildRequest request, ImageReference imageReference);
5155

5256
/**
5357
* Log that the builder image has been pulled.
5458
* @param request the build request
5559
* @param image the builder image that was pulled
60+
* @deprecated since 2.4.0 in favor of {@link #pulledImage(Image, ImageType)}
5661
*/
62+
@Deprecated
5763
void pulledBuilder(BuildRequest request, Image image);
5864

5965
/**
6066
* Log that a run image is being pulled.
6167
* @param request the build request
6268
* @param imageReference the run image reference
6369
* @return a consumer for progress update events
70+
* @deprecated since 2.4.0 in favor of
71+
* {@link #pullingImage(ImageReference, ImageType)}
6472
*/
73+
@Deprecated
6574
Consumer<TotalProgressEvent> pullingRunImage(BuildRequest request, ImageReference imageReference);
6675

6776
/**
6877
* Log that a run image has been pulled.
6978
* @param request the build request
7079
* @param image the run image that was pulled
80+
* @deprecated since 2.4.0 in favor of {@link #pulledImage(Image, ImageType)}
7181
*/
82+
@Deprecated
7283
void pulledRunImage(BuildRequest request, Image image);
7384

85+
/**
86+
* Log that an image is being pulled.
87+
* @param imageReference the image reference
88+
* @param imageType the image type
89+
* @return a consumer for progress update events
90+
*/
91+
Consumer<TotalProgressEvent> pullingImage(ImageReference imageReference, ImageType imageType);
92+
93+
/**
94+
* Log that an image has been pulled.
95+
* @param image the builder image that was pulled
96+
* @param imageType the image type that was pulled
97+
*/
98+
void pulledImage(Image image, ImageType imageType);
99+
74100
/**
75101
* Log that the lifecycle is executing.
76102
* @param request the build request

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildRequest.java

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
*
3333
* @author Phillip Webb
3434
* @author Scott Frederick
35+
* @author Andrey Shlykov
3536
* @since 2.3.0
3637
*/
3738
public class BuildRequest {
@@ -56,6 +57,8 @@ public class BuildRequest {
5657

5758
private final boolean verboseLogging;
5859

60+
private final PullPolicy pullPolicy;
61+
5962
BuildRequest(ImageReference name, Function<Owner, TarArchive> applicationContent) {
6063
Assert.notNull(name, "Name must not be null");
6164
Assert.notNull(applicationContent, "ApplicationContent must not be null");
@@ -66,12 +69,13 @@ public class BuildRequest {
6669
this.env = Collections.emptyMap();
6770
this.cleanCache = false;
6871
this.verboseLogging = false;
72+
this.pullPolicy = PullPolicy.ALWAYS;
6973
this.creator = Creator.withVersion("");
7074
}
7175

7276
BuildRequest(ImageReference name, Function<Owner, TarArchive> applicationContent, ImageReference builder,
7377
ImageReference runImage, Creator creator, Map<String, String> env, boolean cleanCache,
74-
boolean verboseLogging) {
78+
boolean verboseLogging, PullPolicy pullPolicy) {
7579
this.name = name;
7680
this.applicationContent = applicationContent;
7781
this.builder = builder;
@@ -80,6 +84,7 @@ public class BuildRequest {
8084
this.env = env;
8185
this.cleanCache = cleanCache;
8286
this.verboseLogging = verboseLogging;
87+
this.pullPolicy = pullPolicy;
8388
}
8489

8590
/**
@@ -90,7 +95,7 @@ public class BuildRequest {
9095
public BuildRequest withBuilder(ImageReference builder) {
9196
Assert.notNull(builder, "Builder must not be null");
9297
return new BuildRequest(this.name, this.applicationContent, builder.inTaggedOrDigestForm(), this.runImage,
93-
this.creator, this.env, this.cleanCache, this.verboseLogging);
98+
this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy);
9499
}
95100

96101
/**
@@ -100,7 +105,7 @@ public BuildRequest withBuilder(ImageReference builder) {
100105
*/
101106
public BuildRequest withRunImage(ImageReference runImageName) {
102107
return new BuildRequest(this.name, this.applicationContent, this.builder, runImageName.inTaggedOrDigestForm(),
103-
this.creator, this.env, this.cleanCache, this.verboseLogging);
108+
this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy);
104109
}
105110

106111
/**
@@ -111,7 +116,7 @@ public BuildRequest withRunImage(ImageReference runImageName) {
111116
public BuildRequest withCreator(Creator creator) {
112117
Assert.notNull(creator, "Creator must not be null");
113118
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, creator, this.env,
114-
this.cleanCache, this.verboseLogging);
119+
this.cleanCache, this.verboseLogging, this.pullPolicy);
115120
}
116121

117122
/**
@@ -126,7 +131,7 @@ public BuildRequest withEnv(String name, String value) {
126131
Map<String, String> env = new LinkedHashMap<>(this.env);
127132
env.put(name, value);
128133
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator,
129-
Collections.unmodifiableMap(env), this.cleanCache, this.verboseLogging);
134+
Collections.unmodifiableMap(env), this.cleanCache, this.verboseLogging, this.pullPolicy);
130135
}
131136

132137
/**
@@ -139,7 +144,7 @@ public BuildRequest withEnv(Map<String, String> env) {
139144
Map<String, String> updatedEnv = new LinkedHashMap<>(this.env);
140145
updatedEnv.putAll(env);
141146
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator,
142-
Collections.unmodifiableMap(updatedEnv), this.cleanCache, this.verboseLogging);
147+
Collections.unmodifiableMap(updatedEnv), this.cleanCache, this.verboseLogging, this.pullPolicy);
143148
}
144149

145150
/**
@@ -149,7 +154,7 @@ public BuildRequest withEnv(Map<String, String> env) {
149154
*/
150155
public BuildRequest withCleanCache(boolean cleanCache) {
151156
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
152-
cleanCache, this.verboseLogging);
157+
cleanCache, this.verboseLogging, this.pullPolicy);
153158
}
154159

155160
/**
@@ -159,7 +164,17 @@ public BuildRequest withCleanCache(boolean cleanCache) {
159164
*/
160165
public BuildRequest withVerboseLogging(boolean verboseLogging) {
161166
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
162-
this.cleanCache, verboseLogging);
167+
this.cleanCache, verboseLogging, this.pullPolicy);
168+
}
169+
170+
/**
171+
* Return a new {@link BuildRequest} with the updated image pull policy.
172+
* @param pullPolicy image pull policy {@link PullPolicy}
173+
* @return an updated build request
174+
*/
175+
public BuildRequest withPullPolicy(PullPolicy pullPolicy) {
176+
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
177+
this.cleanCache, this.verboseLogging, pullPolicy);
163178
}
164179

165180
/**
@@ -229,6 +244,14 @@ public boolean isVerboseLogging() {
229244
return this.verboseLogging;
230245
}
231246

247+
/**
248+
* Return the image {@link PullPolicy} that the builder should use.
249+
* @return image pull policy
250+
*/
251+
public PullPolicy getPullPolicy() {
252+
return this.pullPolicy;
253+
}
254+
232255
/**
233256
* Factory method to create a new {@link BuildRequest} from a JAR file.
234257
* @param jarFile the source jar file

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Builder.java

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
*
3636
* @author Phillip Webb
3737
* @author Scott Frederick
38+
* @author Andrey Shlykov
3839
* @since 2.3.0
3940
*/
4041
public class Builder {
@@ -60,7 +61,7 @@ public Builder(BuildLog log) {
6061
public void build(BuildRequest request) throws DockerEngineException, IOException {
6162
Assert.notNull(request, "Request must not be null");
6263
this.log.start(request);
63-
Image builderImage = pullBuilder(request);
64+
Image builderImage = getImage(request, ImageType.BUILDER);
6465
BuilderMetadata builderMetadata = BuilderMetadata.fromImage(builderImage);
6566
BuildOwner buildOwner = BuildOwner.fromEnv(builderImage.getConfig().getEnv());
6667
request = determineRunImage(request, builderImage, builderMetadata.getStack());
@@ -75,22 +76,13 @@ public void build(BuildRequest request) throws DockerEngineException, IOExceptio
7576
}
7677
}
7778

78-
private Image pullBuilder(BuildRequest request) throws IOException {
79-
ImageReference builderImageReference = request.getBuilder();
80-
Consumer<TotalProgressEvent> progressConsumer = this.log.pullingBuilder(request, builderImageReference);
81-
TotalProgressPullListener listener = new TotalProgressPullListener(progressConsumer);
82-
Image builderImage = this.docker.image().pull(builderImageReference, listener);
83-
this.log.pulledBuilder(request, builderImage);
84-
return builderImage;
85-
}
86-
8779
private BuildRequest determineRunImage(BuildRequest request, Image builderImage, Stack builderStack)
8880
throws IOException {
8981
if (request.getRunImage() == null) {
9082
ImageReference runImage = getRunImageReferenceForStack(builderStack);
9183
request = request.withRunImage(runImage);
9284
}
93-
Image runImage = pullRunImage(request);
85+
Image runImage = getImage(request, ImageType.RUNNER);
9486
assertStackIdsMatch(runImage, builderImage);
9587
return request;
9688
}
@@ -101,12 +93,31 @@ private ImageReference getRunImageReferenceForStack(Stack stack) {
10193
return ImageReference.of(name).inTaggedOrDigestForm();
10294
}
10395

104-
private Image pullRunImage(BuildRequest request) throws IOException {
105-
ImageReference runImage = request.getRunImage();
106-
Consumer<TotalProgressEvent> progressConsumer = this.log.pullingRunImage(request, runImage);
96+
private Image getImage(BuildRequest request, ImageType imageType) throws IOException {
97+
ImageReference imageReference = (imageType == ImageType.BUILDER) ? request.getBuilder() : request.getRunImage();
98+
99+
if (request.getPullPolicy() == PullPolicy.ALWAYS) {
100+
return pullImage(imageReference, imageType);
101+
}
102+
103+
try {
104+
return this.docker.image().inspect(imageReference);
105+
}
106+
catch (DockerEngineException exception) {
107+
if (request.getPullPolicy() == PullPolicy.IF_NOT_PRESENT && exception.getStatusCode() == 404) {
108+
return pullImage(imageReference, imageType);
109+
}
110+
else {
111+
throw exception;
112+
}
113+
}
114+
}
115+
116+
private Image pullImage(ImageReference reference, ImageType imageType) throws IOException {
117+
Consumer<TotalProgressEvent> progressConsumer = this.log.pullingImage(reference, imageType);
107118
TotalProgressPullListener listener = new TotalProgressPullListener(progressConsumer);
108-
Image image = this.docker.image().pull(runImage, listener);
109-
this.log.pulledRunImage(request, image);
119+
Image image = this.docker.image().pull(reference, listener);
120+
this.log.pulledImage(image, imageType);
110121
return image;
111122
}
112123

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.buildpack.platform.build;
18+
19+
/**
20+
* Image types.
21+
*
22+
* @author Andrey Shlykov
23+
*/
24+
enum ImageType {
25+
26+
/**
27+
* Builder image.
28+
*/
29+
BUILDER("builder image"),
30+
31+
/**
32+
* Run image.
33+
*/
34+
RUNNER("run image");
35+
36+
private final String description;
37+
38+
ImageType(String description) {
39+
this.description = description;
40+
}
41+
42+
String getDescription() {
43+
return this.description;
44+
}
45+
46+
}

0 commit comments

Comments
 (0)