mirror of https://github.com/grpc/grpc-java.git
xds: add support for cert-providers to bootstrap file (#7285)
This commit is contained in:
parent
afcce8d3c0
commit
34513d7ed8
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package io.grpc.xds;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import io.grpc.Internal;
|
||||
import io.grpc.internal.GrpcUtil;
|
||||
|
|
@ -31,6 +33,7 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
|
@ -160,7 +163,28 @@ public abstract class Bootstrapper {
|
|||
nodeBuilder.setUserAgentVersion(buildVersion.getImplementationVersion());
|
||||
nodeBuilder.addClientFeatures(CLIENT_FEATURE_DISABLE_OVERPROVISIONING);
|
||||
|
||||
return new BootstrapInfo(servers, nodeBuilder.build());
|
||||
Map<String, ?> certProvidersBlob = JsonUtil.getObject(rawBootstrap, "certificate_providers");
|
||||
Map<String, CertificateProviderInfo> certProviders = null;
|
||||
if (certProvidersBlob != null) {
|
||||
certProviders = new HashMap<>(certProvidersBlob.size());
|
||||
for (String name : certProvidersBlob.keySet()) {
|
||||
Map<String, ?> valueMap = JsonUtil.getObject(certProvidersBlob, name);
|
||||
String pluginName =
|
||||
checkForNull(JsonUtil.getString(valueMap, "plugin_name"), "plugin_name");
|
||||
Map<String, ?> config = checkForNull(JsonUtil.getObject(valueMap, "config"), "config");
|
||||
CertificateProviderInfo certificateProviderInfo =
|
||||
new CertificateProviderInfo(pluginName, config);
|
||||
certProviders.put(name, certificateProviderInfo);
|
||||
}
|
||||
}
|
||||
return new BootstrapInfo(servers, nodeBuilder.build(), certProviders);
|
||||
}
|
||||
|
||||
static <T> T checkForNull(T value, String fieldName) throws IOException {
|
||||
if (value == null) {
|
||||
throw new IOException("Invalid bootstrap: '" + fieldName + "' does not exist.");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -225,6 +249,30 @@ public abstract class Bootstrapper {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class containing Certificate provider information: the plugin-name and an opaque
|
||||
* Map that represents the config for that plugin.
|
||||
*/
|
||||
@Internal
|
||||
@Immutable
|
||||
public static class CertificateProviderInfo {
|
||||
private final String pluginName;
|
||||
private final Map<String, ?> config;
|
||||
|
||||
CertificateProviderInfo(String pluginName, Map<String, ?> config) {
|
||||
this.pluginName = checkNotNull(pluginName, "pluginName");
|
||||
this.config = checkNotNull(config, "config");
|
||||
}
|
||||
|
||||
String getPluginName() {
|
||||
return pluginName;
|
||||
}
|
||||
|
||||
Map<String, ?> getConfig() {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class containing the results of reading bootstrap.
|
||||
*/
|
||||
|
|
@ -233,11 +281,14 @@ public abstract class Bootstrapper {
|
|||
public static class BootstrapInfo {
|
||||
private List<ServerInfo> servers;
|
||||
private final Node node;
|
||||
@Nullable private final Map<String, CertificateProviderInfo> certProviders;
|
||||
|
||||
@VisibleForTesting
|
||||
BootstrapInfo(List<ServerInfo> servers, Node node) {
|
||||
BootstrapInfo(
|
||||
List<ServerInfo> servers, Node node, Map<String, CertificateProviderInfo> certProviders) {
|
||||
this.servers = servers;
|
||||
this.node = node;
|
||||
this.certProviders = certProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -253,5 +304,10 @@ public abstract class Bootstrapper {
|
|||
public Node getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
/** Returns the cert-providers config map. */
|
||||
public Map<String, CertificateProviderInfo> getCertProviders() {
|
||||
return Collections.unmodifiableMap(certProviders);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package io.grpc.xds;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
|
@ -28,6 +29,7 @@ import io.grpc.xds.EnvoyProtoData.Locality;
|
|||
import io.grpc.xds.EnvoyProtoData.Node;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
|
@ -298,6 +300,206 @@ public class BootstrapperTest {
|
|||
Bootstrapper.parseConfig(rawData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseBootstrap_validData_certProviderInstances() throws IOException {
|
||||
String rawData =
|
||||
"{\n"
|
||||
+ " \"xds_servers\": [],\n"
|
||||
+ " \"certificate_providers\": {\n"
|
||||
+ " \"gcp_id\": {\n"
|
||||
+ " \"plugin_name\": \"meshca\",\n"
|
||||
+ " \"config\": {\n"
|
||||
+ " \"server\": {\n"
|
||||
+ " \"api_type\": \"GRPC\",\n"
|
||||
+ " \"grpc_services\": [{\n"
|
||||
+ " \"google_grpc\": {\n"
|
||||
+ " \"target_uri\": \"meshca.com\",\n"
|
||||
+ " \"channel_credentials\": {\"google_default\": {}},\n"
|
||||
+ " \"call_credentials\": [{\n"
|
||||
+ " \"sts_service\": {\n"
|
||||
+ " \"token_exchange_service\": \"securetoken.googleapis.com\",\n"
|
||||
+ " \"subject_token_path\": \"/etc/secret/sajwt.token\"\n"
|
||||
+ " }\n"
|
||||
+ " }]\n" // end call_credentials
|
||||
+ " },\n" // end google_grpc
|
||||
+ " \"time_out\": {\"seconds\": 10}\n"
|
||||
+ " }]\n" // end grpc_services
|
||||
+ " },\n" // end server
|
||||
+ " \"certificate_lifetime\": {\"seconds\": 86400},\n"
|
||||
+ " \"renewal_grace_period\": {\"seconds\": 3600},\n"
|
||||
+ " \"key_type\": \"RSA\",\n"
|
||||
+ " \"key_size\": 2048,\n"
|
||||
+ " \"location\": \"https://container.googleapis.com/v1/project/test-project1/locations/test-zone2/clusters/test-cluster3\"\n"
|
||||
+ " }\n" // end config
|
||||
+ " },\n" // end gcp_id
|
||||
+ " \"file_provider\": {\n"
|
||||
+ " \"plugin_name\": \"file_watcher\",\n"
|
||||
+ " \"config\": {\"path\": \"/etc/secret/certs\"}\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "}";
|
||||
|
||||
BootstrapInfo info = Bootstrapper.parseConfig(rawData);
|
||||
assertThat(info.getServers()).isEmpty();
|
||||
assertThat(info.getNode()).isEqualTo(getNodeBuilder().build());
|
||||
Map<String, Bootstrapper.CertificateProviderInfo> certProviders = info.getCertProviders();
|
||||
assertThat(certProviders).isNotNull();
|
||||
Bootstrapper.CertificateProviderInfo gcpId = certProviders.get("gcp_id");
|
||||
Bootstrapper.CertificateProviderInfo fileProvider = certProviders.get("file_provider");
|
||||
assertThat(gcpId.getPluginName()).isEqualTo("meshca");
|
||||
assertThat(gcpId.getConfig()).isInstanceOf(Map.class);
|
||||
assertThat(fileProvider.getPluginName()).isEqualTo("file_watcher");
|
||||
assertThat(fileProvider.getConfig()).isInstanceOf(Map.class);
|
||||
Map<String, ?> meshCaConfig = (Map<String, ?>)gcpId.getConfig();
|
||||
assertThat(meshCaConfig.get("key_size")).isEqualTo(2048);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseBootstrap_badPluginName() throws IOException {
|
||||
String rawData =
|
||||
"{\n"
|
||||
+ " \"xds_servers\": [],\n"
|
||||
+ " \"certificate_providers\": {\n"
|
||||
+ " \"gcp_id\": {\n"
|
||||
+ " \"plugin_name\": 234,\n"
|
||||
+ " \"config\": {\n"
|
||||
+ " \"server\": {\n"
|
||||
+ " \"api_type\": \"GRPC\",\n"
|
||||
+ " \"grpc_services\": [{\n"
|
||||
+ " \"google_grpc\": {\n"
|
||||
+ " \"target_uri\": \"meshca.com\",\n"
|
||||
+ " \"channel_credentials\": {\"google_default\": {}},\n"
|
||||
+ " \"call_credentials\": [{\n"
|
||||
+ " \"sts_service\": {\n"
|
||||
+ " \"token_exchange_service\": \"securetoken.googleapis.com\",\n"
|
||||
+ " \"subject_token_path\": \"/etc/secret/sajwt.token\"\n"
|
||||
+ " }\n"
|
||||
+ " }]\n" // end call_credentials
|
||||
+ " },\n" // end google_grpc
|
||||
+ " \"time_out\": {\"seconds\": 10}\n"
|
||||
+ " }]\n" // end grpc_services
|
||||
+ " },\n" // end server
|
||||
+ " \"certificate_lifetime\": {\"seconds\": 86400},\n"
|
||||
+ " \"renewal_grace_period\": {\"seconds\": 3600},\n"
|
||||
+ " \"key_type\": \"RSA\",\n"
|
||||
+ " \"key_size\": 2048,\n"
|
||||
+ " \"location\": \"https://container.googleapis.com/v1/project/test-project1/locations/test-zone2/clusters/test-cluster3\"\n"
|
||||
+ " }\n" // end config
|
||||
+ " },\n" // end gcp_id
|
||||
+ " \"file_provider\": {\n"
|
||||
+ " \"plugin_name\": \"file_watcher\",\n"
|
||||
+ " \"config\": {\"path\": \"/etc/secret/certs\"}\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "}";
|
||||
|
||||
try {
|
||||
Bootstrapper.parseConfig(rawData);
|
||||
fail("exception expected");
|
||||
} catch (ClassCastException expected) {
|
||||
assertThat(expected).hasMessageThat().contains("value '234.0' for key 'plugin_name' in");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseBootstrap_badConfig() throws IOException {
|
||||
String rawData =
|
||||
"{\n"
|
||||
+ " \"xds_servers\": [],\n"
|
||||
+ " \"certificate_providers\": {\n"
|
||||
+ " \"gcp_id\": {\n"
|
||||
+ " \"plugin_name\": \"meshca\",\n"
|
||||
+ " \"config\": \"badValue\"\n"
|
||||
+ " },\n" // end gcp_id
|
||||
+ " \"file_provider\": {\n"
|
||||
+ " \"plugin_name\": \"file_watcher\",\n"
|
||||
+ " \"config\": {\"path\": \"/etc/secret/certs\"}\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "}";
|
||||
|
||||
try {
|
||||
Bootstrapper.parseConfig(rawData);
|
||||
fail("exception expected");
|
||||
} catch (ClassCastException expected) {
|
||||
assertThat(expected).hasMessageThat().contains("value 'badValue' for key 'config' in");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseBootstrap_missingConfig() throws IOException {
|
||||
String rawData =
|
||||
"{\n"
|
||||
+ " \"xds_servers\": [],\n"
|
||||
+ " \"certificate_providers\": {\n"
|
||||
+ " \"gcp_id\": {\n"
|
||||
+ " \"plugin_name\": \"meshca\"\n"
|
||||
+ " },\n" // end gcp_id
|
||||
+ " \"file_provider\": {\n"
|
||||
+ " \"plugin_name\": \"file_watcher\",\n"
|
||||
+ " \"config\": {\"path\": \"/etc/secret/certs\"}\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "}";
|
||||
|
||||
try {
|
||||
Bootstrapper.parseConfig(rawData);
|
||||
fail("exception expected");
|
||||
} catch (IOException expected) {
|
||||
assertThat(expected)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Invalid bootstrap: 'config' does not exist.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseBootstrap_missingPluginName() throws IOException {
|
||||
String rawData =
|
||||
"{\n"
|
||||
+ " \"xds_servers\": [],\n"
|
||||
+ " \"certificate_providers\": {\n"
|
||||
+ " \"gcp_id\": {\n"
|
||||
+ " \"plugin_name\": \"meshca\",\n"
|
||||
+ " \"config\": {\n"
|
||||
+ " \"server\": {\n"
|
||||
+ " \"api_type\": \"GRPC\",\n"
|
||||
+ " \"grpc_services\": [{\n"
|
||||
+ " \"google_grpc\": {\n"
|
||||
+ " \"target_uri\": \"meshca.com\",\n"
|
||||
+ " \"channel_credentials\": {\"google_default\": {}},\n"
|
||||
+ " \"call_credentials\": [{\n"
|
||||
+ " \"sts_service\": {\n"
|
||||
+ " \"token_exchange_service\": \"securetoken.googleapis.com\",\n"
|
||||
+ " \"subject_token_path\": \"/etc/secret/sajwt.token\"\n"
|
||||
+ " }\n"
|
||||
+ " }]\n" // end call_credentials
|
||||
+ " },\n" // end google_grpc
|
||||
+ " \"time_out\": {\"seconds\": 10}\n"
|
||||
+ " }]\n" // end grpc_services
|
||||
+ " },\n" // end server
|
||||
+ " \"certificate_lifetime\": {\"seconds\": 86400},\n"
|
||||
+ " \"renewal_grace_period\": {\"seconds\": 3600},\n"
|
||||
+ " \"key_type\": \"RSA\",\n"
|
||||
+ " \"key_size\": 2048,\n"
|
||||
+ " \"location\": \"https://container.googleapis.com/v1/project/test-project1/locations/test-zone2/clusters/test-cluster3\"\n"
|
||||
+ " }\n" // end config
|
||||
+ " },\n" // end gcp_id
|
||||
+ " \"file_provider\": {\n"
|
||||
+ " \"config\": {\"path\": \"/etc/secret/certs\"}\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "}";
|
||||
|
||||
try {
|
||||
Bootstrapper.parseConfig(rawData);
|
||||
fail("exception expected");
|
||||
} catch (IOException expected) {
|
||||
assertThat(expected)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Invalid bootstrap: 'plugin_name' does not exist.");
|
||||
}
|
||||
}
|
||||
|
||||
private static Node.Builder getNodeBuilder() {
|
||||
GrpcBuildVersion buildVersion = GrpcUtil.getGrpcBuildVersion();
|
||||
return
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ public class EdsLoadBalancerTest {
|
|||
final List<ServerInfo> serverList = ImmutableList.of(
|
||||
new ServerInfo("trafficdirector.googleapis.com", ImmutableList.<ChannelCreds>of(), null));
|
||||
Node node = Node.newBuilder().build();
|
||||
BootstrapInfo bootstrapInfo = new BootstrapInfo(serverList, node);
|
||||
BootstrapInfo bootstrapInfo = new BootstrapInfo(serverList, node, null);
|
||||
doReturn(bootstrapInfo).when(bootstrapper).readBootstrap();
|
||||
|
||||
if (isFullFlow) {
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ public class XdsNameResolverIntegrationTest {
|
|||
public BootstrapInfo readBootstrap() {
|
||||
List<ServerInfo> serverList =
|
||||
ImmutableList.of(new ServerInfo(serverName, ImmutableList.<ChannelCreds>of(), null));
|
||||
return new BootstrapInfo(serverList, FAKE_BOOTSTRAP_NODE);
|
||||
return new BootstrapInfo(serverList, FAKE_BOOTSTRAP_NODE, null);
|
||||
}
|
||||
};
|
||||
xdsNameResolver =
|
||||
|
|
@ -194,7 +194,7 @@ public class XdsNameResolverIntegrationTest {
|
|||
Bootstrapper bootstrapper = new Bootstrapper() {
|
||||
@Override
|
||||
public BootstrapInfo readBootstrap() {
|
||||
return new BootstrapInfo(ImmutableList.<ServerInfo>of(), FAKE_BOOTSTRAP_NODE);
|
||||
return new BootstrapInfo(ImmutableList.<ServerInfo>of(), FAKE_BOOTSTRAP_NODE, null);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue