@@ -63,6 +63,8 @@ private[it] final class TestClient {
6363 testCaseDiscoverNewPod(fixture)
6464 case TestClientTestCase .DnsFailureRecover =>
6565 testCaseDnsFailureRecover(fixture)
66+ case TestClientTestCase .ResolveShortDomainName =>
67+ testCaseResolveShortDomainName(fixture)
6668 }
6769 }
6870
@@ -86,22 +88,22 @@ private[it] final class TestClient {
8688 private def testCaseDiscoverNewPod (fixture : Fixture ): Unit = {
8789 fixture.coreDns.ensureStarted(serviceIps = Set (fixture.srv1Ip))
8890
89- withRoundRobinLbClient { client =>
90- callHost2TimesAssertServerIds (client, expectedServerIds = Set (1 ))
91+ withRoundRobinLbClient() { client =>
92+ callHostManyTimesAssertServerIds (client, expectedServerIds = Set (1 ))
9193
9294 fixture.coreDns.setServiceIps(Set (fixture.srv1Ip, fixture.srv2Ip))
9395
9496 sleepUntilClientGetsDnsUpdate()
9597
96- callHost2TimesAssertServerIds (client, expectedServerIds = Set (1 , 2 ))
98+ callHostManyTimesAssertServerIds (client, expectedServerIds = Set (1 , 2 ))
9799 }
98100 }
99101
100102 private def testCaseDnsFailureRecover (fixture : Fixture ): Unit = {
101103 fixture.coreDns.ensureStarted(serviceIps = Set (fixture.srv1Ip))
102104
103- withRoundRobinLbClient { client =>
104- callHost2TimesAssertServerIds (client, expectedServerIds = Set (1 ))
105+ withRoundRobinLbClient() { client =>
106+ callHostManyTimesAssertServerIds (client, expectedServerIds = Set (1 ))
105107
106108 fixture.coreDns.ensureStopped()
107109
@@ -111,7 +113,15 @@ private[it] final class TestClient {
111113
112114 sleepUntilClientGetsDnsUpdate()
113115
114- callHost2TimesAssertServerIds(client, expectedServerIds = Set (1 , 2 ))
116+ callHostManyTimesAssertServerIds(client, expectedServerIds = Set (1 , 2 ))
117+ }
118+ }
119+
120+ private def testCaseResolveShortDomainName (fixture : Fixture ): Unit = {
121+ fixture.coreDns.ensureStarted(serviceIps = Set (fixture.srv1Ip, fixture.srv2Ip))
122+
123+ withRoundRobinLbClient(targetHostname = svcHostnameShort) { client =>
124+ callHostManyTimesAssertServerIds(client, expectedServerIds = Set (1 , 2 ))
115125 }
116126 }
117127
@@ -125,21 +135,44 @@ private[it] final class TestClient {
125135 Thread .sleep(sleepIntervalSeconds.toLong * 1000 )
126136 }
127137
128- private def callHost2TimesAssertServerIds (
138+ private def callHostManyTimesAssertServerIds (
129139 client : TestSvcBlockingStub ,
130140 expectedServerIds : Set [Int ],
131141 ): Unit = {
132- val actualServerIds = 0 .until(2 ).map { _ =>
142+ require(expectedServerIds.subsetOf(allServerIds))
143+
144+ var observedServerIdsVec : Vector [Int ] = Vector .fill(allServerIds.size) {
133145 client.getId(GetIdRequest ()).id
134- }.toSet
135- if (actualServerIds != expectedServerIds) {
136- sys.error(s " GRPC client observed server IDs $actualServerIds, expected $expectedServerIds" )
146+ }
147+
148+ // When the client is just establishing connections, sometimes calling it multiple times
149+ // doesn't give the round-robin call picture (1, 2, 1, 2,...),
150+ // but it appears as if the client
151+ // routes all the calls to the first opened connection,
152+ // while waiting for the rest to be fully ready.
153+ // So if we haven't observed all the servers yet, let's wait a bit and call again.
154+ // This helps to recover such cases.
155+ if (observedServerIdsVec.toSet.size < allServerIds.size) {
156+ Thread .sleep(1000L )
157+ observedServerIdsVec = observedServerIdsVec ++ Vector .fill(allServerIds.size) {
158+ client.getId(GetIdRequest ()).id
159+ }
160+ }
161+
162+ val observedServerIds = observedServerIdsVec.toSet
163+ if (observedServerIds != expectedServerIds) {
164+ sys.error(s " GRPC client expected server IDs $expectedServerIds, " +
165+ s " observed $observedServerIds (received in order: $observedServerIdsVec) " )
137166 }
138167 }
139168
140- private def withRoundRobinLbClient [T ](body : TestSvcBlockingStub => T ): T = {
169+ private def withRoundRobinLbClient [T ](
170+ targetHostname : String = svcHostname,
171+ )(
172+ body : TestSvcBlockingStub => T ,
173+ ): T = {
141174 val channel = NettyChannelBuilder
142- .forTarget(s " k8s-dns:// $svcHostname : ${ TestAppShared .ServerPort }" )
175+ .forTarget(s " k8s-dns:// $targetHostname : ${ TestAppShared .ServerPort }" )
143176 .usePlaintext()
144177 .defaultLoadBalancingPolicy(" round_robin" )
145178 .build()
@@ -156,7 +189,10 @@ private[it] final class TestClient {
156189}
157190
158191private object TestClient {
159- private val svcHostname : String = " svc.example.org"
192+ private val allServerIds = Set (1 , 2 )
193+ private val clusterHostnameSuffix = " svc.cluster.local"
194+ private val svcHostnameShort = " acme-grpc.acme"
195+ private val svcHostname = s " $svcHostnameShort. $clusterHostnameSuffix"
160196 private val resolveConfPath = " /etc/resolv.conf"
161197
162198 private val coreDnsCoreFilePath = " /etc/coredns/CoreFile"
@@ -178,6 +214,8 @@ private object TestClient {
178214 Paths .get(resolveConfPath),
179215 Vector (
180216 " nameserver 127.0.0.1" ,
217+ s " search $clusterHostnameSuffix" ,
218+ " options ndots:5" ,
181219 ).asJava,
182220 StandardOpenOption .TRUNCATE_EXISTING ,
183221 )
@@ -224,7 +262,7 @@ private object TestClient {
224262 private def writeCoreFile (): Unit = {
225263 Files .writeString(
226264 Paths .get(coreDnsCoreFilePath),
227- s """ $svcHostname {
265+ s """ . {
228266 | hosts $coreDnsHostsFilePath {
229267 | ttl $coreDnsHostsReloadIntervalSeconds
230268 | reload ${ coreDnsHostsReloadIntervalSeconds }s
0 commit comments