Skip to content

Commit e0edaca

Browse files
committed
Updated to support docker-compose DFIU update
1 parent 9fb9df1 commit e0edaca

File tree

13 files changed

+125
-79
lines changed

13 files changed

+125
-79
lines changed

dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/CommandLine.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,18 +83,19 @@ static ArgumentParser getArgumentParser() {
8383
.setDefault(false)
8484
.help("Only update image tag store. Skip creating PRs");
8585
parser.addArgument("-x")
86-
.help("comment snippet mentioned in line just before FROM instruction for ignoring a child image. " +
86+
.help("comment snippet mentioned in line just before 'FROM' instruction(Dockerfile)" +
87+
"or 'image' instruction(docker-compose) for ignoring a child image. " +
8788
"Defaults to 'no-dfiu'");
89+
parser.addArgument("-t", "--" + FILE_NAMES_TO_SEARCH)
90+
.type(String.class)
91+
.setDefault("Dockerfile,docker-compose")
92+
.help("Comma seperated list of filenames to search & update for PR creation" +
93+
"(default: Dockerfile,docker-compose)");
8894
parser.addArgument("-r", "--" + RATE_LIMIT_PR_CREATION)
8995
.type(String.class)
9096
.setDefault("")
9197
.required(false)
9298
.help("Use RateLimiting when sending PRs. RateLimiting is enabled only if this value is set it's disabled by default.");
93-
parser.addArgument("-t", "--" + Constants.FILENAMES_TO_SEARCH)
94-
.type(String.class)
95-
.setDefault("Dockerfile,docker-compose")
96-
.help("Comma seperated list of filenames to search & update for PR creation" +
97-
"(default: Dockerfile,docker-compose)");
9899
return parser;
99100
}
100101

dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/model/ImageKeyValuePair.java

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package com.salesforce.dockerfileimageupdate.model;
22

3+
import com.salesforce.dockerfileimageupdate.utils.DockerfileGitHubUtil;
34
import org.apache.commons.lang3.StringUtils;
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
47

58
import java.util.regex.Pattern;
69

710
public class ImageKeyValuePair {
11+
private static final Logger log = LoggerFactory.getLogger(ImageKeyValuePair.class);
812
private static final String IMAGE = "image";
913
private static final String INVALID_IMAGE_VALUE = "It is not a valid value for image key.";
10-
private static final String INVALID_TAG_VALUE = "It is not a valid value for image tag.";
11-
private static final String TAG_VALUE_LATEST = "Tag value is latest. So ignoring it";
1214

1315
/**
1416
* The name of the base image
@@ -22,6 +24,10 @@ public class ImageKeyValuePair {
2224
* Comment starting with #
2325
*/
2426
private final String comments;
27+
/**
28+
* Yaml Spacing #
29+
*/
30+
private final String spaces;
2531

2632
/**
2733
* Accepts an image key value pair line from a docker-compose file
@@ -41,6 +47,13 @@ public ImageKeyValuePair(String imageKeyValuePair) {
4147
} else {
4248
comments = null;
4349
}
50+
// Get Yaml spacing in variable
51+
if (lineWithoutComment.startsWith(" ")) {
52+
spaces = lineWithoutComment.substring(0, lineWithoutComment.indexOf(IMAGE));
53+
} else {
54+
spaces = "";
55+
}
56+
4457
// Remove "image:" from remaining string
4558
String lineWithoutImageKey = lineWithoutComment.trim().
4659
replaceFirst(IMAGE, "").replaceFirst(":", "").
@@ -51,7 +64,6 @@ public ImageKeyValuePair(String imageKeyValuePair) {
5164
baseImageName = imageAndTag[0];
5265
if (imageAndTag.length > 1) {
5366
tag = imageAndTag[1];
54-
validateImageTag(tag);
5567
} else {
5668
tag = null;
5769
}
@@ -67,14 +79,15 @@ public ImageKeyValuePair(String imageKeyValuePair) {
6779
* @param tag tag to add
6880
* @param comments comments to add
6981
*/
70-
private ImageKeyValuePair(String baseImageName, String tag, String comments) {
82+
private ImageKeyValuePair(String baseImageName, String tag, String comments, String spaces) {
7183
this.baseImageName = baseImageName;
7284
this.tag = tag;
7385
this.comments = comments;
86+
this.spaces = spaces;
7487
}
7588

7689
/**
77-
* Check if this {@code lineInFile} is a FROM instruction,
90+
* Check if this {@code lineInFile} is a image instruction,
7891
* it is referencing {@code imageName} as a base image,
7992
* and the tag is not the same as {@code imageTag} (or there is no tag)
8093
* @param lineInFile Line a code file
@@ -84,25 +97,27 @@ private ImageKeyValuePair(String baseImageName, String tag, String comments) {
8497
*/
8598
public static boolean isImageKeyValuePairWithThisImageAndOlderTag(String lineInFile, String imageName, String imageTag) {
8699
if (ImageKeyValuePair.isImageKeyValuePair(lineInFile)) {
87-
ImageKeyValuePair ImageKeyValuePair = new ImageKeyValuePair(lineInFile);
88-
return ImageKeyValuePair.hasBaseImage(imageName) && ImageKeyValuePair.hasADifferentTag(imageTag);
100+
ImageKeyValuePair imageKeyValuePair = new ImageKeyValuePair(lineInFile);
101+
return imageKeyValuePair.hasBaseImage(imageName)
102+
&& imageKeyValuePair.hasADifferentTag(imageTag)
103+
&& DockerfileGitHubUtil.isValidImageTag(imageKeyValuePair.getTag());
89104
}
90105
return false;
91106
}
92107

93108
/**
94109
* Get a new {@code ComposeImageValuePair} the same as this but with the {@code tag} set as {@code newTag}
95110
* @param newTag the new image tag
96-
* @return a new FROM with the new image tag
111+
* @return a new image instruction with the new image tag
97112
*/
98113
public ImageKeyValuePair getImageKeyValuePairWithNewTag(String newTag) {
99-
return new ImageKeyValuePair(baseImageName, newTag, comments);
114+
return new ImageKeyValuePair(baseImageName, newTag, comments, spaces);
100115
}
101116

102117
/**
103-
* Determines whether the line is a FROM instruction line in a Dockerfile
104-
* @param composeImageKeyValueLine a single line(key:value) from a Docker-compose.yaml
105-
* @return the line is a FROM instruction line or not
118+
* Determines whether the line is a image instruction line in a docker-compose.yaml
119+
* @param composeImageKeyValueLine a single line(key:value) from a docker-compose.yaml
120+
* @return the line is a image instruction line or not
106121
*/
107122
public static boolean isImageKeyValuePair(String composeImageKeyValueLine) {
108123
if (StringUtils.isNotBlank(composeImageKeyValueLine)) {
@@ -112,11 +127,11 @@ public static boolean isImageKeyValuePair(String composeImageKeyValueLine) {
112127
}
113128

114129
/**
115-
* @return a String representation of a FROM instruction line in Dockerfile. No new line at the end
130+
* @return a String representation of a image instruction line in docker-compose.yaml file. No new line at the end
116131
*/
117132
@Override
118133
public String toString() {
119-
StringBuilder stringBuilder = new StringBuilder(IMAGE);
134+
StringBuilder stringBuilder = new StringBuilder(spaces + IMAGE);
120135
stringBuilder.append(": ");
121136
stringBuilder.append(baseImageName);
122137
if (hasTag()) {
@@ -179,15 +194,4 @@ public boolean hasComments() {
179194
public String getComments() {
180195
return comments;
181196
}
182-
183-
public static void validateImageTag(String tag) {
184-
String tagVersionRegexStr = "([a-zA-Z0-9_]([-._a-zA-Z0-9])*)";
185-
Pattern validTag = Pattern.compile(tagVersionRegexStr);
186-
if (!validTag.matcher(tag).matches()) {
187-
throw new IllegalArgumentException(INVALID_TAG_VALUE);
188-
}
189-
if (tag.equals("latest")) {
190-
throw new IllegalArgumentException(TAG_VALUE_LATEST);
191-
}
192-
}
193197
}

dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/search/GitHubImageSearchTermList.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ public class GitHubImageSearchTermList {
2020
* @param image the name of the image including registry
2121
* @return list of GitHub code search terms
2222
*/
23-
public static List<String> getSearchTerms(String image) {
23+
public static List<String> getSearchTerms(String image, String filenames) {
2424
if (image == null || image.trim().isEmpty()) {
2525
return ImmutableList.of();
2626
}
2727
String[] imageParts = image.split("/");
28-
ProcessingState state = processDomainPartOfImage(imageParts[0]);
28+
ProcessingState state = processDomainPartOfImage(imageParts[0], filenames);
2929
if (imageParts.length > 1) {
3030
for (int i = 1; i < imageParts.length - 1; i++) {
3131
String imagePart = imageParts[i];
@@ -90,12 +90,14 @@ private static void processFinalUrlSegment(ProcessingState state, String finalIm
9090
* @param domain the domain part of the image (which may or may not be the full registry name)
9191
* @return processing state for the search terms
9292
*/
93-
static ProcessingState processDomainPartOfImage(String domain) {
93+
static ProcessingState processDomainPartOfImage(String domain, String filenames) {
9494
ProcessingState state = new ProcessingState();
9595
if (domain == null || domain.trim().isEmpty()) {
9696
return state;
9797
} else {
98-
state.addToCurrentTerm("FROM ");
98+
if (filenames.equalsIgnoreCase("Dockerfile")) {
99+
state.addToCurrentTerm("FROM ");
100+
}
99101
if (domain.contains("-")) {
100102
processDashedDomainParts(state, domain);
101103
} else {

dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/subcommands/impl/All.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ protected Optional<Exception> processImageWithTag(String image, String tag, Name
8181
PullRequests pullRequests = getPullRequests();
8282
GitHubPullRequestSender pullRequestSender = getPullRequestSender(dockerfileGitHubUtil, ns);
8383
GitForkBranch gitForkBranch = getGitForkBranch(image, tag, ns);
84-
String filenamesToSearch = ns.get(Constants.FILENAMES_TO_SEARCH);
84+
String filenamesToSearch = ns.get(Constants.FILE_NAMES_TO_SEARCH);
8585

8686
log.info("Finding Dockerfiles with the image name {}...", image);
8787
Optional<List<PagedSearchIterable<GHContent>>> contentsWithImage =

dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/subcommands/impl/Parent.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public void execute(final Namespace ns, DockerfileGitHubUtil dockerfileGitHubUti
4444
String store = ns.get(Constants.STORE);
4545
String img = ns.get(Constants.IMG);
4646
String tag = ns.get(Constants.TAG);
47+
String filenamesToSearch = ns.getString(Constants.FILE_NAMES_TO_SEARCH);
48+
4749
log.info("Updating store...");
4850
try {
4951
ImageTagStore imageTagStore = ImageStoreUtil.initializeImageTagStore(this.dockerfileGitHubUtil, store);
@@ -57,7 +59,6 @@ public void execute(final Namespace ns, DockerfileGitHubUtil dockerfileGitHubUti
5759
+ "be skipped.", Constants.SKIP_PR_CREATION);
5860
return;
5961
}
60-
String filenamesToSearch = ns.get(Constants.FILENAMES_TO_SEARCH);
6162

6263
PullRequests pullRequests = getPullRequests();
6364
GitHubPullRequestSender pullRequestSender = getPullRequestSender(dockerfileGitHubUtil, ns);

dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/utils/Constants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ private Constants() {
3939
public static final String GIT_API_SEARCH_LIMIT = "ghapisearchlimit";
4040
public static final String SKIP_PR_CREATION = "skipprcreation";
4141
public static final String IGNORE_IMAGE_STRING = "x";
42-
public static final String FILENAMES_TO_SEARCH = "filenamestosearch";
42+
public static final String FILE_NAMES_TO_SEARCH = "filenamestosearch";
4343
public static final String RATE_LIMIT_PR_CREATION = "rate-limit-pr-creations";
4444
//max number of PRs to be sent (or tokens to be added) per DEFAULT_RATE_LIMIT_DURATION(per hour in this case)
4545
public static final long DEFAULT_RATE_LIMIT = 60;

dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/utils/DockerfileGitHubUtil.java

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
import net.sourceforge.argparse4j.inf.*;
1717
import org.apache.commons.lang3.StringUtils;
1818
import org.kohsuke.github.*;
19+
20+
import java.util.regex.Pattern;
1921
import java.util.stream.Collectors;
2022
import org.slf4j.Logger;
2123
import org.slf4j.LoggerFactory;
@@ -124,8 +126,8 @@ public Optional<List<PagedSearchIterable<GHContent>>> findFilesWithImage(
124126
if (image.substring(image.lastIndexOf(' ') + 1).length() <= 1) {
125127
throw new IOException("Invalid image name.");
126128
}
127-
List<String> terms = GitHubImageSearchTermList.getSearchTerms(image);
128-
log.info("Searching for {} with terms: {}", image, terms);
129+
List<String> terms = GitHubImageSearchTermList.getSearchTerms(image, filenamesToSearch);
130+
log.info("Searching for {}, in files: {}, with terms: {}", image, filenamesToSearch, terms);
129131
terms.forEach(search::q);
130132
PagedSearchIterable<GHContent> files = search.list();
131133
int totalCount = files.getTotalCount();
@@ -292,7 +294,7 @@ protected void findImagesAndFix(GHContent content, String branch, String img,
292294
boolean modified = rewriteDockerfile(img, tag, reader, strB, ignoreImageString);
293295
if (modified) {
294296
content.update(strB.toString(),
295-
"Fix Dockerfile base image in /" + content.getPath() + "\n\n" + customMessage, branch);
297+
"Fix Docker base image in /" + content.getPath() + "\n\n" + customMessage, branch);
296298
}
297299
}
298300

@@ -317,7 +319,7 @@ protected boolean rewriteDockerfile(String img, String tag,
317319
}
318320

319321
/**
320-
* This method will read a line and see if the line contains a FROM instruction with the specified
322+
* This method will read a line and see if the line contains a FROM instruction(Dockerfile) or imageKeyValuePair instruction(docker-compose) with the specified
321323
* {@code imageToFind}. If the image does not have the given {@code tag} then {@code stringBuilder}
322324
* will get a modified version of the line with the new {@code tag}. We return {@code true} in this
323325
* instance.
@@ -337,7 +339,7 @@ protected boolean changeIfDockerfileBaseImageLine(String imageToFind, String tag
337339
boolean modified = false;
338340
String outputLine = line;
339341

340-
// Only check/modify lines which contain a FROM instruction
342+
// Only check/modify lines which contain a FROM instruction or imageKeyValuePair instruction
341343
if (FromInstruction.isFromInstruction(line)) {
342344
FromInstruction fromInstruction = new FromInstruction(line);
343345

@@ -350,7 +352,8 @@ protected boolean changeIfDockerfileBaseImageLine(String imageToFind, String tag
350352
} else if (ImageKeyValuePair.isImageKeyValuePair(line)) {
351353
ImageKeyValuePair imageKeyValuePair = new ImageKeyValuePair(line);
352354
if (imageKeyValuePair.hasBaseImage(imageToFind) &&
353-
imageKeyValuePair.hasADifferentTag(tag)) {
355+
imageKeyValuePair.hasADifferentTag(tag) &&
356+
DockerfileGitHubUtil.isValidImageTag(imageKeyValuePair.getTag())) {
354357
imageKeyValuePair = imageKeyValuePair.getImageKeyValuePairWithNewTag(tag);
355358
modified = true;
356359
}
@@ -361,7 +364,7 @@ protected boolean changeIfDockerfileBaseImageLine(String imageToFind, String tag
361364
}
362365

363366
/**
364-
* Determines whether a comment before FROM line has {@code ignoreImageString} to ignore creating dfiu PR
367+
* Determines whether a comment before FROM line or imageKeyValuePair line has {@code ignoreImageString} to ignore creating dfiu PR
365368
* If {@code ignoreImageString} present in comment, PR should be ignored
366369
* If {@code ignoreImageString} is empty, then by default 'no-dfiu' comment will be searched
367370
* @param line line to search for comment
@@ -386,7 +389,7 @@ public void createPullReq(GHRepository origRepo,
386389
while (true) {
387390
// TODO: accept rateLimiter Optional with option to get no-op rateLimiter
388391
// where it's not required.
389-
if(rateLimiter != null) {
392+
if (rateLimiter != null) {
390393
log.info("Trying to consume a token before creating pull request..");
391394
// Consume a token from the token bucket.
392395
// If a token is not available this method will block until
@@ -566,4 +569,20 @@ public void changeDockerfiles(Namespace ns,
566569
rateLimiter);
567570
}
568571
}
572+
573+
public static boolean isValidImageTag(String tag) {
574+
String tagVersionRegexStr = "([a-zA-Z0-9_]([-._a-zA-Z0-9])*)";
575+
Pattern validTag = Pattern.compile(tagVersionRegexStr);
576+
if (StringUtils.isNotBlank(tag)) {
577+
if (!validTag.matcher(tag).matches()) {
578+
log.warn("{} is not a valid value for image tag. So will be ignored!", tag);
579+
return false;
580+
} else if (tag.equals("latest")) {
581+
log.warn("Tag value is latest. So will be ignored");
582+
return false;
583+
}
584+
return true;
585+
}
586+
return false;
587+
}
569588
}

dockerfile-image-update/src/test/java/com/salesforce/dockerfileimageupdate/model/ImageKeyValuePairTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ public class ImageKeyValuePairTest {
1111
public Object[][] inputImageKeyValuePairData() {
1212
return new Object[][]{
1313
{"image: dockerimage:3 # some comment", "image: dockerimage:3 # some comment"},
14-
//{"image: dockerimage:3 AS test", "image: dockerimage:3 AS test"},
15-
//{"image: dockerimage:3 \tAS \ttest # some comment", "image: dockerimage:3 \tAS \ttest # some comment"},
14+
{" image: dockerimage:3", " image: dockerimage:3"},
15+
{"\timage: dockerimage:3", "image: dockerimage:3"},
1616
{"image: dockerimage:3# some comment", "image: dockerimage:3 # some comment"},
17-
{" image: dockerimage ", "image: dockerimage"},
17+
{" image: dockerimage ", " image: dockerimage"},
1818
{"\t image: \t dockerimage:4 \t #comment", "image: dockerimage:4 #comment"},
1919
{"image: dockerimage:4:4:4 #comment", "image: dockerimage:4 #comment"},
2020
{"image: dockerimage:4 #comment me # # ", "image: dockerimage:4 #comment me # # "}
@@ -206,7 +206,7 @@ public void testInvalidFromData(String input) {
206206
@DataProvider
207207
public Object[][] isImageKeyValuePairWithThisImageAndOlderTagData() {
208208
return new Object[][]{
209-
{"image: someimage", "someimage", "sometag", true},
209+
{"image: someimage", "someimage", "sometag", false},
210210
{"image: someimage:sometag", "someimage", "sometag", false},
211211
{"not a valid image key-value pair", "someimage", "sometag", false},
212212
{"image: someimage:oldtag", "someimage", "sometag", true}

dockerfile-image-update/src/test/java/com/salesforce/dockerfileimageupdate/search/GitHubImageSearchTermListTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public Object[][] imageAndParts() {
3434

3535
@Test(dataProvider = "imageAndParts")
3636
public void testGetSearchTermsContent(String image, List<String> expectedResult) {
37-
List<String> searchTerms = GitHubImageSearchTermList.getSearchTerms(image);
37+
List<String> searchTerms = GitHubImageSearchTermList.getSearchTerms(image, "Dockerfile");
3838
assertEquals(joinListByComma(searchTerms), joinListByComma(expectedResult));
3939
}
4040

@@ -44,7 +44,7 @@ private String joinListByComma(List<String> list) {
4444

4545
@Test(dataProvider = "imageAndParts")
4646
public void testGetSearchTermsSize(String image, List<String> expectedResult) {
47-
List<String> searchTerms = GitHubImageSearchTermList.getSearchTerms(image);
47+
List<String> searchTerms = GitHubImageSearchTermList.getSearchTerms(image, "Dockerfile");
4848
assertEquals(searchTerms, expectedResult);
4949
}
5050

@@ -63,7 +63,7 @@ public Object[][] domainCurrentTermAndProcessedTerms() {
6363

6464
@Test(dataProvider = "domainCurrentTermAndProcessedTerms")
6565
public void testProcessDomainPartOfImage(String domain, String expectedCurrentTerm, List<String> expectedProcessedTerms) {
66-
GitHubImageSearchTermList.ProcessingState state = GitHubImageSearchTermList.processDomainPartOfImage(domain);
66+
GitHubImageSearchTermList.ProcessingState state = GitHubImageSearchTermList.processDomainPartOfImage(domain, "Dockerfile");
6767
assertEquals(joinListByComma(state.terms), joinListByComma(expectedProcessedTerms));
6868
assertEquals(state.getCurrentTerm(), expectedCurrentTerm);
6969
}

0 commit comments

Comments
 (0)