1 /* 2 * Copyright 2020 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 21 import com.google.common.collect.ImmutableList; 22 import com.google.common.collect.ImmutableMap; 23 import com.google.common.util.concurrent.SettableFuture; 24 import io.grpc.InsecureChannelCredentials; 25 import io.grpc.internal.ObjectPool; 26 import io.grpc.xds.Bootstrapper.BootstrapInfo; 27 import io.grpc.xds.EnvoyServerProtoData.ConnectionSourceType; 28 import io.grpc.xds.EnvoyServerProtoData.FilterChain; 29 import io.grpc.xds.EnvoyServerProtoData.Listener; 30 import io.grpc.xds.Filter.FilterConfig; 31 import io.grpc.xds.Filter.NamedFilterConfig; 32 import io.grpc.xds.VirtualHost.Route; 33 import io.grpc.xds.XdsListenerResource.LdsUpdate; 34 import io.grpc.xds.XdsRouteConfigureResource.RdsUpdate; 35 import java.util.ArrayList; 36 import java.util.Arrays; 37 import java.util.Collections; 38 import java.util.HashMap; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.concurrent.CountDownLatch; 42 import javax.annotation.Nullable; 43 44 /** 45 * Helper methods related to {@link XdsServerBuilder} and related classes. 46 */ 47 public class XdsServerTestHelper { 48 49 private static final String SERVER_URI = "trafficdirector.googleapis.com"; 50 private static final String NODE_ID = 51 "projects/42/networks/default/nodes/5c85b298-6f5b-4722-b74a-f7d1f0ccf5ad"; 52 private static final EnvoyProtoData.Node BOOTSTRAP_NODE = 53 EnvoyProtoData.Node.newBuilder().setId(NODE_ID).build(); 54 static final Bootstrapper.BootstrapInfo BOOTSTRAP_INFO = 55 Bootstrapper.BootstrapInfo.builder() 56 .servers(Arrays.asList( 57 Bootstrapper.ServerInfo.create( 58 SERVER_URI, InsecureChannelCredentials.create()))) 59 .node(BOOTSTRAP_NODE) 60 .serverListenerResourceNameTemplate("grpc/server?udpa.resource.listening_address=%s") 61 .build(); 62 generateListenerUpdate(FakeXdsClient xdsClient, EnvoyServerProtoData.DownstreamTlsContext tlsContext, TlsContextManager tlsContextManager)63 static void generateListenerUpdate(FakeXdsClient xdsClient, 64 EnvoyServerProtoData.DownstreamTlsContext tlsContext, 65 TlsContextManager tlsContextManager) { 66 EnvoyServerProtoData.Listener listener = buildTestListener("listener1", "10.1.2.3", 67 ImmutableList.of(), tlsContext, null, tlsContextManager); 68 LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); 69 xdsClient.deliverLdsUpdate(listenerUpdate); 70 } 71 generateListenerUpdate( FakeXdsClient xdsClient, ImmutableList<Integer> sourcePorts, EnvoyServerProtoData.DownstreamTlsContext tlsContext, EnvoyServerProtoData.DownstreamTlsContext tlsContextForDefaultFilterChain, TlsContextManager tlsContextManager)72 static void generateListenerUpdate( 73 FakeXdsClient xdsClient, ImmutableList<Integer> sourcePorts, 74 EnvoyServerProtoData.DownstreamTlsContext tlsContext, 75 EnvoyServerProtoData.DownstreamTlsContext tlsContextForDefaultFilterChain, 76 TlsContextManager tlsContextManager) { 77 EnvoyServerProtoData.Listener listener = buildTestListener("listener1", "10.1.2.3", sourcePorts, 78 tlsContext, tlsContextForDefaultFilterChain, tlsContextManager); 79 LdsUpdate listenerUpdate = LdsUpdate.forTcpListener(listener); 80 xdsClient.deliverLdsUpdate(listenerUpdate); 81 } 82 buildTestListener( String name, String address, ImmutableList<Integer> sourcePorts, EnvoyServerProtoData.DownstreamTlsContext tlsContext, EnvoyServerProtoData.DownstreamTlsContext tlsContextForDefaultFilterChain, TlsContextManager tlsContextManager)83 static EnvoyServerProtoData.Listener buildTestListener( 84 String name, String address, ImmutableList<Integer> sourcePorts, 85 EnvoyServerProtoData.DownstreamTlsContext tlsContext, 86 EnvoyServerProtoData.DownstreamTlsContext tlsContextForDefaultFilterChain, 87 TlsContextManager tlsContextManager) { 88 EnvoyServerProtoData.FilterChainMatch filterChainMatch1 = 89 EnvoyServerProtoData.FilterChainMatch.create( 90 0, 91 ImmutableList.of(), 92 ImmutableList.of(), 93 ImmutableList.of(), 94 ConnectionSourceType.ANY, 95 sourcePorts, 96 ImmutableList.of(), 97 ""); 98 EnvoyServerProtoData.FilterChainMatch defaultFilterChainMatch = 99 EnvoyServerProtoData.FilterChainMatch.create( 100 0, 101 ImmutableList.of(), 102 ImmutableList.of(), 103 ImmutableList.of(), 104 ConnectionSourceType.ANY, 105 ImmutableList.of(), 106 ImmutableList.of(), 107 ""); 108 VirtualHost virtualHost = 109 VirtualHost.create( 110 "virtual-host", Collections.singletonList("auth"), new ArrayList<Route>(), 111 ImmutableMap.<String, FilterConfig>of()); 112 HttpConnectionManager httpConnectionManager = HttpConnectionManager.forVirtualHosts( 113 0L, Collections.singletonList(virtualHost), new ArrayList<NamedFilterConfig>()); 114 EnvoyServerProtoData.FilterChain filterChain1 = EnvoyServerProtoData.FilterChain.create( 115 "filter-chain-foo", filterChainMatch1, httpConnectionManager, tlsContext, 116 tlsContextManager); 117 EnvoyServerProtoData.FilterChain defaultFilterChain = EnvoyServerProtoData.FilterChain.create( 118 "filter-chain-bar", defaultFilterChainMatch, httpConnectionManager, 119 tlsContextForDefaultFilterChain, tlsContextManager); 120 EnvoyServerProtoData.Listener listener = 121 EnvoyServerProtoData.Listener.create( 122 name, address, ImmutableList.of(filterChain1), defaultFilterChain); 123 return listener; 124 } 125 126 static final class FakeXdsClientPoolFactory 127 implements XdsNameResolverProvider.XdsClientPoolFactory { 128 129 private XdsClient xdsClient; 130 Map<String, ?> savedBootstrap; 131 FakeXdsClientPoolFactory(XdsClient xdsClient)132 FakeXdsClientPoolFactory(XdsClient xdsClient) { 133 this.xdsClient = xdsClient; 134 } 135 136 @Override setBootstrapOverride(Map<String, ?> bootstrap)137 public void setBootstrapOverride(Map<String, ?> bootstrap) { 138 this.savedBootstrap = bootstrap; 139 } 140 141 @Override 142 @Nullable get()143 public ObjectPool<XdsClient> get() { 144 throw new UnsupportedOperationException("Should not be called"); 145 } 146 147 @Override getOrCreate()148 public ObjectPool<XdsClient> getOrCreate() throws XdsInitializationException { 149 return new ObjectPool<XdsClient>() { 150 @Override 151 public XdsClient getObject() { 152 return xdsClient; 153 } 154 155 @Override 156 public XdsClient returnObject(Object object) { 157 xdsClient.shutdown(); 158 return null; 159 } 160 }; 161 } 162 } 163 164 static final class FakeXdsClient extends XdsClient { 165 boolean shutdown; 166 SettableFuture<String> ldsResource = SettableFuture.create(); 167 ResourceWatcher<LdsUpdate> ldsWatcher; 168 CountDownLatch rdsCount = new CountDownLatch(1); 169 final Map<String, ResourceWatcher<RdsUpdate>> rdsWatchers = new HashMap<>(); 170 171 @Override 172 public TlsContextManager getTlsContextManager() { 173 return null; 174 } 175 176 @Override 177 public BootstrapInfo getBootstrapInfo() { 178 return BOOTSTRAP_INFO; 179 } 180 181 @Override 182 @SuppressWarnings("unchecked") 183 <T extends ResourceUpdate> void watchXdsResource(XdsResourceType<T> resourceType, 184 String resourceName, 185 ResourceWatcher<T> watcher) { 186 switch (resourceType.typeName()) { 187 case "LDS": 188 assertThat(ldsWatcher).isNull(); 189 ldsWatcher = (ResourceWatcher<LdsUpdate>) watcher; 190 ldsResource.set(resourceName); 191 break; 192 case "RDS": 193 //re-register is not allowed. 194 assertThat(rdsWatchers.put(resourceName, (ResourceWatcher<RdsUpdate>)watcher)).isNull(); 195 rdsCount.countDown(); 196 break; 197 default: 198 } 199 } 200 201 @Override 202 <T extends ResourceUpdate> void cancelXdsResourceWatch(XdsResourceType<T> type, 203 String resourceName, 204 ResourceWatcher<T> watcher) { 205 switch (type.typeName()) { 206 case "LDS": 207 assertThat(ldsWatcher).isNotNull(); 208 ldsResource = null; 209 ldsWatcher = null; 210 break; 211 case "RDS": 212 rdsWatchers.remove(resourceName); 213 break; 214 default: 215 } 216 } 217 218 @Override 219 void shutdown() { 220 shutdown = true; 221 } 222 223 @Override 224 boolean isShutDown() { 225 return shutdown; 226 } 227 228 void deliverLdsUpdate(List<FilterChain> filterChains, 229 FilterChain defaultFilterChain) { 230 ldsWatcher.onChanged(LdsUpdate.forTcpListener(Listener.create( 231 "listener", "0.0.0.0:1", ImmutableList.copyOf(filterChains), defaultFilterChain))); 232 } 233 234 void deliverLdsUpdate(LdsUpdate ldsUpdate) { 235 ldsWatcher.onChanged(ldsUpdate); 236 } 237 238 void deliverRdsUpdate(String rdsName, List<VirtualHost> virtualHosts) { 239 rdsWatchers.get(rdsName).onChanged(new RdsUpdate(virtualHosts)); 240 } 241 } 242 } 243