Skip to content

Commit 01eb53f

Browse files
committed
Support for alias in registries.conf
Signed-off-by: Valentin Delaye <jonesbusy@users.noreply.github.com>
1 parent b774e08 commit 01eb53f

5 files changed

Lines changed: 125 additions & 4 deletions

File tree

src/main/java/land/oras/ContainerRef.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.net.URLEncoder;
2424
import java.nio.charset.StandardCharsets;
2525
import java.nio.file.Path;
26+
import java.util.List;
2627
import java.util.Objects;
2728
import java.util.regex.Matcher;
2829
import java.util.regex.Pattern;
@@ -391,13 +392,16 @@ public static ContainerRef parse(String name) {
391392
*/
392393
public String getEffectiveRegistry(Registry target) {
393394
if (isUnqualified()) {
395+
String key = getAliasKey();
396+
if (target.getRegistry() == null && target.getRegistriesConf().hasAlias(key)) {
397+
return target.getRegistriesConf().getAliases().get(key);
398+
}
394399
return target.getRegistry() != null
395400
? target.getRegistry()
396401
: determineFirstUnqualifiedSearchRegistry(target);
397402
}
398403
return registry;
399404
}
400-
401405
/**
402406
* Return a copy of reference for a registry other registry
403407
* @param registry The registry
@@ -414,6 +418,13 @@ public ContainerRef forRegistry(String registry) {
414418
*/
415419
public ContainerRef forRegistry(Registry registry) {
416420
if (isUnqualified() && registry.getRegistry() == null) {
421+
String key = getAliasKey();
422+
if (registry.getRegistry() == null && registry.getRegistriesConf().hasAlias(key)) {
423+
String newLocation = registry.getRegistriesConf().getAliases().get(key);
424+
String newRefString = "%s:%s".formatted(newLocation, tag);
425+
LOG.info("Using {} as an alias to {}", key, newRefString);
426+
return ContainerRef.parse(newRefString);
427+
}
417428
LOG.info(
418429
"The container reference {} was created without a registry. Will try to resolve using unqualified-search-registries in order",
419430
this);
@@ -429,6 +440,15 @@ public ContainerRef forRegistry(Registry registry) {
429440
digest);
430441
}
431442

443+
/**
444+
* Return the key of the alias
445+
*/
446+
private String getAliasKey() {
447+
return getRegistry().equals(Const.DEFAULT_REGISTRY) && "library".equals(getNamespace())
448+
? getRepository()
449+
: getFullRepository();
450+
}
451+
432452
private String determineFirstUnqualifiedSearchRegistry(Registry registry) {
433453
// No settings, keep old behavior of defaulting to docker.io for unqualified reference
434454
if (registry.getRegistriesConf().getUnqualifiedRegistries().isEmpty()) {
@@ -437,13 +457,15 @@ private String determineFirstUnqualifiedSearchRegistry(Registry registry) {
437457
LOG.debug(
438458
"Found registries in unqualified-search-registries: {}",
439459
registry.getRegistriesConf().getUnqualifiedRegistries());
440-
for (String searchRegistry : registry.getRegistriesConf().getUnqualifiedRegistries()) {
460+
List<String> unqualifiedRegistries = registry.getRegistriesConf().getUnqualifiedRegistries();
461+
for (String searchRegistry : unqualifiedRegistries) {
441462
Registry targetRegistry = registry.copy(registry, searchRegistry);
442463
LOG.debug("Checking if container {} exists in unqualified search registry {}", this, searchRegistry);
443464
if (targetRegistry.exists(this)) {
444465
LOG.debug("Found container {} in unqualified search registry {}", this, searchRegistry);
445466
return searchRegistry;
446467
}
468+
LOG.debug("Container {} does not exist in unqualified search registry {}", this, searchRegistry);
447469
}
448470
throw new OrasException(
449471
"Container reference %s is unqualified and cannot be found in any of the unqualified search registries: %s"

src/main/java/land/oras/auth/RegistriesConf.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424
import java.nio.file.Files;
2525
import java.nio.file.Path;
2626
import java.util.Collections;
27+
import java.util.HashMap;
2728
import java.util.LinkedList;
2829
import java.util.List;
30+
import java.util.Map;
2931
import java.util.Objects;
3032
import land.oras.exception.OrasException;
3133
import land.oras.utils.TomlUtils;
@@ -94,7 +96,9 @@ public static RegistriesConf newConf() {
9496
* The model of the config file
9597
*
9698
*/
97-
record ConfigFile(@JsonProperty("unqualified-search-registries") @Nullable List<String> unqualifiedRegistries) {}
99+
record ConfigFile(
100+
@JsonProperty("aliases") @Nullable Map<String, String> aliases,
101+
@JsonProperty("unqualified-search-registries") @Nullable List<String> unqualifiedRegistries) {}
98102

99103
/**
100104
* Get the list of unqualified registries.
@@ -104,6 +108,23 @@ public List<String> getUnqualifiedRegistries() {
104108
return Collections.unmodifiableList(config.unqualifiedRegistries);
105109
}
106110

111+
/**
112+
* Return the aliases
113+
* @return an unmodifiable map of aliases, where the key is the alias and the value is the actual registry URL.
114+
*/
115+
public Map<String, String> getAliases() {
116+
return Collections.unmodifiableMap(config.aliases);
117+
}
118+
119+
/**
120+
* Check if the given alias exists in the configuration.
121+
* @param alias the alias to check for existence.
122+
* @return true if the alias exists, false otherwise.
123+
*/
124+
public boolean hasAlias(String alias) {
125+
return config.aliases.containsKey(alias);
126+
}
127+
107128
/**
108129
* Nested Config class for configuration management.
109130
*/
@@ -119,6 +140,11 @@ private Config() {}
119140
*/
120141
private final List<String> unqualifiedRegistries = new LinkedList<>();
121142

143+
/**
144+
* Map of registry aliases, where the key is the alias and the value is the actual registry URL.
145+
*/
146+
private final Map<String, String> aliases = new HashMap<>();
147+
122148
/**
123149
* Loads the configuration from a TOML file at the specified path and populates registries configuration
124150
*
@@ -129,9 +155,13 @@ private Config() {}
129155
public static Config load(ConfigFile configFile) throws OrasException {
130156
Config config = new Config();
131157
if (configFile.unqualifiedRegistries != null) {
132-
LOG.debug("Loading unqualified registries: {}", configFile.unqualifiedRegistries);
158+
LOG.trace("Loading unqualified registries: {}", configFile.unqualifiedRegistries);
133159
config.unqualifiedRegistries.addAll(configFile.unqualifiedRegistries);
134160
}
161+
if (configFile.aliases != null) {
162+
LOG.trace("Loading registry aliases: {}", configFile.aliases);
163+
config.aliases.putAll(configFile.aliases);
164+
}
135165
return config;
136166
}
137167
}

src/test/java/land/oras/ContainerRefTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,30 @@ void shouldThrowIfUnableToFindOnAnyUnQualifiedSearchRegistry() throws Exception
7070
});
7171
}
7272

73+
@Test
74+
void shouldDetermineFromAlias() throws Exception {
75+
76+
// language=toml
77+
String config =
78+
"""
79+
[aliases]
80+
"my-library/my-namespace"="localhost/test"
81+
"my-library"="localhost/test2"
82+
""";
83+
84+
Files.writeString(homeDir.resolve(".config").resolve("containers").resolve("registries.conf"), config);
85+
86+
new EnvironmentVariables()
87+
.set("HOME", homeDir.toAbsolutePath().toString())
88+
.execute(() -> {
89+
Registry registry = Registry.builder().defaults().build();
90+
ContainerRef unqualifiedRef = ContainerRef.parse("my-library/my-namespace");
91+
assertEquals("localhost/test", unqualifiedRef.getEffectiveRegistry(registry));
92+
ContainerRef unqualifiedRef2 = ContainerRef.parse("my-library");
93+
assertEquals("localhost/test2", unqualifiedRef2.getEffectiveRegistry(registry));
94+
});
95+
}
96+
7397
@Test
7498
void shouldDetermineEffectiveRegistry() throws Exception {
7599

src/test/java/land/oras/GitHubContainerRegistryITCase.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,25 @@
2222

2323
import static org.junit.jupiter.api.Assertions.assertNotNull;
2424

25+
import java.nio.file.Files;
2526
import java.nio.file.Path;
2627
import land.oras.utils.ArchiveUtils;
2728
import land.oras.utils.Const;
2829
import org.junit.jupiter.api.Test;
2930
import org.junit.jupiter.api.io.TempDir;
3031
import org.junit.jupiter.api.parallel.Execution;
3132
import org.junit.jupiter.api.parallel.ExecutionMode;
33+
import uk.org.webcompere.systemstubs.environment.EnvironmentVariables;
3234

3335
@Execution(ExecutionMode.CONCURRENT)
3436
class GitHubContainerRegistryITCase {
3537

3638
@TempDir
3739
Path tempDir;
3840

41+
@TempDir
42+
private static Path homeDir;
43+
3944
@Test
4045
void shouldPullIndex() {
4146
Registry registry = Registry.builder().build();
@@ -44,6 +49,30 @@ void shouldPullIndex() {
4449
assertNotNull(index);
4550
}
4651

52+
@Test
53+
void shouldPullIndexWithAlias() throws Exception {
54+
// language=toml
55+
String config =
56+
"""
57+
[aliases]
58+
"oras"="ghcr.io/oras-project/oras"
59+
""";
60+
61+
// Setup
62+
Files.createDirectory(homeDir.resolve(".config"));
63+
Files.createDirectory(homeDir.resolve(".config").resolve("containers"));
64+
Files.writeString(homeDir.resolve(".config").resolve("containers").resolve("registries.conf"), config);
65+
66+
new EnvironmentVariables()
67+
.set("HOME", homeDir.toAbsolutePath().toString())
68+
.execute(() -> {
69+
Registry registry = Registry.builder().defaults().build();
70+
ContainerRef containerRef1 = ContainerRef.parse("oras:main");
71+
Index index = registry.getIndex(containerRef1);
72+
assertNotNull(index);
73+
});
74+
}
75+
4776
@Test
4877
void shouldPUllManifest() {
4978
Registry registry = Registry.builder().build();

src/test/java/land/oras/auth/RegistriesConfTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ class RegistriesConfTest {
4444
public static final String HOME_REGISTRIES_CONF =
4545
"""
4646
unqualified-search-registries = ["docker.io"]
47+
48+
[aliases]
49+
"alpine"="docker.io/library/alpine"
4750
""";
4851

4952
@BeforeAll
@@ -56,6 +59,19 @@ static void init() throws Exception {
5659
homeDir.resolve(".config").resolve("containers").resolve("registries.conf"), HOME_REGISTRIES_CONF);
5760
}
5861

62+
@Test
63+
void shouldReadAlias() throws Exception {
64+
new EnvironmentVariables()
65+
.set("HOME", homeDir.toAbsolutePath().toString())
66+
.execute(() -> {
67+
RegistriesConf conf = RegistriesConf.newConf();
68+
assertNotNull(conf);
69+
assertEquals(1, conf.getAliases().size());
70+
assertEquals("docker.io/library/alpine", conf.getAliases().get("alpine"));
71+
assertTrue(conf.hasAlias("alpine"));
72+
});
73+
}
74+
5975
@Test
6076
void shouldReadUnqualifiedSearchRegistriesFromHome() throws Exception {
6177
new EnvironmentVariables()

0 commit comments

Comments
 (0)