1 //
2 // Copyright 2022 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 #include <grpc/support/port_platform.h>
18 
19 #include "src/core/ext/xds/xds_client_grpc.h"
20 
21 #include <algorithm>
22 #include <memory>
23 #include <string>
24 #include <utility>
25 
26 #include "absl/base/thread_annotations.h"
27 #include "absl/status/status.h"
28 #include "absl/strings/str_cat.h"
29 #include "absl/strings/string_view.h"
30 #include "absl/types/optional.h"
31 
32 #include <grpc/grpc.h>
33 #include <grpc/slice.h>
34 #include <grpc/support/alloc.h>
35 #include <grpc/support/log.h>
36 #include <grpc/support/string_util.h>
37 
38 #include "src/core/ext/xds/xds_bootstrap.h"
39 #include "src/core/ext/xds/xds_bootstrap_grpc.h"
40 #include "src/core/ext/xds/xds_channel_args.h"
41 #include "src/core/ext/xds/xds_transport.h"
42 #include "src/core/ext/xds/xds_transport_grpc.h"
43 #include "src/core/lib/channel/channel_args.h"
44 #include "src/core/lib/debug/trace.h"
45 #include "src/core/lib/event_engine/default_event_engine.h"
46 #include "src/core/lib/gprpp/debug_location.h"
47 #include "src/core/lib/gprpp/env.h"
48 #include "src/core/lib/gprpp/orphanable.h"
49 #include "src/core/lib/gprpp/ref_counted_ptr.h"
50 #include "src/core/lib/gprpp/sync.h"
51 #include "src/core/lib/gprpp/time.h"
52 #include "src/core/lib/iomgr/error.h"
53 #include "src/core/lib/iomgr/exec_ctx.h"
54 #include "src/core/lib/iomgr/load_file.h"
55 #include "src/core/lib/slice/slice.h"
56 #include "src/core/lib/slice/slice_internal.h"
57 #include "src/core/lib/transport/error_utils.h"
58 
59 namespace grpc_core {
60 
61 // If gRPC is built with -DGRPC_XDS_USER_AGENT_NAME_SUFFIX="...", that string
62 // will be appended to the user agent name reported to the xDS server.
63 #ifdef GRPC_XDS_USER_AGENT_NAME_SUFFIX
64 #define GRPC_XDS_USER_AGENT_NAME_SUFFIX_STRING \
65   " " GRPC_XDS_USER_AGENT_NAME_SUFFIX
66 #else
67 #define GRPC_XDS_USER_AGENT_NAME_SUFFIX_STRING ""
68 #endif
69 
70 // If gRPC is built with -DGRPC_XDS_USER_AGENT_VERSION_SUFFIX="...", that string
71 // will be appended to the user agent version reported to the xDS server.
72 #ifdef GRPC_XDS_USER_AGENT_VERSION_SUFFIX
73 #define GRPC_XDS_USER_AGENT_VERSION_SUFFIX_STRING \
74   " " GRPC_XDS_USER_AGENT_VERSION_SUFFIX
75 #else
76 #define GRPC_XDS_USER_AGENT_VERSION_SUFFIX_STRING ""
77 #endif
78 
79 //
80 // GrpcXdsClient
81 //
82 
83 namespace {
84 
85 Mutex* g_mu = new Mutex;
86 const grpc_channel_args* g_channel_args ABSL_GUARDED_BY(*g_mu) = nullptr;
87 GrpcXdsClient* g_xds_client ABSL_GUARDED_BY(*g_mu) = nullptr;
88 char* g_fallback_bootstrap_config ABSL_GUARDED_BY(*g_mu) = nullptr;
89 
90 }  // namespace
91 
92 namespace {
93 
GetBootstrapContents(const char * fallback_config)94 absl::StatusOr<std::string> GetBootstrapContents(const char* fallback_config) {
95   // First, try GRPC_XDS_BOOTSTRAP env var.
96   auto path = GetEnv("GRPC_XDS_BOOTSTRAP");
97   if (path.has_value()) {
98     if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
99       gpr_log(GPR_INFO,
100               "Got bootstrap file location from GRPC_XDS_BOOTSTRAP "
101               "environment variable: %s",
102               path->c_str());
103     }
104     grpc_slice contents;
105     grpc_error_handle error =
106         grpc_load_file(path->c_str(), /*add_null_terminator=*/true, &contents);
107     if (!error.ok()) return grpc_error_to_absl_status(error);
108     std::string contents_str(StringViewFromSlice(contents));
109     CSliceUnref(contents);
110     return contents_str;
111   }
112   // Next, try GRPC_XDS_BOOTSTRAP_CONFIG env var.
113   auto env_config = GetEnv("GRPC_XDS_BOOTSTRAP_CONFIG");
114   if (env_config.has_value()) {
115     if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
116       gpr_log(GPR_INFO,
117               "Got bootstrap contents from GRPC_XDS_BOOTSTRAP_CONFIG "
118               "environment variable");
119     }
120     return std::move(*env_config);
121   }
122   // Finally, try fallback config.
123   if (fallback_config != nullptr) {
124     if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
125       gpr_log(GPR_INFO, "Got bootstrap contents from fallback config");
126     }
127     return fallback_config;
128   }
129   // No bootstrap config found.
130   return absl::FailedPreconditionError(
131       "Environment variables GRPC_XDS_BOOTSTRAP or GRPC_XDS_BOOTSTRAP_CONFIG "
132       "not defined");
133 }
134 
135 }  // namespace
136 
GetOrCreate(const ChannelArgs & args,const char * reason)137 absl::StatusOr<RefCountedPtr<GrpcXdsClient>> GrpcXdsClient::GetOrCreate(
138     const ChannelArgs& args, const char* reason) {
139   // If getting bootstrap from channel args, create a local XdsClient
140   // instance for the channel or server instead of using the global instance.
141   absl::optional<absl::string_view> bootstrap_config = args.GetString(
142       GRPC_ARG_TEST_ONLY_DO_NOT_USE_IN_PROD_XDS_BOOTSTRAP_CONFIG);
143   if (bootstrap_config.has_value()) {
144     auto bootstrap = GrpcXdsBootstrap::Create(*bootstrap_config);
145     if (!bootstrap.ok()) return bootstrap.status();
146     grpc_channel_args* xds_channel_args = args.GetPointer<grpc_channel_args>(
147         GRPC_ARG_TEST_ONLY_DO_NOT_USE_IN_PROD_XDS_CLIENT_CHANNEL_ARGS);
148     return MakeRefCounted<GrpcXdsClient>(std::move(*bootstrap),
149                                          ChannelArgs::FromC(xds_channel_args));
150   }
151   // Otherwise, use the global instance.
152   MutexLock lock(g_mu);
153   if (g_xds_client != nullptr) {
154     auto xds_client = g_xds_client->RefIfNonZero(DEBUG_LOCATION, reason);
155     if (xds_client != nullptr) return xds_client;
156   }
157   // Find bootstrap contents.
158   auto bootstrap_contents = GetBootstrapContents(g_fallback_bootstrap_config);
159   if (!bootstrap_contents.ok()) return bootstrap_contents.status();
160   if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
161     gpr_log(GPR_INFO, "xDS bootstrap contents: %s",
162             bootstrap_contents->c_str());
163   }
164   // Parse bootstrap.
165   auto bootstrap = GrpcXdsBootstrap::Create(*bootstrap_contents);
166   if (!bootstrap.ok()) return bootstrap.status();
167   // Instantiate XdsClient.
168   auto xds_client = MakeRefCounted<GrpcXdsClient>(
169       std::move(*bootstrap), ChannelArgs::FromC(g_channel_args));
170   g_xds_client = xds_client.get();
171   return xds_client;
172 }
173 
GrpcXdsClient(std::unique_ptr<GrpcXdsBootstrap> bootstrap,const ChannelArgs & args)174 GrpcXdsClient::GrpcXdsClient(std::unique_ptr<GrpcXdsBootstrap> bootstrap,
175                              const ChannelArgs& args)
176     : XdsClient(
177           std::move(bootstrap), MakeOrphanable<GrpcXdsTransportFactory>(args),
178           grpc_event_engine::experimental::GetDefaultEventEngine(),
179           absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING,
180                        GRPC_XDS_USER_AGENT_NAME_SUFFIX_STRING),
181           absl::StrCat("C-core ", grpc_version_string(),
182                        GRPC_XDS_USER_AGENT_NAME_SUFFIX_STRING,
183                        GRPC_XDS_USER_AGENT_VERSION_SUFFIX_STRING),
184           std::max(Duration::Zero(),
185                    args.GetDurationFromIntMillis(
186                            GRPC_ARG_XDS_RESOURCE_DOES_NOT_EXIST_TIMEOUT_MS)
187                        .value_or(Duration::Seconds(15)))),
188       certificate_provider_store_(MakeOrphanable<CertificateProviderStore>(
189           static_cast<const GrpcXdsBootstrap&>(this->bootstrap())
190               .certificate_providers())) {}
191 
~GrpcXdsClient()192 GrpcXdsClient::~GrpcXdsClient() {
193   MutexLock lock(g_mu);
194   if (g_xds_client == this) g_xds_client = nullptr;
195 }
196 
interested_parties() const197 grpc_pollset_set* GrpcXdsClient::interested_parties() const {
198   return reinterpret_cast<GrpcXdsTransportFactory*>(transport_factory())
199       ->interested_parties();
200 }
201 
202 namespace internal {
203 
SetXdsChannelArgsForTest(grpc_channel_args * args)204 void SetXdsChannelArgsForTest(grpc_channel_args* args) {
205   MutexLock lock(g_mu);
206   g_channel_args = args;
207 }
208 
UnsetGlobalXdsClientForTest()209 void UnsetGlobalXdsClientForTest() {
210   MutexLock lock(g_mu);
211   g_xds_client = nullptr;
212 }
213 
SetXdsFallbackBootstrapConfig(const char * config)214 void SetXdsFallbackBootstrapConfig(const char* config) {
215   MutexLock lock(g_mu);
216   gpr_free(g_fallback_bootstrap_config);
217   g_fallback_bootstrap_config = gpr_strdup(config);
218 }
219 
220 }  // namespace internal
221 
222 }  // namespace grpc_core
223 
224 // The returned bytes may contain NULL(0), so we can't use c-string.
grpc_dump_xds_configs(void)225 grpc_slice grpc_dump_xds_configs(void) {
226   grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
227   grpc_core::ExecCtx exec_ctx;
228   auto xds_client = grpc_core::GrpcXdsClient::GetOrCreate(
229       grpc_core::ChannelArgs(), "grpc_dump_xds_configs()");
230   if (!xds_client.ok()) {
231     // If we aren't using xDS, just return an empty string.
232     return grpc_empty_slice();
233   }
234   return grpc_slice_from_cpp_string((*xds_client)->DumpClientConfigBinary());
235 }
236