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

Commit f632843

Browse files
authored
improvement: use etcd health check (#78)
1 parent 51d4de5 commit f632843

File tree

7 files changed

+207
-125
lines changed

7 files changed

+207
-125
lines changed

core/src/main/java/io/javaoperatorsdk/jenvtest/KubeAPIServer.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ public class KubeAPIServer implements UnexpectedProcessStopHandler {
1414

1515
private static final Logger log = LoggerFactory.getLogger(KubeAPIServer.class);
1616

17-
public static final int STARTUP_TIMEOUT = 10_000;
18-
1917
private final KubeAPIServerConfig config;
2018
private final BinaryManager binaryManager;
2119
private final CertManager certManager;
@@ -32,7 +30,8 @@ public KubeAPIServer(KubeAPIServerConfig config) {
3230
this.binaryManager = new BinaryManager(config);
3331
this.certManager = new CertManager(config.getJenvtestDir());
3432
this.kubeConfig = new KubeConfig(certManager, binaryManager);
35-
this.etcdProcess = new EtcdProcess(binaryManager, this);
33+
this.etcdProcess = new EtcdProcess(binaryManager, this,
34+
config.isWaitForEtcdHealthCheckOnStartup());
3635
this.kubeApiServerProcess =
3736
new KubeAPIServerProcess(certManager, binaryManager, this, config);
3837
}
@@ -46,7 +45,7 @@ public void start() {
4645
if (config.isUpdateKubeConfig()) {
4746
kubeConfig.updateKubeConfig(apiServerPort);
4847
}
49-
kubeApiServerProcess.waitUntilDefaultNamespaceCreated();
48+
kubeApiServerProcess.waitUntilReady();
5049
log.debug("API Server ready to use");
5150
}
5251

core/src/main/java/io/javaoperatorsdk/jenvtest/KubeAPIServerConfig.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,20 @@ public class KubeAPIServerConfig {
4242
*/
4343
private final boolean updateKubeConfig;
4444

45+
/**
46+
* This is mostly not needed, and increases startup time.
47+
*/
48+
private final boolean waitForEtcdHealthCheckOnStartup;
49+
4550
KubeAPIServerConfig(String jenvtestDir, String apiServerVersion, boolean offlineMode,
46-
List<String> apiServerFlags, boolean updateKubeConfig) {
51+
List<String> apiServerFlags, boolean updateKubeConfig,
52+
boolean waitForEtcdHealthCheckOnStartup) {
4753
this.jenvtestDir = jenvtestDir;
4854
this.apiServerVersion = apiServerVersion;
4955
this.offlineMode = offlineMode;
5056
this.apiServerFlags = apiServerFlags;
5157
this.updateKubeConfig = updateKubeConfig;
58+
this.waitForEtcdHealthCheckOnStartup = waitForEtcdHealthCheckOnStartup;
5259
}
5360

5461
public String getJenvtestDir() {
@@ -70,4 +77,8 @@ public List<String> getApiServerFlags() {
7077
public boolean isUpdateKubeConfig() {
7178
return updateKubeConfig;
7279
}
80+
81+
public boolean isWaitForEtcdHealthCheckOnStartup() {
82+
return waitForEtcdHealthCheckOnStartup;
83+
}
7384
}

core/src/main/java/io/javaoperatorsdk/jenvtest/KubeAPIServerConfigBuilder.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ public final class KubeAPIServerConfigBuilder {
1010
public static final String JENVTEST_DOWNLOAD_BINARIES = "JENVTEST_OFFLINE_MODE";
1111
public static final String JENVTEST_DIR_ENV_VAR = "JENVTEST_DIR";
1212
public static final String JENVTEST_API_SERVER_VERSION_ENV_VAR = "JENVTEST_API_SERVER_VERSION";
13+
public static final String JENVTEST_WAIT_FOR_ETCD_HEALTH_CHECK =
14+
"JENVTEST_WAIT_FOR_ETCD_HEALTH_CHECK";
1315

1416
public static final String DIRECTORY_NAME = ".jenvtest";
1517

@@ -18,6 +20,7 @@ public final class KubeAPIServerConfigBuilder {
1820
private Boolean offlineMode;
1921
private boolean updateKubeConfig = false;
2022
private final List<String> apiServerFlags = new ArrayList<>(0);
23+
private Boolean waitForEtcdHealthCheckOnStartup;
2124

2225
public KubeAPIServerConfigBuilder() {}
2326

@@ -63,8 +66,17 @@ public KubeAPIServerConfig build() {
6366
this.apiServerVersion = apiServerVersionEnvVar;
6467
}
6568
}
69+
if (waitForEtcdHealthCheckOnStartup == null) {
70+
var waitForEtcdHealthCheckOnStartup = System.getenv(JENVTEST_API_SERVER_VERSION_ENV_VAR);
71+
if (waitForEtcdHealthCheckOnStartup != null) {
72+
this.waitForEtcdHealthCheckOnStartup =
73+
Boolean.parseBoolean(waitForEtcdHealthCheckOnStartup);
74+
} else {
75+
this.waitForEtcdHealthCheckOnStartup = false;
76+
}
77+
}
6678
return new KubeAPIServerConfig(jenvtestDir, apiServerVersion, offlineMode, apiServerFlags,
67-
updateKubeConfig);
79+
updateKubeConfig, waitForEtcdHealthCheckOnStartup);
6880
}
6981

7082
public KubeAPIServerConfigBuilder withUpdateKubeConfig(boolean updateKubeConfig) {
@@ -90,6 +102,12 @@ public KubeAPIServerConfigBuilder withApiServerFlag(String key) {
90102
return this;
91103
}
92104

105+
public KubeAPIServerConfigBuilder withWaitForEtcdHealthCheckOnStartup(
106+
boolean waitForEtcdHealthCheckOnStartup) {
107+
this.waitForEtcdHealthCheckOnStartup = waitForEtcdHealthCheckOnStartup;
108+
return this;
109+
}
110+
93111
private void checkKeyPrefix(String key) {
94112
if (!key.startsWith("--")) {
95113
throw new JenvtestException(

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ public class EtcdProcess {
2323
private volatile Process etcdProcess;
2424
private volatile boolean stopped = false;
2525
private final UnexpectedProcessStopHandler processStopHandler;
26+
private final boolean waitForHealthCheck;
2627
private File tempWalDir;
2728
private File tempDataDir;
2829

2930
public EtcdProcess(BinaryManager binaryManager,
30-
UnexpectedProcessStopHandler processStopHandler) {
31+
UnexpectedProcessStopHandler processStopHandler, boolean waitForHealthCheck) {
3132
this.binaryManager = binaryManager;
3233
this.processStopHandler = processStopHandler;
34+
this.waitForHealthCheck = waitForHealthCheck;
3335
}
3436

3537
public int startEtcd() {
@@ -67,12 +69,19 @@ public int startEtcd() {
6769
return null;
6870
});
6971
log.debug("etcd started on port: {}", port);
72+
if (waitForHealthCheck) {
73+
waitUntilEtcdHealthy(port);
74+
}
7075
return port;
7176
} catch (IOException e) {
7277
throw new JenvtestException(e);
7378
}
7479
}
7580

81+
private void waitUntilEtcdHealthy(int port) {
82+
new ProcessReadinessChecker(port, "health", "etcd", false).waitUntilReady();
83+
}
84+
7685
public void cleanEtcdData() {
7786
try {
7887
FileUtils.deleteDirectory(tempDataDir);

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

Lines changed: 2 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,21 @@
22

33
import java.io.File;
44
import java.io.IOException;
5-
import java.net.ConnectException;
6-
import java.net.Socket;
7-
import java.net.URI;
8-
import java.net.URISyntaxException;
9-
import java.net.http.HttpClient;
10-
import java.net.http.HttpRequest;
11-
import java.net.http.HttpResponse;
12-
import java.security.KeyManagementException;
13-
import java.security.NoSuchAlgorithmException;
14-
import java.security.cert.CertificateException;
15-
import java.security.cert.X509Certificate;
16-
import java.time.LocalTime;
17-
import java.time.temporal.ChronoUnit;
185
import java.util.ArrayList;
196
import java.util.List;
207

21-
import javax.net.ssl.*;
22-
238
import org.slf4j.Logger;
249
import org.slf4j.LoggerFactory;
2510

2611
import io.javaoperatorsdk.jenvtest.*;
2712
import io.javaoperatorsdk.jenvtest.binary.BinaryManager;
2813
import io.javaoperatorsdk.jenvtest.cert.CertManager;
2914

30-
import static io.javaoperatorsdk.jenvtest.KubeAPIServer.STARTUP_TIMEOUT;
31-
3215
public class KubeAPIServerProcess {
3316

3417
private static final Logger log = LoggerFactory.getLogger(KubeAPIServerProcess.class);
3518
private static final Logger apiLog = LoggerFactory.getLogger(KubeAPIServerProcess.class
3619
.getName() + ".APIServerProcessLogs");
37-
public static final int POLLING_INTERVAL = 150;
3820

3921
private final CertManager certManager;
4022
private final BinaryManager binaryManager;
@@ -102,106 +84,8 @@ private List<String> createCommand(File apiServerBinary, int apiServerPort, int
10284
return command;
10385
}
10486

105-
public void waitUntilDefaultNamespaceCreated() {
106-
try {
107-
var client = getHttpClient();
108-
var request = getHttpRequest();
109-
var startedAt = LocalTime.now();
110-
while (true) {
111-
if (ready(client, request)) {
112-
return;
113-
}
114-
if (LocalTime.now().isAfter(startedAt.plus(STARTUP_TIMEOUT, ChronoUnit.MILLIS))) {
115-
throw new JenvtestException("API Server did not start properly");
116-
}
117-
Thread.sleep(POLLING_INTERVAL);
118-
}
119-
} catch (InterruptedException e) {
120-
Thread.currentThread().interrupt();
121-
throw new JenvtestException(e);
122-
}
123-
}
124-
125-
private boolean ready(HttpClient client, HttpRequest request) {
126-
try {
127-
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
128-
log.debug("Ready Response message:{} code: {} Api Server Port: {}", response.body(),
129-
response.statusCode(),
130-
apiServerPort);
131-
return response.statusCode() == 200;
132-
} catch (ConnectException e) {
133-
// still want to retry
134-
log.warn("Cannot connect to the server", e);
135-
return false;
136-
} catch (IOException e) {
137-
throw new JenvtestException(e);
138-
} catch (InterruptedException e) {
139-
Thread.currentThread().interrupt();
140-
throw new JenvtestException(e);
141-
}
142-
}
143-
144-
private HttpRequest getHttpRequest() {
145-
try {
146-
return HttpRequest.newBuilder()
147-
.uri(new URI("https://127.0.0.1:" + apiServerPort + "/readyz"))
148-
.GET()
149-
.build();
150-
} catch (URISyntaxException e) {
151-
throw new JenvtestException(e);
152-
}
153-
}
154-
155-
private static HttpClient getHttpClient() {
156-
try {
157-
var sslContext = SSLContext.getInstance("TLS");
158-
sslContext.init(
159-
null,
160-
new TrustManager[] {
161-
new X509ExtendedTrustManager() {
162-
@Override
163-
public void checkClientTrusted(X509Certificate[] chain, String authType,
164-
Socket socket) throws CertificateException {
165-
166-
}
167-
168-
public X509Certificate[] getAcceptedIssuers() {
169-
return null;
170-
}
171-
172-
public void checkClientTrusted(
173-
final X509Certificate[] a_certificates,
174-
final String a_auth_type) {}
175-
176-
public void checkServerTrusted(
177-
final X509Certificate[] a_certificates,
178-
final String a_auth_type) {}
179-
180-
181-
public void checkServerTrusted(
182-
final X509Certificate[] a_certificates,
183-
final String a_auth_type,
184-
final Socket a_socket) {}
185-
186-
@Override
187-
public void checkClientTrusted(X509Certificate[] chain, String authType,
188-
SSLEngine engine) throws CertificateException {
189-
190-
}
191-
192-
public void checkServerTrusted(
193-
final X509Certificate[] a_certificates,
194-
final String a_auth_type,
195-
final SSLEngine a_engine) {}
196-
}
197-
},
198-
null);
199-
return HttpClient.newBuilder()
200-
.sslContext(sslContext)
201-
.build();
202-
} catch (NoSuchAlgorithmException | KeyManagementException e) {
203-
throw new JenvtestException(e);
204-
}
87+
public void waitUntilReady() {
88+
new ProcessReadinessChecker(apiServerPort, "readyz", "Kube API Server", true).waitUntilReady();
20589
}
20690

20791
public void stopApiServer() {

0 commit comments

Comments
 (0)