From 27ec907e68183b9c3c179461782bd5680f95d3c6 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Thu, 5 Sep 2019 10:32:21 -0700 Subject: [PATCH] Implemented a Bootstrapper class which reads a local bootstrap file. --- xds/build.gradle | 9 +- .../main/java/io/grpc/xds/Bootstrapper.java | 135 ++++++++++++++++++ 2 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 xds/src/main/java/io/grpc/xds/Bootstrapper.java diff --git a/xds/build.gradle b/xds/build.gradle index db4d5255bb..2f12516011 100644 --- a/xds/build.gradle +++ b/xds/build.gradle @@ -26,11 +26,18 @@ dependencies { compile project(':grpc-protobuf'), project(':grpc-stub'), project(':grpc-core'), - project(':grpc-services') + project(':grpc-services'), + project(':grpc-auth') compile (libraries.protobuf_util) { // prefer 26.0-android from libraries instead of 20.0 exclude group: 'com.google.guava', module: 'guava' } + compile (libraries.google_auth_oauth2_http) { + // prefer 26.0-android from libraries instead of 25.1-android + exclude group: 'com.google.guava', module: 'guava' + // prefer 0.19.2 from libraries instead of 0.18.0 + exclude group: 'io.opencensus', module: 'opencensus-api' + } testCompile project(':grpc-core').sourceSets.test.output diff --git a/xds/src/main/java/io/grpc/xds/Bootstrapper.java b/xds/src/main/java/io/grpc/xds/Bootstrapper.java new file mode 100644 index 0000000000..124883097d --- /dev/null +++ b/xds/src/main/java/io/grpc/xds/Bootstrapper.java @@ -0,0 +1,135 @@ +/* + * Copyright 2019 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc.xds; + +import com.google.auth.oauth2.ComputeEngineCredentials; +import com.google.common.annotations.VisibleForTesting; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.util.JsonFormat; +import io.envoyproxy.envoy.api.v2.core.ApiConfigSource; +import io.envoyproxy.envoy.api.v2.core.ApiConfigSource.ApiType; +import io.envoyproxy.envoy.api.v2.core.Node; +import io.grpc.CallCredentials; +import io.grpc.auth.MoreCallCredentials; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import javax.annotation.concurrent.Immutable; + +/** + * Loads configuration information to bootstrap xDS load balancer. + */ +@Immutable +abstract class Bootstrapper { + + private static final String BOOTSTRAP_PATH_SYS_ENV_VAR = "GRPC_XDS_BOOTSTRAP"; + private static volatile Exception failToBootstrapException; + private static volatile Bootstrapper DEFAULT_INSTANCE; + + static Bootstrapper getInstance() throws Exception { + if (DEFAULT_INSTANCE == null && failToBootstrapException == null) { + synchronized (Bootstrapper.class) { + if (DEFAULT_INSTANCE == null && failToBootstrapException == null) { + try { + DEFAULT_INSTANCE = new FileBasedBootstrapper(); + } catch (Exception e) { + failToBootstrapException = e; + } + } + } + } + if (DEFAULT_INSTANCE == null) { + throw failToBootstrapException; + } + return DEFAULT_INSTANCE; + } + + /** + * Returns the canonical name of the traffic director to be connected to. + */ + abstract String getBalancerName(); + + /** + * Returns a {@link Node} message with project/network metadata in it to be included in + * xDS requests. + */ + abstract Node getNode(); + + /** + * Returns the credentials to use when communicating with the xDS server. + */ + abstract CallCredentials getCallCredentials(); + + @VisibleForTesting + static final class FileBasedBootstrapper extends Bootstrapper { + + private final String balancerName; + private final Node node; + // TODO(chengyuanzhang): Add configuration for call credentials loaded from bootstrap file. + // hard-coded for alpha release. + + private FileBasedBootstrapper() throws IOException { + this(Bootstrapper.readConfig()); + } + + @VisibleForTesting + FileBasedBootstrapper(Bootstrap bootstrapConfig) { + ApiConfigSource serverConfig = bootstrapConfig.getXdsServer(); + if (!serverConfig.getApiType().equals(ApiType.GRPC)) { + throw new RuntimeException("Unexpected api type: " + serverConfig.getApiType().toString()); + } + if (serverConfig.getGrpcServicesCount() != 1) { + throw new RuntimeException( + "Unexpected number of gRPC services: expected: 1, actual: " + + serverConfig.getGrpcServicesCount()); + } + balancerName = serverConfig.getGrpcServices(0).getGoogleGrpc().getTargetUri(); + node = bootstrapConfig.getNode(); + } + + @Override + String getBalancerName() { + return balancerName; + } + + @Override + Node getNode() { + return node; + } + + @Override + CallCredentials getCallCredentials() { + return MoreCallCredentials.from(ComputeEngineCredentials.create()); + } + } + + private static Bootstrap readConfig() throws IOException { + String filePath = System.getenv(BOOTSTRAP_PATH_SYS_ENV_VAR); + if (filePath == null) { + throw new RuntimeException("Environment variable " + + BOOTSTRAP_PATH_SYS_ENV_VAR + " not found."); + } + return parseConfig(new String(Files.readAllBytes(Paths.get(filePath)), "UTF-8")); + } + + @VisibleForTesting + static Bootstrap parseConfig(String rawData) throws InvalidProtocolBufferException { + Bootstrap.Builder bootstrapBuilder = Bootstrap.newBuilder(); + JsonFormat.parser().merge(rawData, bootstrapBuilder); + return bootstrapBuilder.build(); + } +}