Skip to content

Commit 6048ac5

Browse files
committed
first test run
Signed-off-by: wind57 <eugen.rabii@gmail.com>
1 parent 7036007 commit 6048ac5

8 files changed

Lines changed: 195 additions & 101 deletions

File tree

spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestAssertions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ static void manifests(Phase phase, Fabric8ClientKubernetesFixture fabric8Kuberne
9696

9797
if (phase.equals(Phase.CREATE)) {
9898
fabric8KubernetesFixture.createAndWait(namespace, configMap, null);
99-
fabric8KubernetesFixture.createAndWait(namespace, null, deployment, service, true);
99+
fabric8KubernetesFixture.createAndWait(namespace, deployment, service, true);
100100
}
101101
else {
102102
fabric8KubernetesFixture.deleteAndWait(namespace, configMap, null);

spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java

Lines changed: 46 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,9 @@
1717
package org.springframework.cloud.kubernetes.integration.tests.commons;
1818

1919
import java.io.File;
20-
import java.io.IOException;
2120
import java.io.InputStream;
22-
import java.io.UncheckedIOException;
2321
import java.nio.charset.StandardCharsets;
24-
import java.nio.file.Files;
25-
import java.nio.file.Path;
2622
import java.nio.file.Paths;
27-
import java.nio.file.StandardCopyOption;
2823
import java.time.Duration;
2924
import java.util.Arrays;
3025
import java.util.List;
@@ -33,7 +28,6 @@
3328

3429
import com.github.dockerjava.api.command.ListImagesCmd;
3530
import com.github.dockerjava.api.command.PullImageCmd;
36-
import com.github.dockerjava.api.command.SaveImageCmd;
3731
import com.github.dockerjava.api.model.Image;
3832
import org.apache.commons.logging.Log;
3933
import org.apache.commons.logging.LogFactory;
@@ -50,9 +44,9 @@
5044
import org.springframework.web.reactive.function.client.WebClient;
5145

5246
import static org.springframework.cloud.kubernetes.integration.tests.commons.Constants.KUBERNETES_VERSION_FILE;
53-
import static org.springframework.cloud.kubernetes.integration.tests.commons.Constants.TEMP_FOLDER;
5447
import static org.springframework.cloud.kubernetes.integration.tests.commons.Constants.TMP_IMAGES;
5548
import static org.springframework.cloud.kubernetes.integration.tests.commons.FixedPortsK3sContainer.CONTAINER;
49+
import static org.springframework.cloud.kubernetes.integration.tests.commons.FixedPortsK3sContainer.REGISTRY_PORT;
5650

5751
/**
5852
* A few commons things that can be re-used across clients. This is meant to be used for
@@ -78,66 +72,64 @@ public static K3sContainer container() {
7872
return CONTAINER;
7973
}
8074

81-
public static void loadSpringCloudKubernetesImage(String project, K3sContainer container) {
75+
public static void tagAndPushSpringCloudKubernetesImage(String imageName, K3sContainer container) {
8276
try {
83-
loadImage("springcloud/" + project, pomVersion(), project, container);
77+
String springCloudImage = "springcloud/" + imageName + ":" + pomVersion();
78+
tagAndPushImage(springCloudImage, container);
8479
}
8580
catch (Exception e) {
8681
throw new RuntimeException(e);
8782
}
8883
}
8984

9085
/**
91-
* create a tar, copy it in the running k3s and load this tar as an image.
86+
* tag image and push to local registry.
9287
*/
93-
public static void loadImage(String image, String tag, String tarName, K3sContainer container) {
88+
public static void tagAndPushImage(String imageFromDeploymentWithTag, K3sContainer container) {
9489

95-
if (imageAlreadyInK3s(container, tarName)) {
90+
if (imageAlreadyInK3s(container, imageFromDeploymentWithTag)) {
9691
return;
9792
}
9893

99-
// save image
100-
try (SaveImageCmd saveImageCmd = container.getDockerClient().saveImageCmd(image)) {
101-
InputStream imageStream = saveImageCmd.withTag(tag).exec();
102-
103-
Path imagePath = Paths.get(TEMP_FOLDER + "/" + tarName + ".tar");
104-
try {
105-
Files.copy(imageStream, imagePath, StandardCopyOption.REPLACE_EXISTING);
106-
}
107-
catch (IOException e) {
108-
throw new UncheckedIOException(e);
94+
try {
95+
int lastColon = imageFromDeploymentWithTag.lastIndexOf(':');
96+
if (lastColon < 0) {
97+
throw new IllegalArgumentException("image must include tag: " + imageFromDeploymentWithTag);
10998
}
110-
// import image with ctr. this works because TEMP_FOLDER is mounted in the
111-
// container
99+
100+
String imageWithoutTag = imageFromDeploymentWithTag.substring(0, lastColon);
101+
String tag = imageFromDeploymentWithTag.substring(lastColon + 1);
102+
103+
String targetRepository = "localhost:" + REGISTRY_PORT + "/" + imageWithoutTag;
104+
String targetImageWithTag = targetRepository + ":" + tag;
105+
106+
container.getDockerClient().tagImageCmd(imageFromDeploymentWithTag, targetRepository, tag).exec();
107+
112108
Awaitilities.awaitUntil(120, 1000, () -> {
113-
Container.ExecResult result;
114109
try {
115-
result = container.execInContainer("ctr", "i", "import",
116-
Constants.TEMP_FOLDER + "/" + tarName + ".tar");
110+
container.getDockerClient().pushImageCmd(targetImageWithTag).start().awaitCompletion();
111+
return true;
117112
}
118113
catch (Exception e) {
119-
throw new RuntimeException(e);
114+
LOG.info("failed to push image " + targetImageWithTag + " to local registry", e);
115+
return false;
120116
}
121-
boolean noErrors = result.getStderr() == null || result.getStderr().isEmpty();
122-
if (!noErrors) {
123-
LOG.info("error is : " + result.getStderr());
124-
}
125-
return noErrors;
126117
});
127118
}
128-
119+
catch (Exception e) {
120+
throw new RuntimeException(e);
121+
}
129122
}
130123

131124
/**
132-
* Ensures a common external test image is available inside K3s/containerd.
133-
* It first checks whether the image is already present in K3s.
134-
* If not, it tries to load it as a tar under '/tmp/docker/images'.
135-
* If no matching tar is found, it pulls the image directly inside K3s
136-
* using 'ctr images pull'.
137-
* This is meant for shared test images such as busybox, wiremock, kafka, etc.
125+
* Ensures a common external test image is available inside K3s/containerd. It first
126+
* checks whether the image is already present in K3s. If not, it tries to load it as
127+
* a tar under '/tmp/docker/images'. If no matching tar is found, it pulls the image
128+
* directly inside K3s using 'ctr images pull'. This is meant for shared test images
129+
* such as busybox, wiremock, kafka, etc.
138130
*/
139-
public static void loadOrPullCommonTestImages(K3sContainer container, String tarName,
140-
String imageNameForDownload, String imageVersion) {
131+
public static void loadOrPullCommonTestImages(K3sContainer container, String tarName, String imageNameForDownload,
132+
String imageVersion) {
141133

142134
if (imageAlreadyInK3s(container, tarName)) {
143135
return;
@@ -239,15 +231,14 @@ public static void validateImage(String image, K3sContainer container) {
239231
}
240232
}
241233

242-
public static void pullImage(String image, String tag, String tarName, K3sContainer container)
243-
throws InterruptedException {
234+
public static void pullImage(String imageFromDeployment, K3sContainer container) throws InterruptedException {
244235

245-
if (imageAlreadyInK3s(container, tarName)) {
236+
if (imageAlreadyInK3s(container, imageFromDeployment)) {
246237
return;
247238
}
248239

249-
try (PullImageCmd pullImageCmd = container.getDockerClient().pullImageCmd(image)) {
250-
pullImageCmd.withTag(tag).start().awaitCompletion();
240+
try (PullImageCmd pullImageCmd = container.getDockerClient().pullImageCmd(imageFromDeployment)) {
241+
pullImageCmd.start().awaitCompletion();
251242
}
252243
}
253244

@@ -324,24 +315,19 @@ private static void loadImageFromPath(String tarName, K3sContainer container) {
324315
});
325316
}
326317

327-
private static boolean imageAlreadyInK3s(K3sContainer container, String tarName) {
318+
private static boolean imageAlreadyInK3s(K3sContainer container, String imageWithTag) {
319+
try {
320+
String stdout = container.execInContainer("ctr", "-n", "k8s.io", "images", "list", "-q").getStdout();
328321

329-
if (tarName == null) {
330-
return false;
331-
}
322+
boolean present = Arrays.stream(stdout.split("\\R")).map(String::trim).anyMatch(imageWithTag::equals);
332323

333-
try {
334-
boolean present = container.execInContainer("sh", "-c", "ctr images list | grep " + tarName)
335-
.getStdout()
336-
.contains(tarName);
337324
if (present) {
338-
System.out.println("image : " + tarName + " already in k3s, skipping");
325+
System.out.println("image : " + imageWithTag + " already in k3s, skipping");
339326
return true;
340327
}
341-
else {
342-
System.out.println("image : " + tarName + " not in k3s");
343-
return false;
344-
}
328+
329+
System.out.println("image : " + imageWithTag + " not in k3s");
330+
return false;
345331
}
346332
catch (Exception e) {
347333
throw new RuntimeException(e);

spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Constants.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package org.springframework.cloud.kubernetes.integration.tests.commons;
1818

19-
import java.io.File;
20-
2119
/**
2220
* @author wind57
2321
*/
@@ -32,11 +30,6 @@ private Constants() {
3230
*/
3331
static final String TMP_IMAGES = "/tmp/docker/images";
3432

35-
/**
36-
* Temporary folder where to load images.
37-
*/
38-
static final String TEMP_FOLDER = new File(System.getProperty("java.io.tmpdir")).getAbsolutePath();
39-
4033
/**
4134
* where is the version situated.
4235
*/

spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/FixedPortsK3sContainer.java

Lines changed: 82 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,22 @@
1616

1717
package org.springframework.cloud.kubernetes.integration.tests.commons;
1818

19+
import java.io.IOException;
20+
import java.io.UncheckedIOException;
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
1923
import java.util.Objects;
2024

2125
import com.github.dockerjava.api.model.Bind;
2226
import com.github.dockerjava.api.model.HostConfig;
27+
import org.testcontainers.containers.GenericContainer;
28+
import org.testcontainers.containers.Network;
2329
import org.testcontainers.k3s.K3sContainer;
2430
import org.testcontainers.utility.DockerImageName;
31+
import org.testcontainers.utility.MountableFile;
32+
33+
import org.springframework.test.util.TestSocketUtils;
2534

26-
import static org.springframework.cloud.kubernetes.integration.tests.commons.Constants.TEMP_FOLDER;
2735
import static org.springframework.cloud.kubernetes.integration.tests.commons.Constants.TMP_IMAGES;
2836

2937
/**
@@ -32,49 +40,114 @@
3240
*
3341
* @author wind57
3442
*/
35-
final class FixedPortsK3sContainer extends K3sContainer {
43+
public final class FixedPortsK3sContainer extends K3sContainer {
3644

3745
/**
3846
* Test containers exposed ports.
3947
*/
4048
private static final int[] EXPOSED_PORTS = new int[] { 80, 6443, 8080, 8888, 9092, 32321, 32322 };
4149

50+
/**
51+
* Port for the local running images registry.
52+
*/
53+
public static final int REGISTRY_PORT = TestSocketUtils.findAvailableTcpPort();
54+
55+
private static final Network NETWORK = Network.newNetwork();
56+
57+
private static final Path REGISTRIES_YAML = createRegistriesYaml();
58+
59+
private static final LocalRegistryContainer REGISTRY = new LocalRegistryContainer().configureFixedPorts()
60+
.withNetwork(NETWORK)
61+
.withNetworkAliases("registry")
62+
.withReuse(true);
63+
4264
/**
4365
* Rancher version to use for test-containers.
4466
*/
4567
private static final String RANCHER_VERSION = "rancher/k3s:v1.34.1-k3s1";
4668

69+
/**
70+
* Docker registry version.
71+
*/
72+
private static final String DOCKER_REGISTRY_VERSION = "registry:3.1.0";
73+
4774
/**
4875
* Command to use when starting rancher. Without "server" option, traefik is not
4976
* installed
5077
*/
51-
private static final String RANCHER_COMMAND = "server --disable=metric-server";
78+
private static final String RANCHER_COMMAND = "server --disable=metric-server --disable-default-registry-endpoint";
5279

5380
static final K3sContainer CONTAINER = new FixedPortsK3sContainer(DockerImageName.parse(RANCHER_VERSION))
5481
.configureFixedPorts()
5582
.addBinds()
5683
.withCommand(RANCHER_COMMAND)
57-
.withReuse(true);
84+
.withReuse(true)
85+
.withNetwork(NETWORK)
86+
.withCopyFileToContainer(MountableFile.forHostPath(REGISTRIES_YAML), "/etc/rancher/k3s/registries.yaml");
5887

59-
FixedPortsK3sContainer(DockerImageName dockerImageName) {
88+
private FixedPortsK3sContainer(DockerImageName dockerImageName) {
6089
super(dockerImageName);
90+
REGISTRY.start();
6191
}
6292

63-
FixedPortsK3sContainer configureFixedPorts() {
93+
private FixedPortsK3sContainer configureFixedPorts() {
6494
for (int port : EXPOSED_PORTS) {
6595
super.addFixedExposedPort(port, port);
6696
}
6797
return this;
6898
}
6999

70-
FixedPortsK3sContainer addBinds() {
100+
private FixedPortsK3sContainer addBinds() {
71101
super.withCreateContainerCmdModifier(cmd -> {
72102
HostConfig hostConfig = Objects.requireNonNull(cmd.getHostConfig());
73-
hostConfig.withBinds(Bind.parse(TEMP_FOLDER + ":" + TEMP_FOLDER),
74-
Bind.parse(TMP_IMAGES + ":" + TMP_IMAGES));
103+
hostConfig.withBinds(Bind.parse(TMP_IMAGES + ":" + TMP_IMAGES));
75104
});
76105

77106
return this;
78107
}
79108

109+
/**
110+
* Small doc on how the set-up works. ( 5000 is just an example ) <pre>
111+
* - we start a local registry and expose it on localhost:<5000>
112+
* - from the host, we can push an image with:
113+
* docker push localhost:5000/image:tag
114+
* - the image is now stored in that local registry
115+
* - k3s later sees the image reference: localhost:5000/image:tag
116+
* - inside the k3s container, localhost would point to k3s itself, not to the registry
117+
* - because of the mirror entry in registries.yaml, when k3s/containerd sees
118+
* an image starting with localhost:5000, it actually uses the endpoint 'http://registry:5000'
119+
* - LocalRegistryContainer has withNetworkAliases("registry")
120+
* - this makes the registry reachable from the same Docker network via the hostname "registry"
121+
* </pre>
122+
*/
123+
private static Path createRegistriesYaml() {
124+
try {
125+
Path file = Files.createTempFile("registries", ".yaml");
126+
// can't use text blocks here because of checkstyle
127+
String content = "mirrors:\n" + " \"localhost:%s\":\n" + " endpoint:\n"
128+
+ " - \"http://registry:5000\"\n";
129+
Files.writeString(file, content.formatted(REGISTRY_PORT));
130+
return file;
131+
}
132+
catch (IOException e) {
133+
throw new UncheckedIOException(e);
134+
}
135+
}
136+
137+
/**
138+
* official registry image with fixed port 5000.
139+
*/
140+
private static final class LocalRegistryContainer extends GenericContainer<LocalRegistryContainer> {
141+
142+
private LocalRegistryContainer() {
143+
super(DOCKER_REGISTRY_VERSION);
144+
}
145+
146+
private LocalRegistryContainer configureFixedPorts() {
147+
addFixedExposedPort(REGISTRY_PORT, 5000);
148+
return this;
149+
}
150+
151+
}
152+
80153
}

0 commit comments

Comments
 (0)