|
18 | 18 |
|
19 | 19 | import static com.google.common.truth.Truth.assertThat; |
20 | 20 | import static com.google.common.truth.Truth.assertWithMessage; |
| 21 | +import static io.grpc.StatusMatcher.statusHasCode; |
21 | 22 | import static org.mockito.ArgumentMatchers.any; |
22 | 23 | import static org.mockito.ArgumentMatchers.argThat; |
23 | 24 | import static org.mockito.ArgumentMatchers.eq; |
|
42 | 43 | import com.google.protobuf.Duration; |
43 | 44 | import com.google.protobuf.InvalidProtocolBufferException; |
44 | 45 | import com.google.protobuf.Message; |
| 46 | +import com.google.protobuf.StringValue; |
45 | 47 | import com.google.protobuf.UInt32Value; |
46 | 48 | import com.google.protobuf.util.Durations; |
47 | 49 | import io.envoyproxy.envoy.config.cluster.v3.OutlierDetection; |
|
59 | 61 | import io.grpc.Status; |
60 | 62 | import io.grpc.Status.Code; |
61 | 63 | import io.grpc.StatusOr; |
| 64 | +import io.grpc.StatusOrMatcher; |
62 | 65 | import io.grpc.inprocess.InProcessChannelBuilder; |
63 | 66 | import io.grpc.inprocess.InProcessServerBuilder; |
64 | 67 | import io.grpc.internal.BackoffPolicy; |
|
111 | 114 | import java.util.Collections; |
112 | 115 | import java.util.List; |
113 | 116 | import java.util.Map; |
| 117 | +import java.util.Objects; |
114 | 118 | import java.util.Queue; |
115 | 119 | import java.util.concurrent.BlockingDeque; |
116 | 120 | import java.util.concurrent.CountDownLatch; |
@@ -278,6 +282,8 @@ public long currentTimeNanos() { |
278 | 282 | @Mock |
279 | 283 | private ResourceWatcher<EdsUpdate> edsResourceWatcher; |
280 | 284 | @Mock |
| 285 | + private ResourceWatcher<StringUpdate> stringResourceWatcher; |
| 286 | + @Mock |
281 | 287 | private XdsClientMetricReporter xdsClientMetricReporter; |
282 | 288 | @Mock |
283 | 289 | private ServerConnectionCallback serverConnectionCallback; |
@@ -667,6 +673,58 @@ private void verifyServerConnection(int times, boolean isConnected, String xdsSe |
667 | 673 | eq(xdsServer)); |
668 | 674 | } |
669 | 675 |
|
| 676 | + @Test |
| 677 | + public void doParse_returnsSuccessfully() { |
| 678 | + XdsStringResource resourceType = new XdsStringResource(); |
| 679 | + xdsClient.watchXdsResource( |
| 680 | + resourceType, "resource1", stringResourceWatcher, MoreExecutors.directExecutor()); |
| 681 | + DiscoveryRpcCall call = resourceDiscoveryCalls.poll(); |
| 682 | + |
| 683 | + Any resource = Any.pack(StringValue.newBuilder().setValue("resource1").build()); |
| 684 | + call.sendResponse(resourceType, resource, VERSION_1, "0000"); |
| 685 | + verify(stringResourceWatcher).onResourceChanged(argThat(StatusOrMatcher.hasValue( |
| 686 | + (StringUpdate arg) -> new StringUpdate("resource1").equals(arg)))); |
| 687 | + } |
| 688 | + |
| 689 | + @Test |
| 690 | + public void doParse_throwsResourceInvalidException_resourceInvalid() { |
| 691 | + XdsStringResource resourceType = new XdsStringResource() { |
| 692 | + @Override |
| 693 | + protected StringUpdate doParse(Args args, Message unpackedMessage) |
| 694 | + throws ResourceInvalidException { |
| 695 | + throw new ResourceInvalidException("some bad input"); |
| 696 | + } |
| 697 | + }; |
| 698 | + xdsClient.watchXdsResource( |
| 699 | + resourceType, "resource1", stringResourceWatcher, MoreExecutors.directExecutor()); |
| 700 | + DiscoveryRpcCall call = resourceDiscoveryCalls.poll(); |
| 701 | + |
| 702 | + Any resource = Any.pack(StringValue.newBuilder().setValue("resource1").build()); |
| 703 | + call.sendResponse(resourceType, resource, VERSION_1, "0000"); |
| 704 | + verify(stringResourceWatcher).onResourceChanged(argThat(StatusOrMatcher.hasStatus( |
| 705 | + statusHasCode(Status.Code.UNAVAILABLE) |
| 706 | + .andDescriptionContains("validation error: some bad input")))); |
| 707 | + } |
| 708 | + |
| 709 | + @Test |
| 710 | + public void doParse_throwsError_resourceInvalid() throws Exception { |
| 711 | + XdsStringResource resourceType = new XdsStringResource() { |
| 712 | + @Override |
| 713 | + protected StringUpdate doParse(Args args, Message unpackedMessage) { |
| 714 | + throw new AssertionError("something bad happened"); |
| 715 | + } |
| 716 | + }; |
| 717 | + xdsClient.watchXdsResource( |
| 718 | + resourceType, "resource1", stringResourceWatcher, MoreExecutors.directExecutor()); |
| 719 | + DiscoveryRpcCall call = resourceDiscoveryCalls.poll(); |
| 720 | + |
| 721 | + Any resource = Any.pack(StringValue.newBuilder().setValue("resource1").build()); |
| 722 | + call.sendResponse(resourceType, resource, VERSION_1, "0000"); |
| 723 | + verify(stringResourceWatcher).onResourceChanged(argThat(StatusOrMatcher.hasStatus( |
| 724 | + statusHasCode(Status.Code.UNAVAILABLE) |
| 725 | + .andDescriptionContains("unexpected error: AssertionError: something bad happened")))); |
| 726 | + } |
| 727 | + |
670 | 728 | @Test |
671 | 729 | public void ldsResourceNotFound() { |
672 | 730 | DiscoveryRpcCall call = startResourceWatcher(XdsListenerResource.getInstance(), LDS_RESOURCE, |
@@ -5318,4 +5376,70 @@ protected abstract Message buildHttpConnectionManagerFilter( |
5318 | 5376 |
|
5319 | 5377 | protected abstract Message buildTerminalFilter(); |
5320 | 5378 | } |
| 5379 | + |
| 5380 | + private static class XdsStringResource extends XdsResourceType<StringUpdate> { |
| 5381 | + @Override |
| 5382 | + @SuppressWarnings("unchecked") |
| 5383 | + protected Class<? extends com.google.protobuf.Message> unpackedClassName() { |
| 5384 | + return StringValue.class; |
| 5385 | + } |
| 5386 | + |
| 5387 | + @Override |
| 5388 | + public String typeName() { |
| 5389 | + return "EMPTY"; |
| 5390 | + } |
| 5391 | + |
| 5392 | + @Override |
| 5393 | + public String typeUrl() { |
| 5394 | + return "type.googleapis.com/google.protobuf.StringValue"; |
| 5395 | + } |
| 5396 | + |
| 5397 | + @Override |
| 5398 | + public boolean shouldRetrieveResourceKeysForArgs() { |
| 5399 | + return false; |
| 5400 | + } |
| 5401 | + |
| 5402 | + @Override |
| 5403 | + protected boolean isFullStateOfTheWorld() { |
| 5404 | + return false; |
| 5405 | + } |
| 5406 | + |
| 5407 | + @Override |
| 5408 | + @Nullable |
| 5409 | + protected String extractResourceName(Message unpackedResource) { |
| 5410 | + if (!(unpackedResource instanceof StringValue)) { |
| 5411 | + return null; |
| 5412 | + } |
| 5413 | + return ((StringValue) unpackedResource).getValue(); |
| 5414 | + } |
| 5415 | + |
| 5416 | + @Override |
| 5417 | + protected StringUpdate doParse(Args args, Message unpackedMessage) |
| 5418 | + throws ResourceInvalidException { |
| 5419 | + return new StringUpdate(((StringValue) unpackedMessage).getValue()); |
| 5420 | + } |
| 5421 | + } |
| 5422 | + |
| 5423 | + private static final class StringUpdate implements ResourceUpdate { |
| 5424 | + @SuppressWarnings("UnusedVariable") |
| 5425 | + public final String value; |
| 5426 | + |
| 5427 | + public StringUpdate(String value) { |
| 5428 | + this.value = value; |
| 5429 | + } |
| 5430 | + |
| 5431 | + @Override |
| 5432 | + public boolean equals(Object o) { |
| 5433 | + if (!(o instanceof StringUpdate)) { |
| 5434 | + return false; |
| 5435 | + } |
| 5436 | + StringUpdate that = (StringUpdate) o; |
| 5437 | + return Objects.equals(this.value, that.value); |
| 5438 | + } |
| 5439 | + |
| 5440 | + @Override |
| 5441 | + public int hashCode() { |
| 5442 | + return Objects.hash(value); |
| 5443 | + } |
| 5444 | + } |
5321 | 5445 | } |
0 commit comments