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

Commit 73eebbb

Browse files
committed
startup wait
1 parent b2ebbe5 commit 73eebbb

File tree

6 files changed

+113
-14
lines changed

6 files changed

+113
-14
lines changed

devnotes.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11

2+
bins:
3+
https://github.com/etcd-io/etcd/releases/download/v3.4.24/etcd-v3.4.24-linux-amd64.tar.gz
4+
https://dl.k8s.io/v1.26.1/bin/linux/amd64/kube-apiserver
5+
26
Gen self signed for api server:
37
https://raymii.org/s/tutorials/OpenSSL_generate_self_signed_cert_with_Subject_Alternative_name_oneliner.html
48

@@ -15,7 +19,7 @@ run etcd:
1519

1620
run self signed:
1721

18-
./kube-apiserver --cert-dir /home/csviri/.jenvtest/ --etcd-servers http://0.0.0.0:2379 --authorization-mode RBAC --service-account-issuer https://localhost --service-account-signing-key-file /home/csviri/.jenvtest/apiserver.key --service-account-signing-key-file /home/csviri/.jenvtest/apiserver.key --service-account-key-file /home/csviri/Downloads/kubeapi/tempcerts/apiserver.key --service-account-issuer /home/csviri/.jenvtest/apiserver.cert --disable-admission-plugins ServiceAccount --client-ca-file /home/csviri/.jenvtest/client.crt
22+
./kube-apiserver --cert-dir /home/csviri/.jenvtest/ --etcd-servers http://0.0.0.0:2379 --authorization-mode RBAC --service-account-issuer https://localhost --service-account-signing-key-file /home/csviri/.jenvtest/apiserver.key --service-account-signing-key-file /home/csviri/.jenvtest/apiserver.key --service-account-key-file /home/csviri/.jenvtest/apiserver.key --service-account-issuer /home/csviri/.jenvtest/apiserver.cert --disable-admission-plugins ServiceAccount --client-ca-file /home/csviri/.jenvtest/client.crt
1923

2024
// ./kube-apiserver --cert-dir .
2125
// --etcd-servers http://0.0.0.0:2379

src/main/java/com/csviri/kubeapi/APIServer.java

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,22 @@
44
import org.slf4j.Logger;
55
import org.slf4j.LoggerFactory;
66

7-
import java.io.File;
8-
import java.io.IOException;
7+
import java.io.*;
8+
import java.util.Scanner;
99

