Skip to content

Commit fc6b044

Browse files
authored
Tag old images with no-new-use-public-image (#2938)
* [WIP] Tag old images with no-new-use-public-image * spotless * Exception handling and flag parsing
1 parent 92106f5 commit fc6b044

File tree

3 files changed

+87
-8
lines changed

3 files changed

+87
-8
lines changed

plugins/templates-maven-plugin/src/main/java/com/google/cloud/teleport/plugin/maven/PromoteHelper.java

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class PromoteHelper {
4343
private final String token;
4444
private final String imageTag;
4545
private final @Nullable String additionalTag;
46+
private final @Nullable String replacementTag;
4647

4748
/**
4849
* Promote the staged flex template image using MOSS promote API.
@@ -52,16 +53,26 @@ class PromoteHelper {
5253
* @param imageTag - image tag
5354
* @param additionalTag - additional destination tag, used by repo managements, e.g.
5455
* public-image-latest
56+
* @param replacementTag - tag to put on original holder of additionalTag, used by repo
57+
* managements, e.g. no-new-use-public-image-
5558
* @param sourceDigest - source image digest, e.g. sha256:xxxxx
5659
*/
5760
public PromoteHelper(
5861
String sourcePath,
5962
String targetPath,
6063
String imageTag,
6164
@Nullable String additionalTag,
65+
@Nullable String replacementTag,
6266
String sourceDigest)
6367
throws IOException, InterruptedException {
64-
this(sourcePath, targetPath, imageTag, additionalTag, sourceDigest, accessToken());
68+
this(
69+
sourcePath,
70+
targetPath,
71+
imageTag,
72+
additionalTag,
73+
replacementTag,
74+
sourceDigest,
75+
accessToken());
6576
}
6677

6778
@VisibleForTesting
@@ -70,30 +81,39 @@ public PromoteHelper(
7081
String targetPath,
7182
String imageTag,
7283
@Nullable String additionalTag,
84+
@Nullable String replacementTag,
7385
String sourceDigest,
7486
String token) {
7587
this.sourceSpec = new ArtifactRegImageSpec(sourcePath);
7688
this.targetSpec = new ArtifactRegImageSpec(targetPath);
7789
this.imageTag = imageTag;
7890
this.additionalTag = additionalTag;
91+
this.replacementTag = replacementTag;
7992
this.sourceDigest = sourceDigest;
8093
this.token = token;
8194
this.targetPath = targetPath;
8295
}
8396

8497
/** Promote the artifact. */
8598
public void promote() throws IOException, InterruptedException {
99+
String originalDigest = null;
100+
if (additionalTag != null) {
101+
originalDigest = getDigestFromTag(additionalTag);
102+
}
86103
String[] promoteArtifactCmd = getPromoteFlexTemplateImageCmd();
87104
// promote API returns a long-running-operation
88105
String responseRLO = TemplatesStageMojo.runCommandCapturesOutput(promoteArtifactCmd, null);
89106
JsonElement parsed = JsonParser.parseString(responseRLO);
90107
String operation = parsed.getAsJsonObject().get("name").getAsString();
91108
waitForComplete(operation);
92-
addTag(imageTag);
109+
addTag(imageTag, sourceDigest);
93110
// override latest (for pull default tag) and additionalTag (for vul scan, if present)
94-
addTag("latest");
111+
addTag("latest", sourceDigest);
95112
if (additionalTag != null) {
96-
addTag(additionalTag);
113+
addTag(additionalTag, sourceDigest);
114+
}
115+
if (originalDigest != null && !originalDigest.isEmpty()) {
116+
addTag(replacementTag, originalDigest);
97117
}
98118
}
99119

@@ -157,7 +177,7 @@ private void waitForComplete(String operation) {
157177
}
158178

159179
/** Add tag after promotion. */
160-
private void addTag(String tag) throws IOException, InterruptedException {
180+
private void addTag(String tag, String digest) throws IOException, InterruptedException {
161181
// TODO: remove this once copy tag is supported by promote API
162182
String[] command;
163183
if (targetSpec.repository.endsWith("gcr.io")) {
@@ -169,7 +189,7 @@ private void addTag(String tag) throws IOException, InterruptedException {
169189
"images",
170190
"add-tag",
171191
"-q",
172-
String.format("%s@%s", targetPath, sourceDigest),
192+
String.format("%s@%s", targetPath, digest),
173193
String.format("%s:%s", targetPath, tag)
174194
};
175195
} else {
@@ -180,13 +200,65 @@ private void addTag(String tag) throws IOException, InterruptedException {
180200
"docker",
181201
"tags",
182202
"add",
183-
String.format("%s@%s", targetPath, sourceDigest),
203+
String.format("%s@%s", targetPath, digest),
184204
String.format("%s:%s", targetPath, tag)
185205
};
186206
}
187207
TemplatesStageMojo.runCommandCapturesOutput(command, null);
188208
}
189209

210+
/**
211+
* Get the digest of an image with a specific tag.
212+
*
213+
* @param tag - The tag of the image to retrieve.
214+
* @return The digest of the image.
215+
*/
216+
@VisibleForTesting
217+
String getDigestFromTag(String tag) throws IOException, InterruptedException {
218+
String[] command;
219+
String imageReference = String.format("%s:%s", targetPath, tag);
220+
221+
if (targetSpec.repository.endsWith("gcr.io")) {
222+
// gcr.io repository needs to use `gcloud container` to list tags
223+
command =
224+
new String[] {
225+
"gcloud",
226+
"container",
227+
"images",
228+
"list-tags",
229+
targetPath, // This is the image name, e.g., us.gcr.io/my-project/my-image
230+
"--filter=tags:" + tag,
231+
"--format",
232+
"get(digest)"
233+
};
234+
} else {
235+
// Artifact Registry repository needs to use `gcloud artifacts docker images describe`
236+
command =
237+
new String[] {
238+
"gcloud",
239+
"artifacts",
240+
"docker",
241+
"images",
242+
"describe",
243+
imageReference, // This is the full image reference including tag, e.g.,
244+
// us-central1-docker.pkg.dev/my-project/my-repo/my-image:tag
245+
"--format",
246+
"get(image_summary.digest)"
247+
};
248+
}
249+
250+
String response = "";
251+
try {
252+
response = TemplatesStageMojo.runCommandCapturesOutput(command, null);
253+
} catch (Exception e) {
254+
// Swallow exceptions here - usually this means that the image does not exist with the tag
255+
return "";
256+
}
257+
// The response is expected to be just the digest, e.g., "sha256:..."
258+
// Trim any leading/trailing whitespace.
259+
return response.trim();
260+
}
261+
190262
private static class QueryOperationRunnable implements dev.failsafe.function.CheckedRunnable {
191263
String[] command;
192264

plugins/templates-maven-plugin/src/main/java/com/google/cloud/teleport/plugin/maven/TemplatesStageMojo.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353
import java.nio.file.Paths;
5454
import java.nio.file.StandardCopyOption;
5555
import java.time.Duration;
56+
import java.time.LocalDateTime;
57+
import java.time.format.DateTimeFormatter;
5658
import java.util.ArrayList;
5759
import java.util.List;
5860
import java.util.Map;
@@ -550,14 +552,18 @@ protected String stageFlexTemplate(
550552
// resolve tag to apply
551553
ImageSpecMetadata metadata = imageSpec.getMetadata();
552554
String trackTag = "public-image-latest";
555+
String dateSuffix =
556+
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH"));
557+
String deprecatedTag = "no-new-use-public-image-newer-available-" + dateSuffix;
553558
if (metadata.isHidden()) {
554559
trackTag = "no-new-use-public-image-latest";
555560
} else if (metadata.getName().contains("[Deprecated]")) {
556561
trackTag = "deprecated-public-image-latest";
557562
}
558563
// promote image
559564
PromoteHelper promoteHelper =
560-
new PromoteHelper(imagePath, targetImagePath, stagePrefix, trackTag, digest);
565+
new PromoteHelper(
566+
imagePath, targetImagePath, stagePrefix, trackTag, deprecatedTag, digest);
561567
promoteHelper.promote();
562568

563569
if (!stageImageOnly) {

plugins/templates-maven-plugin/src/test/java/com/google/cloud/teleport/plugin/maven/PromoteHelperTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public void testPromoteFlexTemplateImage() {
3333
"us-docker.pkg.dev/target-project/target-repo/some-prefix/io_to_io",
3434
"2020_10_10_rc00",
3535
null,
36+
null,
3637
"sha256:123",
3738
"fake-token");
3839
String[] cmds = helper.getPromoteFlexTemplateImageCmd();

0 commit comments

Comments
 (0)