Skip to content

Commit 174e350

Browse files
add CI for podman
1 parent e152425 commit 174e350

File tree

11 files changed

+125
-13
lines changed

11 files changed

+125
-13
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: CI-Podman-Rootless
2+
3+
on:
4+
pull_request: {}
5+
push: { branches: [ main ] }
6+
7+
permissions:
8+
contents: read
9+
10+
jobs:
11+
test:
12+
runs-on: ubuntu-22.04
13+
steps:
14+
- uses: actions/checkout@v3
15+
- name: Uninstall unwanted packages
16+
run: sudo apt-get -q -y --purge remove podman moby-engine moby-buildx && sudo rm -rf /var/run/docker.sock
17+
- name: Set XDG_RUNTIME_DIR
18+
run: echo "XDG_RUNTIME_DIR=/run/user/$UID" >> $GITHUB_ENV
19+
- name: Create registries.conf
20+
# allow pulling images without a registry specified and allow pulling from insecure local registry
21+
run: |
22+
mkdir -p $HOME/.config/containers
23+
echo 'unqualified-search-registries = ["docker.io"]' > $HOME/.config/containers/registries.conf
24+
echo '' >> $HOME/.config/containers/registries.conf
25+
echo '[[registry]]' >> $HOME/.config/containers/registries.conf
26+
echo 'location = "localhost:50001"' >> $HOME/.config/containers/registries.conf
27+
echo 'insecure = true' >> $HOME/.config/containers/registries.conf
28+
- name: Istall latest podman release
29+
# see https://podman.io/getting-started/installation#ubuntu
30+
run: |
31+
sudo mkdir -p /etc/apt/keyrings
32+
curl -fsSL https://download.opensuse.org/repositories/devel:kubic:libcontainers:unstable/xUbuntu_$(lsb_release -rs)/Release.key \
33+
| gpg --dearmor \
34+
| sudo tee /etc/apt/keyrings/devel_kubic_libcontainers_unstable.gpg > /dev/null
35+
echo \
36+
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/devel_kubic_libcontainers_unstable.gpg]\
37+
https://download.opensuse.org/repositories/devel:kubic:libcontainers:unstable/xUbuntu_$(lsb_release -rs)/ /" \
38+
| sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list > /dev/null
39+
sudo apt-get update -qq
40+
sudo apt-get -qq -y install podman
41+
- name: Print podman environment information
42+
run: podman info
43+
- name: Enable podman socket
44+
run: systemctl --user enable --now podman.socket
45+
- name: Build with Gradle
46+
run: ./gradlew --no-daemon --scan -Dtest.profile=podman testcontainers:test
47+
- uses: actions/upload-artifact@v3
48+
if: failure()
49+
with:
50+
name: test report
51+
path: ~/work/testcontainers-java/testcontainers-java/core/build/reports/tests/**

build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ subprojects {
9797
failOnPassedAfterRetry = false
9898
}
9999
}
100+
101+
// podman does not support compose
102+
if (System.properties['test.profile'] == 'podman') {
103+
exclude '**/*DockerCompose*'
104+
}
100105
}
101106