1010
public class APIServer {
1111

1212
private static final Logger log = LoggerFactory.getLogger(APIServer.class);
13+
public static final int STARTUP_TIMEOUT = 10_000;
1314

1415
private final APIServerConfig config;
1516
private BinaryManager binaryManager;
1617
private CertManager certManager;
1718
private KubeConfigManager kubeConfigManager;
1819
private Process etcdProcess;
19-
private Process apiServerProcess;
20+
private volatile Process apiServerProcess;
21+
private Thread startupWaiter;
22+
private volatile boolean startedUpProperly;
2023

2124
public APIServer() {
2225
this(new APIServerConfig());
@@ -30,13 +33,14 @@ public APIServer(APIServerConfig config) {
3033
}
3134

3235
public void start() {
33-
log.debug("Stating. Using jenvtest dir: {}", config.getJenvtestDirectory());
36+
log.debug("Stating API Server. Using jenvtest dir: {}", config.getJenvtestDirectory());
3437
prepareLogDirectory();
3538
cleanEtcdData();
3639
startEtcd();
3740
startApiServer();
3841
kubeConfigManager.updateKubeConfig();
3942
waitUntilDefaultNamespaceCreated();
43+
log.info("API Server ready to use");
4044
}
4145

4246
private void prepareLogDirectory() {
@@ -58,7 +62,7 @@ public void stop() {
5862

5963
private void stopApiServer() {
6064
if (apiServerProcess != null) {
61-
apiServerProcess.destroy();
65+
apiServerProcess.destroyForcibly();
6266
}
6367
log.debug("API Server stopped");
6468
}
@@ -71,7 +75,6 @@ private void cleanEtcdData() {
7175
}
7276
}
7377

74-
7578
private void stopEtcd() {
7679
if (etcdProcess != null) {
7780
etcdProcess.destroy();
@@ -80,7 +83,14 @@ private void stopEtcd() {
8083
}
8184

8285
private void waitUntilDefaultNamespaceCreated() {
83-
// todo
86+
try {
87+
startupWaiter.join(STARTUP_TIMEOUT);
88+
if (!startedUpProperly) {
89+
throw new KubeApiException("Something went wrong starting up KubeApi server. Check the logs");
90+
}
91+
} catch (InterruptedException e) {
92+
throw new KubeApiException(e);
93+
}
8494
}
8595

8696
// todo detect if process not started up correctly
@@ -96,6 +106,7 @@ private void startEtcd() {
96106
etcdProcess = new ProcessBuilder(etcdBinary.getAbsolutePath(),
97107
"--listen-client-urls=http://0.0.0.0:2379",
98108
"--advertise-client-urls=http://0.0.0.0:2379")
109+
// todo log to a different logger on debug level
99110
.redirectOutput(logsFile)
100111
.redirectError(logsFile)
101112
.start();
@@ -126,15 +137,34 @@ private void startApiServer() {
126137
"--client-ca-file", certManager.getClientCertPath(),
127138
"--service-cluster-ip-range", "10.0.0.0/24",
128139
"--allow-privileged"
129-
)
130-
.redirectOutput(logsFile)
131-
.redirectError(logsFile)
140+
)
132141
.start();
133-
// todo detect premature termination
142+
143+
addStartupReadyHandler();
144+
// todo detect premature termination
134145
// apiServerProcess.onExit()
135146
log.debug("API Server started");
136147
} catch (IOException e) {
137148
throw new RuntimeException(e);
138149
}
139150
}
151+
152+
private void addStartupReadyHandler() {
153+
// alternative would be to use health checks? https://kubernetes.io/docs/reference/using-api/health-checks/
154+
// waits until fully started, otherwise default namespace might be missing
155+
this.startupWaiter = new Thread(() -> {
156+
// todo the scanner is not closed
157+
Scanner sc = new Scanner(apiServerProcess.getErrorStream());
158+
while (sc.hasNextLine()) {
159+
String line = sc.nextLine();
160+
// if (line.contains("Caches are synced")) {
161+
if (line.contains("all system priority classes are created successfully or already exist")) {
162+
startedUpProperly = true;
163+
return;
164+
}
165+
}
166+
});
167+
this.startupWaiter.start();
168+
}
169+
140170
}
Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,22 @@
11
package com.csviri.kubeapi.junit;
22

3-
public class APIServerExtension {
3+
import com.csviri.kubeapi.APIServer;
4+
import org.junit.jupiter.api.extension.AfterAllCallback;
5+
import org.junit.jupiter.api.extension.BeforeAllCallback;
6+
import org.junit.jupiter.api.extension.ExtensionContext;
7+
8+
public class APIServerExtension implements BeforeAllCallback, AfterAllCallback {
9+
10+
private APIServer apiServer;
11+
12+
@Override
13+
public void beforeAll(ExtensionContext extensionContext) throws Exception {
14+
apiServer = new APIServer();
15+
apiServer.start();
16+
}
17+
18+
@Override
19+
public void afterAll(ExtensionContext extensionContext) throws Exception {
20+
apiServer.stop();
21+
}
422
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
11
package com.csviri.kubeapi.junit;
22

3+
import org.junit.jupiter.api.extension.ExtendWith;
4+
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.Target;
7+
8+
import static java.lang.annotation.ElementType.*;
9+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
10+
11+
@ExtendWith(APIServerExtension.class)
12+
@Target({ TYPE, ANNOTATION_TYPE })
13+
@Retention(RUNTIME)
314
public @interface EnableAPIServer {
415
}

src/test/java/com/csviri/kubeapi/ApiServerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ void sanityTest() throws InterruptedException {
1717
var kubeApi = new APIServer();
1818
try {
1919
kubeApi.start();
20-
Thread.sleep(3000);
20+
2121
// todo cleanup before start
2222
var client = new KubernetesClientBuilder().build();
2323
client.resource(configMap()).createOrReplace();
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.csviri.kubeapi;
2+
3+
import com.csviri.kubeapi.junit.EnableAPIServer;
4+
import io.fabric8.kubernetes.api.model.ConfigMap;
5+
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
6+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
7+
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
8+
import org.junit.jupiter.api.Test;
9+
10+
import java.util.Map;
11+
12+
import static org.assertj.core.api.Assertions.assertThat;
13+
14+
@EnableAPIServer
15+
public class JUnitExtensionTest {
16+
17+
@Test
18+
public void testCommunication() {
19+
var client = new KubernetesClientBuilder().build();
20+
client.resource(configMap()).createOrReplace();
21+
var cm = client.resource(configMap()).get();
22+
23+
assertThat(cm).isNotNull();
24+
}
25+
26+
private ConfigMap configMap() {
27+
return new ConfigMapBuilder()
28+
.withMetadata(new ObjectMetaBuilder()
29+
.withName("test1")
30+
.withNamespace("default")
31+
.build())
32+
.withData(Map.of("key","data"))
33+
.build();
34+
}
35+
36+
}

0 commit comments

Comments
 (0)