Skip to content

Commit 43565b5

Browse files
octavia-squidington-iiiogusakjdpgrailsdev
committed
fix: parsing docker image when the full image name contains registry port (#13990)
Co-authored-by: Oleg Gusak <[email protected]> Co-authored-by: Jonathan Pearlin <[email protected]>
1 parent d1c36c4 commit 43565b5

File tree

5 files changed

+52
-20
lines changed

5 files changed

+52
-20
lines changed

airbyte-commons-with-dependencies/src/main/java/io/airbyte/commons/helper/DockerImageNameHelper.java

+13-5
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ public class DockerImageNameHelper {
2626
/**
2727
* Docker image names are by convention separated by slashes. The last portion is the image's name.
2828
* This is followed by a colon and a version number. e.g. airbyte/scheduler:v1 or
29-
* gcr.io/my-project/my-project:v2.
29+
* gcr.io/my-project/image-name:v2. Registry name may also include port number, e.g.
30+
* registry.internal:1234/my-project/image-name:v2
3031
*
3132
* @param fullImagePath the image name with repository and version ex
3233
* gcr.io/my-project/image-name:v2
@@ -43,7 +44,7 @@ public static String extractShortImageName(final String fullImagePath) {
4344
* Extracts the image name without the version tag.
4445
*
4546
* @param fullImagePath the docker image name
46-
* @return anything before ":"
47+
* @return anything before last ":"
4748
*/
4849
public static String extractImageNameWithoutVersion(final String fullImagePath) {
4950
return extractPartFromFullPath(fullImagePath, NAME_PARTS_INDEX);
@@ -53,7 +54,7 @@ public static String extractImageNameWithoutVersion(final String fullImagePath)
5354
* Extracts the image version label as a string.
5455
*
5556
* @param fullImagePath the docker image name
56-
* @return anything after ":"
57+
* @return anything after last ":"
5758
*/
5859
public static String extractImageVersionString(final String fullImagePath) {
5960
return extractPartFromFullPath(fullImagePath, VERSION_PART_INDEX);
@@ -78,8 +79,15 @@ public static Optional<Version> extractImageVersion(final String fullImagePath)
7879
}
7980

8081
private static String extractPartFromFullPath(final String fullImagePath, final int partIndex) {
81-
final String[] parts = fullImagePath.split(VERSION_DELIMITER);
82-
return parts.length > partIndex ? parts[partIndex] : null;
82+
final int delimeterIndex = fullImagePath.lastIndexOf(VERSION_DELIMITER);
83+
if (partIndex == NAME_PARTS_INDEX) {
84+
return delimeterIndex >= 0 ? fullImagePath.substring(0, delimeterIndex) : fullImagePath;
85+
} else if (partIndex == VERSION_PART_INDEX) {
86+
return delimeterIndex >= 0 ? fullImagePath.substring(delimeterIndex + 1) : null;
87+
} else {
88+
LOGGER.warn("Invalid part index: {}", partIndex);
89+
return null;
90+
}
8391
}
8492

8593
}

airbyte-commons-with-dependencies/src/test/java/io/airbyte/commons/helper/DockerImageNameHelperTest.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,22 @@
2020
class DockerImageNameHelperTest {
2121

2222
@ParameterizedTest
23-
@CsvSource({"hello:1,hello", "hello/world:2,world", "foo/bar/fizz/buzz:3,buzz", "hello,hello"})
23+
@CsvSource({"hello:1,hello", "hello/world:2,world", "foo/bar/fizz/buzz:3,buzz", "hello,hello", "registry.internal:1234/foo/bar:1,bar"})
2424
void testExtractShortImageName(final String fullName, final String expected) {
2525
final var actual = DockerImageNameHelper.extractShortImageName(fullName);
2626
Assertions.assertEquals(expected, actual);
2727
}
2828

2929
@ParameterizedTest
30-
@CsvSource({"hello:1,hello", "hello/world:2,hello/world", "foo/bar/fizz/buzz:3,foo/bar/fizz/buzz", "hello,hello", "hello:1.1-foo,hello"})
30+
@CsvSource({"hello:1,hello", "hello/world:2,hello/world", "foo/bar/fizz/buzz:3,foo/bar/fizz/buzz", "hello,hello", "hello:1.1-foo,hello",
31+
"registry.internal:1234/foo/bar:1,registry.internal:1234/foo/bar"})
3132
void testExtractImageNameWithoutVersion(final String fullName, final String expected) {
3233
final var actual = DockerImageNameHelper.extractImageNameWithoutVersion(fullName);
3334
Assertions.assertEquals(expected, actual);
3435
}
3536

3637
@ParameterizedTest
37-
@CsvSource({"hello:1,1", "hello/world:2,2", "foo/bar/fizz/buzz:3,3", "hello,", "hello:1.1-foo,1.1-foo"})
38+
@CsvSource({"hello:1,1", "hello/world:2,2", "foo/bar/fizz/buzz:3,3", "hello,", "hello:1.1-foo,1.1-foo", "registry.internal:1234/foo/bar:1,1"})
3839
void testExtractImageVersionString(final String fullName, final String expected) {
3940
final var actual = DockerImageNameHelper.extractImageVersionString(fullName);
4041
Assertions.assertEquals(expected, actual);
@@ -49,7 +50,8 @@ public Stream<? extends Arguments> provideArguments(final ExtensionContext conte
4950
Arguments.of("hello:1", null),
5051
Arguments.of("hello:dev", new Version("dev")),
5152
Arguments.of("hello", null),
52-
Arguments.of("hello/foo/bar:1.2.3", new Version("1.2.3")));
53+
Arguments.of("hello/foo/bar:1.2.3", new Version("1.2.3")),
54+
Arguments.of("registry.internal:1234/foo/bar:1.0.0", new Version("1.0.0")));
5355
}
5456

5557
}

airbyte-commons-worker/src/main/java/io/airbyte/workers/helper/ConnectorApmSupportHelper.java

+14-6
Original file line numberDiff line numberDiff line change
@@ -67,31 +67,39 @@ Map<String, String> getEnv() {
6767

6868
/**
6969
* Extracts the image name from the provided image string, if the image string uses the following
70-
* format: {@code <image name>:<image version>}.
70+
* format: {@code <image name>:<image version>}. Image name may include registry and port number,
71+
* which is also delimited by ":"
7172
*
7273
* @param image The image.
7374
* @return The name extracted from the image, or the originally provided string if blank.
7475
*/
7576
public static String getImageName(final String image) {
7677
if (StringUtils.isNotEmpty(image)) {
77-
return image.split(IMAGE_DELIMITER)[0];
78+
final int delimeterIndex = image.lastIndexOf(IMAGE_DELIMITER);
79+
if (delimeterIndex >= 0) {
80+
return image.substring(0, delimeterIndex);
81+
}
7882
}
79-
83+
// If image is null, empty, or does not contain a delimiter, return the original string.
8084
return image;
8185
}
8286

8387
/**
8488
* Extracts the image version from the provided image string, if the image string uses the following
85-
* format: {@code <image name>:<image version>}.
89+
* format: {@code <image name>:<image version>}. Image name may include registry and port number,
90+
* which is also delimited by ":"
8691
*
8792
* @param image The image.
8893
* @return The version extracted from the image, or the originally provided string if blank.
8994
*/
9095
public static String getImageVersion(final String image) {
9196
if (StringUtils.isNotEmpty(image)) {
92-
return image.split(IMAGE_DELIMITER)[1];
97+
final int delimeterIndex = image.lastIndexOf(IMAGE_DELIMITER);
98+
if (delimeterIndex >= 0 && image.length() > delimeterIndex + 1) {
99+
return image.substring(delimeterIndex + 1);
100+
}
93101
}
94-
102+
// If image is null, empty, or does not contain a delimiter, return the original string.
95103
return image;
96104
}
97105

airbyte-commons-worker/src/main/java/io/airbyte/workers/pod/PodUtils.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ static String createProcessName(final String fullImagePath, final String jobType
6161
* Extract the shortname of the image from the full path.
6262
*/
6363
static String getShortImageName(final String fullImagePath) {
64-
if (fullImagePath == null || fullImagePath.length() < 1) {
64+
if (fullImagePath == null || fullImagePath.isEmpty()) {
6565
return null;
6666
}
6767
return DockerImageNameHelper.extractShortImageName(fullImagePath);
@@ -70,15 +70,17 @@ static String getShortImageName(final String fullImagePath) {
7070
/**
7171
* Docker image names are by convention separated by slashes. The last portion is the image's name.
7272
* This is followed by a colon and a version number. e.g. airbyte/scheduler:v1 or
73-
* gcr.io/my-project/image-name:v2.
74-
* <p>
73+
* gcr.io/my-project/image-name:v2. Registry name may also include port number, e.g.
74+
* registry.internal:1234/my-project/image-name:v2
75+
* <p />
7576
* Get the image version by returning the substring following a colon.
7677
*/
7778
static String getImageVersion(final String fullImagePath) {
78-
if (fullImagePath == null || fullImagePath.length() < 1) {
79+
if (fullImagePath == null || fullImagePath.isEmpty()) {
7980
return null;
8081
}
81-
int colonIndex = fullImagePath.indexOf(":");
82+
// Use the last colon to find the image tag
83+
int colonIndex = fullImagePath.lastIndexOf(":");
8284
if (colonIndex != -1 && fullImagePath.length() > colonIndex + 1) {
8385
return fullImagePath.substring(colonIndex + 1);
8486
}

airbyte-commons-worker/src/test/java/io/airbyte/workers/helper/ConnectorApmSupportHelperTest.java

+12
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ class ConnectorApmSupportHelperTest {
2323
private static final String CONNECTOR_NAME = "postgres";
2424
private static final String CONNECTOR_VERSION = "2.0.5";
2525
private static final String IMAGE = "postgres:2.0.5";
26+
private static final String REGISTRY_NAME = "registry.internal:1234";
27+
private static final String REPOSITORY_ORG = "airbyte";
28+
private static final String IMAGE_WITH_PORT = String.join("/", REGISTRY_NAME, REPOSITORY_ORG, IMAGE);
2629
private ConnectorApmSupportHelper supportHelper;
2730

2831
@BeforeEach
@@ -39,6 +42,15 @@ void testExtractAirbyteVersionFromImageName() {
3942
assertEquals(CONNECTOR_VERSION, imageVersion);
4043
}
4144

45+
@Test
46+
void testExtractAirbyteVersionFromImageNameWithRegistryPort() {
47+
final String imageName = ConnectorApmSupportHelper.getImageName(IMAGE_WITH_PORT);
48+
final String imageVersion = ConnectorApmSupportHelper.getImageVersion(IMAGE_WITH_PORT);
49+
50+
assertEquals(String.join("/", REGISTRY_NAME, REPOSITORY_ORG, CONNECTOR_NAME), imageName);
51+
assertEquals(CONNECTOR_VERSION, imageVersion);
52+
}
53+
4254
@Test
4355
void testExtractAirbyteVersionFromBlankImageName() {
4456
final String blankString = "";

0 commit comments

Comments
 (0)