102107
tasks.withType(Test).all {

core/src/main/java/org/testcontainers/DockerClientFactory.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.apache.commons.lang3.SystemUtils;
2222
import org.testcontainers.dockerclient.DockerClientProviderStrategy;
2323
import org.testcontainers.dockerclient.DockerMachineClientProviderStrategy;
24+
import org.testcontainers.dockerclient.RootlessPodmanClientProviderStrategy;
2425
import org.testcontainers.dockerclient.TransportConfig;
2526
import org.testcontainers.images.RemoteDockerImage;
2627
import org.testcontainers.images.TimeLimitedLoggedPullImageResultCallback;
@@ -381,4 +382,9 @@ public boolean isUsing(Class<? extends DockerClientProviderStrategy> providerStr
381382
public Info getInfo() {
382383
return getOrInitializeStrategy().getInfo();
383384
}
385+
386+
public boolean supportsCompose() {
387+
// podman does not support compose
388+
return !(getOrInitializeStrategy() instanceof RootlessPodmanClientProviderStrategy);
389+
}
384390
}

core/src/main/java/org/testcontainers/utility/TestEnvironment.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ public static boolean dockerApiAtLeast(String minimumVersion) {
1717
return current.compareTo(min) >= 0;
1818
}
1919

20+
public static boolean clientSupportsCompose() {
21+
return DockerClientFactory.instance().supportsCompose();
22+
}
23+
2024
public static boolean dockerExecutionDriverSupportsExec() {
2125
String executionDriver = DockerClientFactory.instance().getActiveExecutionDriver();
2226

core/src/test/java/org/testcontainers/DockerRegistryContainer.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public DockerRegistryContainer(@NonNull Future<String> image) {
3737
@Override
3838
protected void configure() {
3939
super.configure();
40-
withEnv("REGISTRY_HTTP_ADDR", "127.0.0.1:0");
40+
withEnv("REGISTRY_HTTP_ADDR", "127.0.0.1:50001");
4141
withCreateContainerCmdModifier(cmd -> {
4242
cmd.getHostConfig().withNetworkMode("host");
4343
});
@@ -77,7 +77,7 @@ protected void containerIsStarting(InspectContainerResponse containerInfo) {
7777
);
7878
}
7979

80-
endpoint = getHost() + ":" + port.get();
80+
endpoint = "http://" + getHost() + ":" + port.get();
8181
}
8282

8383
public DockerImageName createImage() {
@@ -96,7 +96,7 @@ public DockerImageName createImage(String originalImage, String tag) {
9696
String dummyImageId = client.inspectImageCmd(originalImage).exec().getId();
9797

9898
DockerImageName imageName = DockerImageName
99-
.parse(getEndpoint() + "/" + Base58.randomString(6).toLowerCase())
99+
.parse(getEndpoint().replaceFirst("http://", "") + "/" + Base58.randomString(6).toLowerCase())
100100
.withTag(tag);
101101

102102
// push the image to the registry

core/src/test/java/org/testcontainers/containers/GenericContainerTest.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.Arrays;
2626
import java.util.List;
2727
import java.util.Map;
28+
import java.util.Objects;
2829
import java.util.concurrent.TimeUnit;
2930
import java.util.function.Predicate;
3031
import java.util.stream.Collectors;
@@ -40,6 +41,8 @@ public void shouldReportOOMAfterWait() {
4041
Info info = DockerClientFactory.instance().client().infoCmd().exec();
4142
// Poor man's rootless Docker detection :D
4243
Assumptions.assumeThat(info.getSecurityOptions()).doesNotContain("rootless");
44+
// setting swappiness is not allowed for cgroups v2
45+
Assumptions.assumeThat(info.getRawValues().get("CgroupVersion")).isNotEqualTo("2");
4346
try (
4447
GenericContainer<?> container = new GenericContainer<>(TestImages.TINY_IMAGE)
4548
.withStartupCheckStrategy(new NoopStartupCheckStrategy())
@@ -136,7 +139,13 @@ public void shouldOnlyPublishExposedPorts() {
136139
.getHostConfig()
137140
.getPortBindings()
138141
.getBindings();
139-
assertThat(hostBindings).as("only 1 port is bound on the host (published)").hasSize(1);
142+
// podman also returns unbound ports, but sets the binding value to null
143+
List<Ports.Binding[]> boundPorts = hostBindings
144+
.values()
145+
.stream()
146+
.filter(Objects::nonNull)
147+
.collect(Collectors.toList());
148+
assertThat(boundPorts).as("only 1 port is bound on the host (published)").hasSize(1);
140149

141150
Integer mappedPort = container.getMappedPort(8080);
142151
assertThat(mappedPort != 8080).as("port 8080 is bound to a different port on the host").isTrue();

core/src/test/java/org/testcontainers/containers/NetworkTest.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.testcontainers.containers;
22

3+
import com.github.dockerjava.api.model.Network.Ipam;
34
import org.junit.Rule;
45
import org.junit.Test;
56
import org.junit.experimental.runners.Enclosed;
@@ -65,7 +66,19 @@ public void testNetworkSupport() throws Exception {
6566

6667
@Test
6768
public void testBuilder() {
68-
try (Network network = Network.builder().driver("macvlan").build()) {
69+
try (
70+
Network network = Network
71+
.builder()
72+
.driver("macvlan")
73+
.createNetworkCmdModifier(cmd -> {
74+
cmd.withIpam(
75+
// mcvlan needs a subnet or podman will refuse to create the network
76+
// https://docs.podman.io/en/latest/markdown/podman-network-create.1.html#driver-d
77+
new Ipam().withConfig(new Ipam.Config().withSubnet("192.168.100.1/25"))
78+
);
79+
})
80+
.build()
81+
) {
6982
String id = network.getId();
7083
assertThat(
7184
DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(id).exec().getDriver()
@@ -78,7 +91,19 @@ public void testBuilder() {
7891
@Test
7992
public void testModifiers() {
8093
try (
81-
Network network = Network.builder().createNetworkCmdModifier(cmd -> cmd.withDriver("macvlan")).build()
94+
Network network = Network
95+
.builder()
96+
.createNetworkCmdModifier(cmd -> {
97+
cmd
98+
.withDriver("macvlan")
99+
.withIpam(
100+
new Ipam()
101+
// mcvlan needs a subnet or podman will refuse to create the network
102+
// https://docs.podman.io/en/latest/markdown/podman-network-create.1.html#driver-d
103+
.withConfig(new Ipam.Config().withSubnet("192.168.100.1/25"))
104+
);
105+
})
106+
.build()
82107
) {
83108
String id = network.getId();
84109
assertThat(

core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public void setUp() {
2727
)
2828
.withFileFromClasspath("Dockerfile", "health-wait-strategy-dockerfile/Dockerfile")
2929
)
30-
.waitingFor(Wait.forHealthcheck().withStartupTimeout(Duration.ofSeconds(3)));
30+
.waitingFor(Wait.forHealthcheck().withStartupTimeout(Duration.ofSeconds(5)));
3131
}
3232

3333
@Test

core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@ public void testNoNetworkContainer() {
2727
container.start();
2828
NetworkSettings networkSettings = container.getContainerInfo().getNetworkSettings();
2929

30-
assertThat(networkSettings.getNetworks()).as("only one network is set").hasSize(1);
31-
assertThat(networkSettings.getNetworks()).as("network is 'none'").containsKey("none");
30+
assertThat(networkSettings.getNetworks())
31+
.as("only one network is set")
32+
.allSatisfy((name, containerNetwork) -> {
33+
assertThat(name).as("network is 'none'").isEqualTo("none");
34+
});
3235
}
3336
}
3437

@@ -43,8 +46,11 @@ public void testHostNetworkContainer() {
4346
container.start();
4447
NetworkSettings networkSettings = container.getContainerInfo().getNetworkSettings();
4548

46-
assertThat(networkSettings.getNetworks()).as("only one network is set").hasSize(1);
47-
assertThat(networkSettings.getNetworks()).as("network is 'host'").containsKey("host");
49+
assertThat(networkSettings.getNetworks())
50+
.as("only one network is set")
51+
.allSatisfy((name, containerNetwork) -> {
52+
assertThat(networkSettings.getNetworks()).as("network is 'host'").containsKey("host");
53+
});
4854
}
4955
}
5056
}

core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.github.dockerjava.api.model.AuthConfig;
44
import org.intellij.lang.annotations.Language;
55
import org.junit.AfterClass;
6+
import org.junit.Assume;
67
import org.junit.BeforeClass;
78
import org.junit.ClassRule;
89
import org.junit.Test;
@@ -63,14 +64,14 @@ public static void beforeClass() throws Exception {
6364
final AuthConfig authConfig = new AuthConfig()
6465
.withUsername("testuser")
6566
.withPassword("notasecret")
66-
.withRegistryAddress("http://" + testRegistryAddress);
67+
.withRegistryAddress(testRegistryAddress);
6768

6869
// Replace the RegistryAuthLocator singleton with our mock, for the duration of this test
6970
final RegistryAuthLocator mockAuthLocator = Mockito.mock(RegistryAuthLocator.class);
7071
RegistryAuthLocator.setInstance(mockAuthLocator);
7172
when(
7273
mockAuthLocator.lookupAuthConfig(
73-
argThat(argument -> testRegistryAddress.equals(argument.getRegistry())),
74+
argThat(argument -> testRegistryAddress.replaceFirst("http://", "").equals(argument.getRegistry())),
7475
any()
7576
)
7677
)
@@ -117,6 +118,7 @@ public void testThatAuthLocatorIsUsedForDockerfileBuild() throws IOException {
117118

118119
@Test
119120
public void testThatAuthLocatorIsUsedForDockerComposePull() throws IOException {
121+
Assume.assumeTrue(TestEnvironment.clientSupportsCompose());
120122
// Prepare a simple temporary Docker Compose manifest which requires our custom private image
121123
Path tempFile = getLocalTempFile(".docker-compose.yml");
122124
@Language("yaml")

0 commit comments

Comments
 (0)