Skip to content
This repository was archived by the owner on Apr 10, 2024. It is now read-only.

Commit bd9c173

Browse files
authored
feat: running tests in parallel (#77)
1 parent 3374f3e commit bd9c173

File tree

10 files changed

+204
-60
lines changed

10 files changed

+204
-60
lines changed

core/src/main/java/io/javaoperatorsdk/jenvtest/binary/BinaryDownloader.java

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import io.javaoperatorsdk.jenvtest.JenvtestException;
1818
import io.javaoperatorsdk.jenvtest.Utils;
1919
import io.javaoperatorsdk.jenvtest.binary.repo.BinaryRepo;
20+
import io.javaoperatorsdk.jenvtest.lock.LockFile;
2021

2122
public class BinaryDownloader {
2223

@@ -32,7 +33,6 @@ public BinaryDownloader(String jenvtestDir, OSInfo osInfoProvider) {
3233
this.jenvtestDir = jenvtestDir;
3334
this.osInfoProvider = osInfoProvider;
3435
this.binaryRepo = new BinaryRepo(osInfoProvider);
35-
3636
}
3737

3838
BinaryDownloader(String jenvtestDir, BinaryRepo binaryRepo, OSInfo osInfoProvider) {
@@ -43,14 +43,30 @@ public BinaryDownloader(String jenvtestDir, OSInfo osInfoProvider) {
4343

4444
public File download(String version) {
4545
log.info("Downloading binaries with version: {}", version);
46-
var tempFile = binaryRepo.downloadVersionToTempFile(version);
47-
File dir = createDirForBinaries(version);
48-
extractFiles(tempFile, dir);
49-
var deleted = tempFile.delete();
50-
if (!deleted) {
51-
log.warn("Unable to delete temp file: {}", tempFile.getPath());
46+
var downloadDir = new File(jenvtestDir, BinaryManager.BINARY_LIST_DIR);
47+
downloadDir.mkdirs();
48+
LockFile lock =
49+
new LockFile(version + ".lock", downloadDir.getPath());
50+
var dirForVersion = dirForVersion(version);
51+
if (lock.tryLock()) {
52+
if (dirForVersion.exists()) {
53+
return dirForVersion;
54+
}
55+
var tempFile = binaryRepo.downloadVersionToTempFile(version);
56+
File dir = createDirForBinaries(version);
57+
extractFiles(tempFile, dir);
58+
var deleted = tempFile.delete();
59+
if (!deleted) {
60+
log.warn("Unable to delete temp file: {}", tempFile.getPath());
61+
}
62+
lock.releaseLock();
63+
return dir;
64+
} else {
65+
log.debug("Waiting for lock to be deleted for version: {}", version);
66+
lock.waitUntilLockDeleted();
67+
log.debug("Lock deleted for version: {}", version);
68+
return dirForVersion;
5269
}
53-
return dir;
5470
}
5571

5672
public File downloadLatest() {
@@ -99,14 +115,18 @@ private File extractEntry(TarArchiveEntry entry, File dir, TarArchiveInputStream
99115
}
100116

101117
private File createDirForBinaries(String version) {
102-
var dir = new File(jenvtestDir, BinaryManager.BINARY_LIST_DIR + File.separator
103-
+ version + Utils.platformSuffix(osInfoProvider));
118+
var dir = dirForVersion(version);
104119
if (!dir.mkdirs()) {
105120
throw new JenvtestException("Cannot created director: " + dir.getPath());
106121
}
107122
return dir;
108123
}
109124

125+
private File dirForVersion(String version) {
126+
return new File(jenvtestDir, BinaryManager.BINARY_LIST_DIR + File.separator
127+
+ version + Utils.platformSuffix(osInfoProvider));
128+
}
129+
110130
public String findLatestVersion() {
111131
var allRelevantVersions =
112132
listAllRelevantVersions().sorted(Utils.SEMVER_COMPARATOR).collect(Collectors.toList());

core/src/main/java/io/javaoperatorsdk/jenvtest/cert/CertManager.java

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.slf4j.LoggerFactory;
2929

3030
import io.javaoperatorsdk.jenvtest.JenvtestException;
31+
import io.javaoperatorsdk.jenvtest.lock.LockFile;
3132

3233
public class CertManager {
3334

@@ -46,16 +47,36 @@ public CertManager(String jenvtestDir) {
4647
}
4748

4849
public void createCertificatesIfNeeded() {
49-
generateAPIServerCertificates();
50-
generateUserCertificates();
50+
if (certFilesPresent()) {
51+
return;
52+
}
53+
// locking is for parallel execution
54+
LockFile lockFile = new LockFile("cert.lock", jenvtestDir);
55+
if (lockFile.tryLock()) {
56+
if (certFilesPresent()) {
57+
return;
58+
}
59+
try {
60+
generateAPIServerCertificates();
61+
generateUserCertificates();
62+
} finally {
63+
lockFile.releaseLock();
64+
}
65+
} else {
66+
lockFile.waitUntilLockDeleted();
67+
}
68+
}
69+
70+
private boolean certFilesPresent() {
71+
var apiCert = new File(jenvtestDir, API_SERVER_CERT_NAME);
72+
var apiKey = new File(jenvtestDir, API_SERVER_KEY_NAME);
73+
var clientCert = new File(jenvtestDir, CLIENT_CERT_NAME);
74+
var clientKey = new File(jenvtestDir, CLIENT_KEY_NAME);
75+
76+
return apiCert.exists() && apiKey.exists() && clientCert.exists() && clientKey.exists();
5177
}
5278

5379
private void generateAPIServerCertificates() {
54-
var cert = new File(jenvtestDir, API_SERVER_CERT_NAME);
55-
var key = new File(jenvtestDir, API_SERVER_KEY_NAME);
56-
if (cert.exists() && key.exists()) {
57-
return;
58-
}
5980
log.info("Generating API Server certificates");
6081
generateKeyAndCertificate("CN=example.org", new File(jenvtestDir, API_SERVER_KEY_NAME),
6182
new File(jenvtestDir, API_SERVER_CERT_NAME),
@@ -70,11 +91,6 @@ private GeneralName dns(String dns) {
7091
}
7192

7293
private void generateUserCertificates() {
73-
var cert = new File(jenvtestDir, CLIENT_CERT_NAME);
74-
var key = new File(jenvtestDir, CLIENT_KEY_NAME);
75-
if (cert.exists() && key.exists()) {
76-
return;
77-
}
7894
log.info("Generating Client certificates");
7995
generateKeyAndCertificate("O=system:masters,CN=jenvtest",
8096
new File(jenvtestDir, CLIENT_KEY_NAME),
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package io.javaoperatorsdk.jenvtest.lock;
2+
3+
import java.io.File;
4+
import java.io.IOException;
5+
import java.nio.file.*;
6+
7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
9+
10+
import io.javaoperatorsdk.jenvtest.JenvtestException;
11+
12+
public class LockFile {
13+
14+
private static final Logger log = LoggerFactory.getLogger(LockFile.class);
15+
16+
private final String dir;
17+
private final String lockFileName;
18+
19+
public LockFile(String lockFileName, String dir) {
20+
this.dir = dir;
21+
this.lockFileName = lockFileName;
22+
}
23+
24+
public boolean tryLock() {
25+
File file = new File(dir, lockFileName);
26+
try {
27+
return file.createNewFile();
28+
} catch (IOException e) {
29+
throw new JenvtestException(e);
30+
}
31+
}
32+
33+
public void releaseLock() {
34+
File file = new File(dir, lockFileName);
35+
try {
36+
Files.deleteIfExists(file.toPath());
37+
} catch (IOException e) {
38+
throw new JenvtestException(e);
39+
}
40+
}
41+
42+
public void waitUntilLockDeleted() {
43+
var file = new File(dir);
44+
var path = file.toPath();
45+
46+
try (final WatchService watchService = FileSystems.getDefault().newWatchService()) {
47+
path.register(watchService, StandardWatchEventKinds.ENTRY_DELETE);
48+
while (true) {
49+
final WatchKey wk = watchService.take();
50+
for (WatchEvent<?> event : wk.pollEvents()) {
51+
final Path changed = (Path) event.context();
52+
log.info("!! Event path: {} event: {}", changed, event);
53+
if (changed.endsWith(lockFileName)) {
54+
return;
55+
}
56+
}
57+
// reset the key
58+
boolean valid = wk.reset();
59+
if (!valid) {
60+
log.warn("Watch key no longer valid");
61+
}
62+
}
63+
} catch (IOException e) {
64+
throw new JenvtestException(e);
65+
} catch (InterruptedException e) {
66+
Thread.currentThread().interrupt();
67+
throw new JenvtestException(e);
68+
}
69+
}
70+
71+
}

core/src/main/java/io/javaoperatorsdk/jenvtest/process/EtcdProcess.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public int startEtcd() {
7979
}
8080

8181
private void waitUntilEtcdHealthy(int port) {
82-
new ProcessReadinessChecker(port, "health", "etcd", false).waitUntilReady();
82+
new ProcessReadinessChecker().waitUntilReady(port, "health", "etcd", false);
8383
}
8484

8585
public void cleanEtcdData() {

core/src/main/java/io/javaoperatorsdk/jenvtest/process/KubeAPIServerProcess.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public class KubeAPIServerProcess {
1717
private static final Logger log = LoggerFactory.getLogger(KubeAPIServerProcess.class);
1818
private static final Logger apiLog = LoggerFactory.getLogger(KubeAPIServerProcess.class
1919
.getName() + ".APIServerProcessLogs");
20+
public static final String KUBE_API_SERVER = "Kube API Server";
2021

2122
private final CertManager certManager;
2223
private final BinaryManager binaryManager;
@@ -85,7 +86,9 @@ private List<String> createCommand(File apiServerBinary, int apiServerPort, int
8586
}
8687

8788
public void waitUntilReady() {
88-
new ProcessReadinessChecker(apiServerPort, "readyz", "Kube API Server", true).waitUntilReady();
89+
var readinessChecker = new ProcessReadinessChecker();
90+
readinessChecker.waitUntilReady(apiServerPort, "readyz", KUBE_API_SERVER, true);
91+
readinessChecker.waitUntilDefaultNamespaceAvailable(apiServerPort, binaryManager, certManager);
8992
}
9093

9194
public void stopApiServer() {

core/src/main/java/io/javaoperatorsdk/jenvtest/process/ProcessReadinessChecker.java

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.security.cert.X509Certificate;
1515
import java.time.LocalTime;
1616
import java.time.temporal.ChronoUnit;
17+
import java.util.function.BooleanSupplier;
1718

1819
import javax.net.ssl.SSLContext;
1920
import javax.net.ssl.SSLEngine;
@@ -24,34 +25,57 @@
2425
import org.slf4j.LoggerFactory;
2526

2627
import io.javaoperatorsdk.jenvtest.JenvtestException;
28+
import io.javaoperatorsdk.jenvtest.binary.BinaryManager;
29+
import io.javaoperatorsdk.jenvtest.cert.CertManager;
30+
31+
import static io.javaoperatorsdk.jenvtest.process.KubeAPIServerProcess.KUBE_API_SERVER;
2732

2833
public class ProcessReadinessChecker {
34+
2935
private static final Logger log = LoggerFactory.getLogger(ProcessReadinessChecker.class);
3036

31-
public static final int STARTUP_TIMEOUT = 10_000;
32-
public static final int POLLING_INTERVAL = 150;
37+
public static final int STARTUP_TIMEOUT = 60_000;
38+
public static final int POLLING_INTERVAL = 200;
39+
3340

34-
private final int port;
35-
private final String readyCheckPath;
36-
private final String processName;
37-
private final boolean useTLS;
41+
public void waitUntilDefaultNamespaceAvailable(int apiServerPort,
42+
BinaryManager binaryManager,
43+
CertManager certManager) {
44+
pollWithTimeout(() -> defaultNamespaceExists(apiServerPort, binaryManager, certManager),
45+
KUBE_API_SERVER);
46+
}
3847

48+
private boolean defaultNamespaceExists(int apiServerPort, BinaryManager binaryManager,
49+
CertManager certManager) {
50+
try {
51+
Process process = new ProcessBuilder(binaryManager.binaries().getKubectl().getPath(),
52+
"--client-certificate=" + certManager.getClientCertPath(),
53+
"--client-key=" + certManager.getClientKeyPath(),
54+
"--certificate-authority=" + certManager.getAPIServerCertPath(),
55+
"--server=https://127.0.0.1:" + apiServerPort,
56+
"--request-timeout=5s",
57+
"get", "ns", "default").start();
58+
return process.waitFor() == 0;
59+
} catch (IOException e) {
60+
throw new JenvtestException(e);
61+
} catch (InterruptedException e) {
62+
Thread.currentThread().interrupt();
63+
throw new JenvtestException(e);
64+
}
65+
}
3966

40-
public ProcessReadinessChecker(int port, String readyCheckPath, String processName,
67+
public void waitUntilReady(int port, String readyCheckPath, String processName,
4168
boolean useTLS) {
42-
this.port = port;
43-
this.readyCheckPath = readyCheckPath;
44-
this.processName = processName;
45-
this.useTLS = useTLS;
69+
var client = getHttpClient();
70+
var request = getHttpRequest(useTLS, readyCheckPath, port);
71+
pollWithTimeout(() -> ready(client, request, processName, port), processName);
4672
}
4773

48-
public void waitUntilReady() {
74+
private static void pollWithTimeout(BooleanSupplier predicate, String processName) {
4975
try {
50-
var client = getHttpClient();
51-
var request = getHttpRequest();
5276
var startedAt = LocalTime.now();
5377
while (true) {
54-
if (ready(client, request)) {
78+
if (predicate.getAsBoolean()) {
5579
return;
5680
}
5781
if (LocalTime.now().isAfter(startedAt.plus(STARTUP_TIMEOUT, ChronoUnit.MILLIS))) {
@@ -65,7 +89,7 @@ public void waitUntilReady() {
6589
}
6690
}
6791

68-
private boolean ready(HttpClient client, HttpRequest request) {
92+
private boolean ready(HttpClient client, HttpRequest request, String processName, int port) {
6993
try {
7094
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
7195
log.debug("Ready Response message:{} code: {} for {} on Port: {}", response.body(),
@@ -84,7 +108,7 @@ private boolean ready(HttpClient client, HttpRequest request) {
84108
}
85109
}
86110

87-
private HttpRequest getHttpRequest() {
111+
private HttpRequest getHttpRequest(boolean useTLS, String readyCheckPath, int port) {
88112
try {
89113
return HttpRequest.newBuilder()
90114
.uri(new URI((useTLS ? "https" : "http") + "://127.0.0.1:" + port + "/" + readyCheckPath))
@@ -128,7 +152,7 @@ public void checkServerTrusted(
128152

129153
@Override
130154
public void checkClientTrusted(X509Certificate[] chain, String authType,
131-
SSLEngine engine) throws CertificateException {
155+
SSLEngine engine) {
132156

133157
}
134158

core/src/test/java/io/javaoperatorsdk/jenvtest/sample/KubeApiServerTest.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,6 @@ void apiServerWithSpecificVersion() {
2626
.build()));
2727
}
2828

29-
@Test
30-
void usingKubeConfigFileToInitClient() {
31-
var kubeApi = new KubeAPIServer(KubeAPIServerConfigBuilder.anAPIServerConfig()
32-
.withUpdateKubeConfig(true)
33-
.build());
34-
kubeApi.start();
35-
36-
var client = new KubernetesClientBuilder().build();
37-
38-
TestUtils.simpleTest(client);
39-
}
40-
4129
@Test
4230
void canWaitForEtcdHealthCheckOnStartup() {
4331
var kubeApi = new KubeAPIServer(KubeAPIServerConfigBuilder.anAPIServerConfig()

0 commit comments

Comments
 (0)