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