xref: /aosp_15_r20/external/grpc-grpc-java/xds/src/test/java/io/grpc/xds/XdsServerBuilderTest.java (revision e07d83d3ffcef9ecfc9f7f475418ec639ff0e5fe)
1 /*
2  * Copyright 2019 The gRPC Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.grpc.xds;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static org.junit.Assert.fail;
21 import static org.mockito.Mockito.any;
22 import static org.mockito.Mockito.mock;
23 import static org.mockito.Mockito.never;
24 import static org.mockito.Mockito.reset;
25 import static org.mockito.Mockito.times;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
28 
29 import com.google.common.util.concurrent.SettableFuture;
30 import io.grpc.BindableService;
31 import io.grpc.InsecureServerCredentials;
32 import io.grpc.ServerServiceDefinition;
33 import io.grpc.Status;
34 import io.grpc.StatusException;
35 import io.grpc.testing.GrpcCleanupRule;
36 import io.grpc.xds.XdsServerTestHelper.FakeXdsClient;
37 import io.grpc.xds.XdsServerTestHelper.FakeXdsClientPoolFactory;
38 import io.grpc.xds.internal.security.CommonTlsContextTestsUtil;
39 import java.io.IOException;
40 import java.net.InetSocketAddress;
41 import java.net.ServerSocket;
42 import java.net.SocketAddress;
43 import java.util.HashMap;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.concurrent.ExecutionException;
47 import java.util.concurrent.Executors;
48 import java.util.concurrent.Future;
49 import java.util.concurrent.TimeUnit;
50 import java.util.concurrent.TimeoutException;
51 import org.junit.Rule;
52 import org.junit.Test;
53 import org.junit.runner.RunWith;
54 import org.junit.runners.JUnit4;
55 import org.mockito.ArgumentCaptor;
56 
57 // TODO (zivy@): move certain tests down to XdsServerWrapperTest or to XdsSecurityClientServerTest
58 /**
59  * Unit tests for {@link XdsServerBuilder}.
60  */
61 @RunWith(JUnit4.class)
62 public class XdsServerBuilderTest {
63 
64   @Rule public final GrpcCleanupRule cleanupRule = new GrpcCleanupRule();
65   private XdsServerBuilder builder;
66   private XdsServerWrapper xdsServer;
67   private int port;
68   private TlsContextManager tlsContextManager;
69   private FakeXdsClient xdsClient = new FakeXdsClient();
70   private FakeXdsClientPoolFactory xdsClientPoolFactory = new FakeXdsClientPoolFactory(xdsClient);
71 
buildServer(XdsServerBuilder.XdsServingStatusListener xdsServingStatusListener)72   private void buildServer(XdsServerBuilder.XdsServingStatusListener xdsServingStatusListener)
73       throws IOException {
74     buildBuilder(xdsServingStatusListener);
75     xdsServer = cleanupRule.register((XdsServerWrapper) builder.build());
76   }
77 
buildBuilder(XdsServerBuilder.XdsServingStatusListener xdsServingStatusListener)78   private void buildBuilder(XdsServerBuilder.XdsServingStatusListener xdsServingStatusListener)
79       throws IOException {
80     builder =
81         XdsServerBuilder.forPort(
82             port, XdsServerCredentials.create(InsecureServerCredentials.create()));
83     builder.xdsClientPoolFactory(xdsClientPoolFactory);
84     if (xdsServingStatusListener != null) {
85       builder.xdsServingStatusListener(xdsServingStatusListener);
86     }
87     tlsContextManager = mock(TlsContextManager.class);
88   }
89 
verifyServer( Future<Throwable> future, XdsServerBuilder.XdsServingStatusListener mockXdsServingStatusListener, Status notServingStatus)90   private void verifyServer(
91       Future<Throwable> future,
92       XdsServerBuilder.XdsServingStatusListener mockXdsServingStatusListener,
93       Status notServingStatus)
94       throws InterruptedException, ExecutionException, TimeoutException {
95     if (future != null) {
96       Throwable exception = future.get(5, TimeUnit.SECONDS);
97       assertThat(exception).isNull();
98     }
99     List<? extends SocketAddress> list = xdsServer.getListenSockets();
100     assertThat(list).hasSize(1);
101     InetSocketAddress socketAddress = (InetSocketAddress) list.get(0);
102     assertThat(socketAddress.getAddress().isAnyLocalAddress()).isTrue();
103     assertThat(socketAddress.getPort()).isGreaterThan(-1);
104     if (mockXdsServingStatusListener != null) {
105       if (notServingStatus != null) {
106         ArgumentCaptor<Throwable> argCaptor = ArgumentCaptor.forClass(Throwable.class);
107         verify(mockXdsServingStatusListener, times(1)).onNotServing(argCaptor.capture());
108         Throwable throwable = argCaptor.getValue();
109         assertThat(throwable).isInstanceOf(StatusException.class);
110         assertThat(((StatusException) throwable).getStatus()).isEqualTo(notServingStatus);
111       } else {
112         verify(mockXdsServingStatusListener, never()).onNotServing(any(Throwable.class));
113         verify(mockXdsServingStatusListener, times(1)).onServing();
114       }
115     }
116   }
117 
verifyShutdown()118   private void verifyShutdown() throws InterruptedException {
119     xdsServer.shutdown();
120     xdsServer.awaitTermination(500L, TimeUnit.MILLISECONDS);
121     assertThat(xdsClient.isShutDown()).isTrue();
122   }
123 
startServerAsync()124   private Future<Throwable> startServerAsync() throws
125           InterruptedException, TimeoutException, ExecutionException {
126     final SettableFuture<Throwable> settableFuture = SettableFuture.create();
127     Executors.newSingleThreadExecutor().execute(new Runnable() {
128       @Override
129       public void run() {
130         try {
131           xdsServer.start();
132           settableFuture.set(null);
133         } catch (Throwable e) {
134           settableFuture.set(e);
135         }
136       }
137     });
138     xdsClient.ldsResource.get(5000, TimeUnit.MILLISECONDS);
139     return settableFuture;
140   }
141 
142   @Test
xdsServerStartAndShutdown()143   public void xdsServerStartAndShutdown()
144       throws IOException, InterruptedException, TimeoutException, ExecutionException {
145     buildServer(null);
146     Future<Throwable> future = startServerAsync();
147     XdsServerTestHelper.generateListenerUpdate(
148         xdsClient,
149         CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"),
150         tlsContextManager);
151     verifyServer(future, null, null);
152     verifyShutdown();
153   }
154 
155   @Test
xdsServerRestartAfterListenerUpdate()156   public void xdsServerRestartAfterListenerUpdate()
157           throws IOException, InterruptedException, TimeoutException, ExecutionException {
158     buildServer(null);
159     Future<Throwable> future = startServerAsync();
160     XdsServerTestHelper.generateListenerUpdate(
161             xdsClient,
162             CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"),
163             tlsContextManager);
164     try {
165       xdsServer.start();
166       fail("expected exception");
167     } catch (IllegalStateException expected) {
168       assertThat(expected).hasMessageThat().contains("Already started");
169     }
170     verifyServer(future,null, null);
171   }
172 
173   @Test
xdsServerStartAndShutdownWithXdsServingStatusListener()174   public void xdsServerStartAndShutdownWithXdsServingStatusListener()
175       throws IOException, InterruptedException, TimeoutException, ExecutionException {
176     XdsServerBuilder.XdsServingStatusListener mockXdsServingStatusListener =
177         mock(XdsServerBuilder.XdsServingStatusListener.class);
178     buildServer(mockXdsServingStatusListener);
179     Future<Throwable> future = startServerAsync();
180     XdsServerTestHelper.generateListenerUpdate(
181         xdsClient,
182         CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"),
183             tlsContextManager);
184     verifyServer(future, mockXdsServingStatusListener, null);
185   }
186 
187   @Test
xdsServer_discoverState()188   public void xdsServer_discoverState() throws Exception {
189     XdsServerBuilder.XdsServingStatusListener mockXdsServingStatusListener =
190         mock(XdsServerBuilder.XdsServingStatusListener.class);
191     buildServer(mockXdsServingStatusListener);
192     Future<Throwable> future = startServerAsync();
193     XdsServerTestHelper.generateListenerUpdate(
194             xdsClient,
195             CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"),
196             tlsContextManager);
197     future.get(5000, TimeUnit.MILLISECONDS);
198     xdsClient.ldsWatcher.onError(Status.ABORTED);
199     verify(mockXdsServingStatusListener, never()).onNotServing(any(StatusException.class));
200     reset(mockXdsServingStatusListener);
201     xdsClient.ldsWatcher.onError(Status.CANCELLED);
202     verify(mockXdsServingStatusListener, never()).onNotServing(any(StatusException.class));
203     reset(mockXdsServingStatusListener);
204     xdsClient.ldsWatcher.onResourceDoesNotExist("not found error");
205     verify(mockXdsServingStatusListener).onNotServing(any(StatusException.class));
206     reset(mockXdsServingStatusListener);
207     XdsServerTestHelper.generateListenerUpdate(
208         xdsClient,
209         CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"),
210             tlsContextManager);
211     verifyServer(null, mockXdsServingStatusListener, null);
212   }
213 
214   @Test
xdsServer_startError()215   public void xdsServer_startError()
216       throws IOException, InterruptedException, TimeoutException, ExecutionException {
217     XdsServerBuilder.XdsServingStatusListener mockXdsServingStatusListener =
218         mock(XdsServerBuilder.XdsServingStatusListener.class);
219     ServerSocket serverSocket = new ServerSocket(0);
220     port = serverSocket.getLocalPort();
221     buildServer(mockXdsServingStatusListener);
222     Future<Throwable> future = startServerAsync();
223     // create port conflict for start to fail
224     XdsServerTestHelper.generateListenerUpdate(
225         xdsClient,
226         CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"),
227             tlsContextManager);
228     Throwable exception = future.get(5, TimeUnit.SECONDS);
229     assertThat(exception).isInstanceOf(IOException.class);
230     assertThat(exception).hasMessageThat().contains("Failed to bind");
231     verify(mockXdsServingStatusListener, never()).onNotServing(any(Throwable.class));
232     serverSocket.close();
233   }
234 
235   @Test
xdsServerStartSecondUpdateAndError()236   public void xdsServerStartSecondUpdateAndError()
237       throws IOException, InterruptedException, TimeoutException, ExecutionException {
238     XdsServerBuilder.XdsServingStatusListener mockXdsServingStatusListener =
239         mock(XdsServerBuilder.XdsServingStatusListener.class);
240     buildServer(mockXdsServingStatusListener);
241     Future<Throwable> future = startServerAsync();
242     XdsServerTestHelper.generateListenerUpdate(
243         xdsClient,
244         CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"),
245             tlsContextManager);
246     XdsServerTestHelper.generateListenerUpdate(
247         xdsClient,
248         CommonTlsContextTestsUtil.buildTestInternalDownstreamTlsContext("CERT1", "VA1"),
249             tlsContextManager);
250     verify(mockXdsServingStatusListener, never()).onNotServing(any(Throwable.class));
251     verifyServer(future, mockXdsServingStatusListener, null);
252     xdsClient.ldsWatcher.onError(Status.ABORTED);
253     verifyServer(null, mockXdsServingStatusListener, null);
254   }
255 
256   @Test
xdsServer_2ndBuild_expectException()257   public void xdsServer_2ndBuild_expectException() throws IOException {
258     XdsServerBuilder.XdsServingStatusListener mockXdsServingStatusListener =
259         mock(XdsServerBuilder.XdsServingStatusListener.class);
260     buildServer(mockXdsServingStatusListener);
261     try {
262       builder.build();
263       fail("exception expected");
264     } catch (IllegalStateException expected) {
265       assertThat(expected).hasMessageThat().contains("Server already built!");
266     }
267   }
268 
269   @Test
xdsServer_2ndSetter_expectException()270   public void xdsServer_2ndSetter_expectException() throws IOException {
271     XdsServerBuilder.XdsServingStatusListener mockXdsServingStatusListener =
272         mock(XdsServerBuilder.XdsServingStatusListener.class);
273     buildBuilder(mockXdsServingStatusListener);
274     BindableService mockBindableService = mock(BindableService.class);
275     ServerServiceDefinition serverServiceDefinition = io.grpc.ServerServiceDefinition
276         .builder("mock").build();
277     when(mockBindableService.bindService()).thenReturn(serverServiceDefinition);
278     builder.addService(mockBindableService);
279     xdsServer = cleanupRule.register((XdsServerWrapper) builder.build());
280     try {
281       builder.addService(mock(BindableService.class));
282       fail("exception expected");
283     } catch (IllegalStateException expected) {
284       assertThat(expected).hasMessageThat().contains("Server already built!");
285     }
286   }
287 
288   @Test
drainGraceTime_negativeThrows()289   public void drainGraceTime_negativeThrows() throws IOException {
290     buildBuilder(null);
291     try {
292       builder.drainGraceTime(-1, TimeUnit.SECONDS);
293       fail("exception expected");
294     } catch (IllegalArgumentException expected) {
295       assertThat(expected).hasMessageThat().contains("drain grace time");
296     }
297   }
298 
299   @Test
testOverrideBootstrap()300   public void testOverrideBootstrap() throws Exception {
301     Map<String, Object> b = new HashMap<>();
302     buildBuilder(null);
303     builder.overrideBootstrapForTest(b);
304     assertThat(xdsClientPoolFactory.savedBootstrap).isEqualTo(b);
305   }
306 }
307