Skip to content

Commit

Permalink
xds: implement cloud-to-prod resolver (#7900)
Browse files Browse the repository at this point in the history
Implemented CloudToProdNameResolver, which will be used for DirectPath with URI scheme "google-c2p". The resolver is only a wrapper that delegates name resolution either to DNS or xDS resolver depending on the environment. If it is delegating to the xDS resolver, it will send HTTP requests (to a local HTTP server) to fetch metadata that is used to generate a bootstrap config. The self-generated bootstrap will be used for xDS.
  • Loading branch information
voidzcy committed Feb 23, 2021
1 parent bfc67bf commit 2bfa003
Show file tree
Hide file tree
Showing 16 changed files with 776 additions and 74 deletions.
5 changes: 4 additions & 1 deletion alts/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ import net.ltgt.gradle.errorprone.CheckSeverity
it.options.errorprone.check("FutureReturnValueIgnored", CheckSeverity.OFF)
}

javadoc { exclude 'io/grpc/alts/internal/**' }
javadoc {
exclude 'io/grpc/alts/internal/**'
exclude 'io/grpc/alts/Internal*'
}

jar {
// Must use a different classifier to avoid conflicting with shadowJar
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public ChannelCredentials build() {
}

InternalProtocolNegotiator.ClientFactory buildProtocolNegotiatorFactory() {
if (!CheckGcpEnvironment.isOnGcp()) {
if (!InternalCheckGcpEnvironment.isOnGcp()) {
if (enableUntrustedAlts) {
logger.log(
Level.WARNING,
Expand Down
2 changes: 1 addition & 1 deletion alts/src/main/java/io/grpc/alts/AltsServerCredentials.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public ServerCredentials build() {
}

InternalProtocolNegotiator.ProtocolNegotiator buildProtocolNegotiator() {
if (!CheckGcpEnvironment.isOnGcp()) {
if (!InternalCheckGcpEnvironment.isOnGcp()) {
if (enableUntrustedAlts) {
logger.log(
Level.WARNING,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public static ChannelCredentials create() {
ChannelCredentials nettyCredentials =
InternalNettyChannelCredentials.create(createClientFactory());
CallCredentials callCredentials;
if (CheckGcpEnvironment.isOnGcp()) {
if (InternalCheckGcpEnvironment.isOnGcp()) {
callCredentials = MoreCallCredentials.from(ComputeEngineCredentials.create());
} else {
callCredentials = new FailingCallCredentials(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.common.annotations.VisibleForTesting;
import io.grpc.Internal;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
Expand All @@ -28,18 +29,27 @@
import java.util.logging.Level;
import java.util.logging.Logger;

/** Class for checking if the system is running on Google Cloud Platform (GCP). */
final class CheckGcpEnvironment {
/**
* Class for checking if the system is running on Google Cloud Platform (GCP).
* This is intended for usage internal to the gRPC team. If you *really* think you need
* to use this, contact the gRPC team first.
*/
@Internal
public final class InternalCheckGcpEnvironment {

private static final Logger logger = Logger.getLogger(CheckGcpEnvironment.class.getName());
private static final Logger logger =
Logger.getLogger(InternalCheckGcpEnvironment.class.getName());
private static final String DMI_PRODUCT_NAME = "/sys/class/dmi/id/product_name";
private static final String WINDOWS_COMMAND = "powershell.exe";
private static Boolean cachedResult = null;

// Construct me not!
private CheckGcpEnvironment() {}
private InternalCheckGcpEnvironment() {}

static synchronized boolean isOnGcp() {
/**
* Returns {@code true} if currently running on Google Cloud Platform (GCP).
*/
public static synchronized boolean isOnGcp() {
if (cachedResult == null) {
cachedResult = isRunningOnGcp();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,37 +26,37 @@
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public final class CheckGcpEnvironmentTest {
public final class InternalCheckGcpEnvironmentTest {

@Test
public void checkGcpLinuxPlatformData() throws Exception {
BufferedReader reader;
reader = new BufferedReader(new StringReader("HP Z440 Workstation"));
assertFalse(CheckGcpEnvironment.checkProductNameOnLinux(reader));
assertFalse(InternalCheckGcpEnvironment.checkProductNameOnLinux(reader));
reader = new BufferedReader(new StringReader("Google"));
assertTrue(CheckGcpEnvironment.checkProductNameOnLinux(reader));
assertTrue(InternalCheckGcpEnvironment.checkProductNameOnLinux(reader));
reader = new BufferedReader(new StringReader("Google Compute Engine"));
assertTrue(CheckGcpEnvironment.checkProductNameOnLinux(reader));
assertTrue(InternalCheckGcpEnvironment.checkProductNameOnLinux(reader));
reader = new BufferedReader(new StringReader("Google Compute Engine "));
assertTrue(CheckGcpEnvironment.checkProductNameOnLinux(reader));
assertTrue(InternalCheckGcpEnvironment.checkProductNameOnLinux(reader));
}

@Test
public void checkGcpWindowsPlatformData() throws Exception {
BufferedReader reader;
reader = new BufferedReader(new StringReader("Product : Google"));
assertFalse(CheckGcpEnvironment.checkBiosDataOnWindows(reader));
assertFalse(InternalCheckGcpEnvironment.checkBiosDataOnWindows(reader));
reader = new BufferedReader(new StringReader("Manufacturer : LENOVO"));
assertFalse(CheckGcpEnvironment.checkBiosDataOnWindows(reader));
assertFalse(InternalCheckGcpEnvironment.checkBiosDataOnWindows(reader));
reader = new BufferedReader(new StringReader("Manufacturer : Google Compute Engine"));
assertFalse(CheckGcpEnvironment.checkBiosDataOnWindows(reader));
assertFalse(InternalCheckGcpEnvironment.checkBiosDataOnWindows(reader));
reader = new BufferedReader(new StringReader("Manufacturer : Google"));
assertTrue(CheckGcpEnvironment.checkBiosDataOnWindows(reader));
assertTrue(InternalCheckGcpEnvironment.checkBiosDataOnWindows(reader));
reader = new BufferedReader(new StringReader("Manufacturer:Google"));
assertTrue(CheckGcpEnvironment.checkBiosDataOnWindows(reader));
assertTrue(InternalCheckGcpEnvironment.checkBiosDataOnWindows(reader));
reader = new BufferedReader(new StringReader("Manufacturer : Google "));
assertTrue(CheckGcpEnvironment.checkBiosDataOnWindows(reader));
assertTrue(InternalCheckGcpEnvironment.checkBiosDataOnWindows(reader));
reader = new BufferedReader(new StringReader("BIOSVersion : 1.0\nManufacturer : Google\n"));
assertTrue(CheckGcpEnvironment.checkBiosDataOnWindows(reader));
assertTrue(InternalCheckGcpEnvironment.checkBiosDataOnWindows(reader));
}
}
19 changes: 13 additions & 6 deletions xds/src/main/java/io/grpc/xds/Bootstrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,26 @@
* Loads configuration information to bootstrap gRPC's integration of xDS protocol.
*/
@Internal
public interface Bootstrapper {
public abstract class Bootstrapper {

/**
* Returns configurations from bootstrap.
* Returns system-loaded bootstrap configuration.
*/
BootstrapInfo bootstrap() throws XdsInitializationException;
public abstract BootstrapInfo bootstrap() throws XdsInitializationException;

/**
* Returns bootstrap configuration given by the raw data in JSON format.
*/
BootstrapInfo bootstrap(Map<String, ?> rawData) throws XdsInitializationException {
throw new UnsupportedOperationException();
}

/**
* Data class containing xDS server information, such as server URI and channel credentials
* to be used for communication.
*/
@Internal
class ServerInfo {
static class ServerInfo {
private final String target;
private final ChannelCredentials channelCredentials;
private final boolean useProtocolV3;
Expand Down Expand Up @@ -73,7 +80,7 @@ boolean isUseProtocolV3() {
* Map that represents the config for that plugin.
*/
@Internal
class CertificateProviderInfo {
public static class CertificateProviderInfo {
private final String pluginName;
private final Map<String, ?> config;

Expand All @@ -95,7 +102,7 @@ public String getPluginName() {
* Data class containing the results of reading bootstrap.
*/
@Internal
class BootstrapInfo {
public static class BootstrapInfo {
private List<ServerInfo> servers;
private final Node node;
@Nullable private final Map<String, CertificateProviderInfo> certProviders;
Expand Down
92 changes: 46 additions & 46 deletions xds/src/main/java/io/grpc/xds/BootstrapperImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
* A {@link Bootstrapper} implementation that reads xDS configurations from local file system.
*/
@Internal
public class BootstrapperImpl implements Bootstrapper {
public class BootstrapperImpl extends Bootstrapper {

private static final String BOOTSTRAP_PATH_SYS_ENV_VAR = "GRPC_XDS_BOOTSTRAP";
@VisibleForTesting
Expand Down Expand Up @@ -80,70 +80,49 @@ public BootstrapperImpl() {
* <li>Java System Property value of "io.grpc.xds.bootstrap_value"</li>
* </ol>
*/
@SuppressWarnings("unchecked")
@Override
public BootstrapInfo bootstrap() throws XdsInitializationException {
String filePath =
bootstrapPathFromEnvVar != null ? bootstrapPathFromEnvVar : bootstrapPathFromSysProp;
String rawBootstrap;
String fileContent;
if (filePath != null) {
logger.log(XdsLogLevel.INFO, "Reading bootstrap file from {0}", filePath);
try {
rawBootstrap = reader.readFile(filePath);
fileContent = reader.readFile(filePath);
} catch (IOException e) {
throw new XdsInitializationException("Fail to read bootstrap file", e);
}
} else {
rawBootstrap = bootstrapConfigFromEnvVar != null
fileContent = bootstrapConfigFromEnvVar != null
? bootstrapConfigFromEnvVar : bootstrapConfigFromSysProp;
}
if (rawBootstrap != null) {
return parseConfig(rawBootstrap);
}
throw new XdsInitializationException(
"Cannot find bootstrap configuration\n"
+ "Environment variables searched:\n"
+ "- " + BOOTSTRAP_PATH_SYS_ENV_VAR + "\n"
+ "- " + BOOTSTRAP_CONFIG_SYS_ENV_VAR + "\n\n"
+ "Java System Properties searched:\n"
+ "- " + BOOTSTRAP_PATH_SYS_PROPERTY + "\n"
+ "- " + BOOTSTRAP_CONFIG_SYS_PROPERTY_VAR + "\n\n");
}

@VisibleForTesting
void setFileReader(FileReader reader) {
this.reader = reader;
}

/**
* Reads the content of the file with the given path in the file system.
*/
interface FileReader {
String readFile(String path) throws IOException;
}

private enum LocalFileReader implements FileReader {
INSTANCE;

@Override
public String readFile(String path) throws IOException {
return new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
if (fileContent == null) {
throw new XdsInitializationException(
"Cannot find bootstrap configuration\n"
+ "Environment variables searched:\n"
+ "- " + BOOTSTRAP_PATH_SYS_ENV_VAR + "\n"
+ "- " + BOOTSTRAP_CONFIG_SYS_ENV_VAR + "\n\n"
+ "Java System Properties searched:\n"
+ "- " + BOOTSTRAP_PATH_SYS_PROPERTY + "\n"
+ "- " + BOOTSTRAP_CONFIG_SYS_PROPERTY_VAR + "\n\n");
}
}

/** Parses a raw string into {@link BootstrapInfo}. */
@SuppressWarnings("unchecked")
private BootstrapInfo parseConfig(String rawData) throws XdsInitializationException {
logger.log(XdsLogLevel.INFO, "Reading bootstrap information");
logger.log(XdsLogLevel.INFO, "Reading bootstrap from " + filePath);
Map<String, ?> rawBootstrap;
try {
rawBootstrap = (Map<String, ?>) JsonParser.parse(rawData);
rawBootstrap = (Map<String, ?>) JsonParser.parse(fileContent);
} catch (IOException e) {
throw new XdsInitializationException("Failed to parse JSON", e);
}
logger.log(XdsLogLevel.DEBUG, "Bootstrap configuration:\n{0}", rawBootstrap);
return bootstrap(rawBootstrap);
}

@Override
BootstrapInfo bootstrap(Map<String, ?> rawData) throws XdsInitializationException {
List<ServerInfo> servers = new ArrayList<>();
List<?> rawServerConfigs = JsonUtil.getList(rawBootstrap, "xds_servers");
List<?> rawServerConfigs = JsonUtil.getList(rawData, "xds_servers");
if (rawServerConfigs == null) {
throw new XdsInitializationException("Invalid bootstrap: 'xds_servers' does not exist.");
}
Expand Down Expand Up @@ -179,7 +158,7 @@ private BootstrapInfo parseConfig(String rawData) throws XdsInitializationExcept
}

Node.Builder nodeBuilder = Node.newBuilder();
Map<String, ?> rawNode = JsonUtil.getObject(rawBootstrap, "node");
Map<String, ?> rawNode = JsonUtil.getObject(rawData, "node");
if (rawNode != null) {
String id = JsonUtil.getString(rawNode, "id");
if (id != null) {
Expand Down Expand Up @@ -222,7 +201,7 @@ private BootstrapInfo parseConfig(String rawData) throws XdsInitializationExcept
nodeBuilder.setUserAgentVersion(buildVersion.getImplementationVersion());
nodeBuilder.addClientFeatures(CLIENT_FEATURE_DISABLE_OVERPROVISIONING);

Map<String, ?> certProvidersBlob = JsonUtil.getObject(rawBootstrap, "certificate_providers");
Map<String, ?> certProvidersBlob = JsonUtil.getObject(rawData, "certificate_providers");
Map<String, CertificateProviderInfo> certProviders = null;
if (certProvidersBlob != null) {
certProviders = new HashMap<>(certProvidersBlob.size());
Expand All @@ -236,11 +215,32 @@ private BootstrapInfo parseConfig(String rawData) throws XdsInitializationExcept
certProviders.put(name, certificateProviderInfo);
}
}
String grpcServerResourceId = JsonUtil.getString(rawBootstrap, "grpc_server_resource_name_id");
String grpcServerResourceId = JsonUtil.getString(rawData, "grpc_server_resource_name_id");
return new BootstrapInfo(servers, nodeBuilder.build(), certProviders, grpcServerResourceId);
}

static <T> T checkForNull(T value, String fieldName) throws XdsInitializationException {
@VisibleForTesting
void setFileReader(FileReader reader) {
this.reader = reader;
}

/**
* Reads the content of the file with the given path in the file system.
*/
interface FileReader {
String readFile(String path) throws IOException;
}

private enum LocalFileReader implements FileReader {
INSTANCE;

@Override
public String readFile(String path) throws IOException {
return new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
}
}

private static <T> T checkForNull(T value, String fieldName) throws XdsInitializationException {
if (value == null) {
throw new XdsInitializationException(
"Invalid bootstrap: '" + fieldName + "' does not exist.");
Expand Down

0 comments on commit 2bfa003

Please sign in to comment.