Skip to content

Commit e5aa8b9

Browse files
committed
WIP
1 parent c015e69 commit e5aa8b9

File tree

8 files changed

+616
-214
lines changed

8 files changed

+616
-214
lines changed

build.sbt

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ lazy val k8sDnsNameResolver = project.in(file("k8s-dns-name-resolver"))
7070
),
7171
)
7272

73-
// TODO: WIP
73+
// TODO: WIP describe why the f it needs docker plugin and such
7474
lazy val k8sDnsNameResolverIt = project.in(file("k8s-dns-name-resolver-it"))
7575
.enablePlugins(JavaAppPackaging, DockerPlugin)
7676
.settings(
@@ -83,22 +83,25 @@ lazy val k8sDnsNameResolverIt = project.in(file("k8s-dns-name-resolver-it"))
8383
),
8484
dockerBaseImage := "amazoncorretto:17-alpine",
8585
dockerCommands ++= Seq(
86-
// TODO
86+
// root rights are needed to install additional packages, and also test client needs it
87+
// to manipulate its DNS settings
8788
Cmd("USER", "root"),
88-
ExecCmd("RUN", "apk", "add", "--no-cache", "bash"),
89-
ExecCmd("RUN", "apk", "add", "--no-cache", "coredns", "bind-tools"),
89+
// TODO: WIP do we really need bind-tools?
90+
ExecCmd("RUN", "apk", "add", "--no-cache", "bash", "lsof", "coredns", "bind-tools"),
9091
),
91-
dockerExposedPorts ++= Seq(9000),
92+
dockerExposedPorts := Seq(9000), // Should match the test app GRPC server port.
93+
// The int test here needs the test app docker container staged before running the code.
94+
// It's then used in docker compose inside testcontainers.
9295
test := {
9396
(Docker / stage).value
9497
(Test / test).value
9598
},
9699
libraryDependencies ++= Seq(
100+
Slf4j.simple,
97101
"io.grpc" % "grpc-netty" % scalapb.compiler.Version.grpcJavaVersion,
98102
"com.thesamet.scalapb" %% "scalapb-runtime-grpc" % scalapb.compiler.Version.scalapbVersion,
99-
Slf4j.simple,
100-
"org.testcontainers" % "testcontainers" % "2.0.2" % Test,
101-
"org.testcontainers" % "testcontainers-junit-jupiter" % "2.0.2" % Test,
103+
Testcontainers.core % Test,
104+
Testcontainers.junitJupiter % Test,
102105
),
103106
).dependsOn(
104107
k8sDnsNameResolver,
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Configure slf4j-simple to have concise output for tests
2+
# Supported settings: https://www.slf4j.org/api/org/slf4j/simple/SimpleLogger.html
3+
org.slf4j.simpleLogger.showDateTime=true
4+
org.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss.SSS
5+
org.slf4j.simpleLogger.showThreadName=false
6+
org.slf4j.simpleLogger.showShortLogName=true
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package com.evolution.jgrpc.tools.k8sdns.it
2+
3+
import java.nio.file.{Path, Paths}
4+
5+
// TODO: WIP rename ItApp to TestApp
6+
/**
7+
* Common things shared between the `K8sDnsNameResolver` integration test code and the
8+
* test service code
9+
*
10+
* @see
11+
* [[ItApp]]
12+
*/
13+
object ItAppShared {
14+
15+
/**
16+
* [[ItApp]] GRPC server port
17+
*/
18+
val ServerPort: Int = 9000
19+
20+
/**
21+
* Docker compose service names for [[ItApp]] containers.
22+
*
23+
* The names here should match the ones used in the
24+
* `src/test/resources/docker/compose-test.yml` file.
25+
*/
26+
object TestAppSvcNames {
27+
28+
/**
29+
* First [[ItApp]] service in a [[TestServer]] mode
30+
*/
31+
val Server1: String = "test-server1"
32+
33+
/**
34+
* Second [[ItApp]] service in a [[TestServer]] mode
35+
*/
36+
val Server2: String = "test-server2"
37+
38+
/**
39+
* [[ItApp]] service in a [[TestClient]] mode
40+
*/
41+
val Client: String = "test-client"
42+
}
43+
44+
/**
45+
* `K8sDnsNameResolver` integration test watches for these [[ItApp]] log messages in the
46+
* stdout.
47+
*/
48+
object TestAppSpecialLogMsgs {
49+
50+
/**
51+
* [[ItApp]] docker container has been started and ready to proceed with the test
52+
*/
53+
val Ready: String = "TEST CONTAINER READY"
54+
55+
/**
56+
* [[ItApp]] in the [[TestClient]] mode died prematurely, all the tests should be
57+
* aborted
58+
*/
59+
val ClientPrematureDeath: String = "TEST CLIENT PANIC"
60+
61+
/**
62+
* [[ItApp]] in the [[TestClient]] mode completed a requested test case successfully
63+
*
64+
* @see
65+
* [[TestClientControl]] for how to request a test case execution
66+
*/
67+
val ClientTestCaseSuccess: String = "TEST SUCCESS"
68+
69+
/**
70+
* [[ItApp]] in the [[TestClient]] mode ran a requested test case and got a failure
71+
*
72+
* @see
73+
* [[TestClientControl]] for how to request a test case execution
74+
*/
75+
val ClientTestCaseFailed: String = "TEST FAILED"
76+
}
77+
78+
/**
79+
* Defines the way to send commands to the [[ItApp]] container in the [[TestClient]]
80+
* mode:
81+
* - create an empty file in the [[CmdDirPath]] directory on the container - the name
82+
* of the file is the command name
83+
* - the [[TestClient]] code deletes the file and queues the command for execution
84+
* - commands are executed on the [[TestClient]] one-by-one
85+
* - monitor [[TestClient]] container stdout for the command progress - see
86+
* [[TestAppSpecialLogMsgs]]
87+
*
88+
* Currently supported commands:
89+
* - [[RunTestCaseCmdFileName]] for running [[TestClientTestCase]]
90+
*/
91+
object TestClientControl {
92+
93+
/**
94+
* Directory which [[ItApp]] in the [[TestClient]] mode uses for receiving commands
95+
*
96+
* @see
97+
* [[TestClientControl]]
98+
*/
99+
val CmdDirPath: Path = Paths.get("/tmp/test-client-control")
100+
101+
/**
102+
* [[TestClientControl]] command for running [[TestClientTestCase]].
103+
*/
104+
object RunTestCaseCmdFileName {
105+
private val fileNamePrefix = ".run-test-case-"
106+
107+
/**
108+
* Creates a [[TestClientControl]] command file name for running the given
109+
* [[TestClientTestCase]]
110+
*/
111+
def apply(testCase: TestClientTestCase): String = {
112+
s"$fileNamePrefix${ testCase.name }"
113+
}
114+
115+
/**
116+
* Matches [[TestClientControl]] command file name which runs a
117+
* [[TestClientTestCase]]
118+
*/
119+
def unapply(fileName: String): Option[TestClientTestCase] = {
120+
if (fileName.startsWith(fileNamePrefix)) {
121+
val testCaseName = fileName.drop(fileNamePrefix.length)
122+
TestClientTestCase.values.find(_.name == testCaseName)
123+
} else {
124+
None
125+
}
126+
}
127+
}
128+
}
129+
130+
/**
131+
* Test case to run on [[TestClient]].
132+
*
133+
* @see
134+
* [[TestClientControl.RunTestCaseCmdFileName]]
135+
*/
136+
sealed abstract class TestClientTestCase extends Product {
137+
final def name: String = productPrefix
138+
}
139+
object TestClientTestCase {
140+
val values: Vector[TestClientTestCase] = Vector(
141+
DiscoverNewPod,
142+
DnsFailureRecover,
143+
)
144+
145+
/**
146+
* [[TestClient]] test case verifying that `K8sDnsNameResolver` live pod discovery
147+
* works.
148+
*
149+
* Test steps overview:
150+
* - point the service host DNS records to one server container
151+
* - create a GRPC client, check that it sees only the first server
152+
* - add the second server to the DNS records
153+
* - check that after the configured reload TTL, the client sees both servers
154+
*/
155+
case object DiscoverNewPod extends TestClientTestCase
156+
157+
/**
158+
* [[TestClient]] test case verifying that `K8sDnsNameResolver` recovers after a DNS
159+
* call failure.
160+
*
161+
* Test steps overview:
162+
* - point the service host DNS records to one server container
163+
* - create a GRPC client, check that it sees only the first server
164+
* - stop the DNS server, wait until the client gets a DNS error
165+
* - start the DNS server back again, with 2 servers in the records
166+
* - check that after the configured reload TTL, the client sees both servers
167+
*/
168+
case object DnsFailureRecover extends TestClientTestCase
169+
}
170+
}

k8s-dns-name-resolver-it/src/main/scala/com/evolution/jgrpc/tools/k8sdns/it/K8sDnsItAppShared.scala

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)