1 //
2 // Copyright 2019 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 "src/core/service_config/service_config.h"
18
19 #include <grpc/grpc.h>
20 #include <stdint.h>
21
22 #include <memory>
23 #include <string>
24
25 #include "absl/status/status.h"
26 #include "absl/status/statusor.h"
27 #include "absl/strings/str_cat.h"
28 #include "absl/types/optional.h"
29 #include "gmock/gmock.h"
30 #include "gtest/gtest.h"
31 #include "src/core/config/core_configuration.h"
32 #include "src/core/lib/channel/channel_args.h"
33 #include "src/core/service_config/service_config_impl.h"
34 #include "src/core/service_config/service_config_parser.h"
35 #include "src/core/util/json/json.h"
36 #include "src/core/util/json/json_args.h"
37 #include "src/core/util/json/json_object_loader.h"
38 #include "src/core/util/ref_counted_ptr.h"
39 #include "src/core/util/validation_errors.h"
40 #include "test/core/test_util/test_config.h"
41
42 namespace grpc_core {
43 namespace testing {
44
45 // Set this channel arg to true to disable parsing.
46 #define GRPC_ARG_DISABLE_PARSING "disable_parsing"
47
48 class TestParsedConfig1 : public ServiceConfigParser::ParsedConfig {
49 public:
value() const50 uint32_t value() const { return value_; }
51
JsonLoader(const JsonArgs &)52 static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
53 static const auto* loader =
54 JsonObjectLoader<TestParsedConfig1>()
55 .OptionalField("global_param", &TestParsedConfig1::value_)
56 .Finish();
57 return loader;
58 }
59
60 private:
61 uint32_t value_;
62 };
63
64 class TestParser1 : public ServiceConfigParser::Parser {
65 public:
name() const66 absl::string_view name() const override { return "test_parser_1"; }
67
ParseGlobalParams(const ChannelArgs & args,const Json & json,ValidationErrors * errors)68 std::unique_ptr<ServiceConfigParser::ParsedConfig> ParseGlobalParams(
69 const ChannelArgs& args, const Json& json,
70 ValidationErrors* errors) override {
71 if (args.GetBool(GRPC_ARG_DISABLE_PARSING).value_or(false)) {
72 return nullptr;
73 }
74 return LoadFromJson<std::unique_ptr<TestParsedConfig1>>(json, JsonArgs(),
75 errors);
76 }
77 };
78
79 class TestParsedConfig2 : public ServiceConfigParser::ParsedConfig {
80 public:
value() const81 uint32_t value() const { return value_; }
82
JsonLoader(const JsonArgs &)83 static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
84 static const auto* loader =
85 JsonObjectLoader<TestParsedConfig2>()
86 .OptionalField("method_param", &TestParsedConfig2::value_)
87 .Finish();
88 return loader;
89 }
90
91 private:
92 uint32_t value_;
93 };
94
95 class TestParser2 : public ServiceConfigParser::Parser {
96 public:
name() const97 absl::string_view name() const override { return "test_parser_2"; }
98
ParsePerMethodParams(const ChannelArgs & args,const Json & json,ValidationErrors * errors)99 std::unique_ptr<ServiceConfigParser::ParsedConfig> ParsePerMethodParams(
100 const ChannelArgs& args, const Json& json,
101 ValidationErrors* errors) override {
102 if (args.GetBool(GRPC_ARG_DISABLE_PARSING).value_or(false)) {
103 return nullptr;
104 }
105 return LoadFromJson<std::unique_ptr<TestParsedConfig2>>(json, JsonArgs(),
106 errors);
107 }
108 };
109
110 class ServiceConfigTest : public ::testing::Test {
111 protected:
SetUp()112 void SetUp() override {
113 builder_ = std::make_unique<CoreConfiguration::WithSubstituteBuilder>(
114 [](CoreConfiguration::Builder* builder) {
115 builder->service_config_parser()->RegisterParser(
116 std::make_unique<TestParser1>());
117 builder->service_config_parser()->RegisterParser(
118 std::make_unique<TestParser2>());
119 });
120 EXPECT_EQ(CoreConfiguration::Get().service_config_parser().GetParserIndex(
121 "test_parser_1"),
122 0);
123 EXPECT_EQ(CoreConfiguration::Get().service_config_parser().GetParserIndex(
124 "test_parser_2"),
125 1);
126 }
127
128 private:
129 std::unique_ptr<CoreConfiguration::WithSubstituteBuilder> builder_;
130 };
131
TEST_F(ServiceConfigTest,JsonParseError)132 TEST_F(ServiceConfigTest, JsonParseError) {
133 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), "");
134 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
135 EXPECT_THAT(std::string(service_config.status().message()),
136 ::testing::StartsWith("JSON parsing failed"))
137 << service_config.status();
138 }
139
TEST_F(ServiceConfigTest,EmptyConfig)140 TEST_F(ServiceConfigTest, EmptyConfig) {
141 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), "{}");
142 ASSERT_TRUE(service_config.ok()) << service_config.status();
143 EXPECT_EQ((*service_config)->json_string(), "{}");
144 }
145
TEST_F(ServiceConfigTest,SkipMethodConfigWithNoNameOrEmptyName)146 TEST_F(ServiceConfigTest, SkipMethodConfigWithNoNameOrEmptyName) {
147 const char* test_json =
148 "{\"methodConfig\": ["
149 " {\"method_param\":1},"
150 " {\"name\":[], \"method_param\":1},"
151 " {\"name\":[{\"service\":\"TestServ\"}], \"method_param\":2}"
152 "]}";
153 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
154 ASSERT_TRUE(service_config.ok()) << service_config.status();
155 auto vector_ptr =
156 (*service_config)
157 ->GetMethodParsedConfigVector(
158 grpc_slice_from_static_string("/TestServ/TestMethod"));
159 ASSERT_EQ(vector_ptr->size(), 2UL);
160 auto parsed_config = ((*vector_ptr)[1]).get();
161 EXPECT_EQ(static_cast<TestParsedConfig1*>(parsed_config)->value(), 2);
162 }
163
TEST_F(ServiceConfigTest,ErrorDuplicateMethodConfigNames)164 TEST_F(ServiceConfigTest, ErrorDuplicateMethodConfigNames) {
165 const char* test_json =
166 "{\"methodConfig\": ["
167 " {\"name\":[{\"service\":\"TestServ\"}]},"
168 " {\"name\":[{\"service\":\"TestServ\"}]}"
169 "]}";
170 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
171 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
172 EXPECT_EQ(service_config.status().message(),
173 "errors validating service config: ["
174 "field:methodConfig[1].name[0] "
175 "error:multiple method configs for path /TestServ/]")
176 << service_config.status();
177 }
178
TEST_F(ServiceConfigTest,ErrorDuplicateMethodConfigNamesWithNullMethod)179 TEST_F(ServiceConfigTest, ErrorDuplicateMethodConfigNamesWithNullMethod) {
180 const char* test_json =
181 "{\"methodConfig\": ["
182 " {\"name\":[{\"service\":\"TestServ\",\"method\":null}]},"
183 " {\"name\":[{\"service\":\"TestServ\"}]}"
184 "]}";
185 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
186 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
187 EXPECT_EQ(service_config.status().message(),
188 "errors validating service config: ["
189 "field:methodConfig[1].name[0] "
190 "error:multiple method configs for path /TestServ/]")
191 << service_config.status();
192 }
193
TEST_F(ServiceConfigTest,ErrorDuplicateMethodConfigNamesWithEmptyMethod)194 TEST_F(ServiceConfigTest, ErrorDuplicateMethodConfigNamesWithEmptyMethod) {
195 const char* test_json =
196 "{\"methodConfig\": ["
197 " {\"name\":[{\"service\":\"TestServ\",\"method\":\"\"}]},"
198 " {\"name\":[{\"service\":\"TestServ\"}]}"
199 "]}";
200 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
201 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
202 EXPECT_EQ(service_config.status().message(),
203 "errors validating service config: ["
204 "field:methodConfig[1].name[0] "
205 "error:multiple method configs for path /TestServ/]")
206 << service_config.status();
207 }
208
TEST_F(ServiceConfigTest,ErrorDuplicateDefaultMethodConfigs)209 TEST_F(ServiceConfigTest, ErrorDuplicateDefaultMethodConfigs) {
210 const char* test_json =
211 "{\"methodConfig\": ["
212 " {\"name\":[{}]},"
213 " {\"name\":[{}]}"
214 "]}";
215 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
216 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
217 EXPECT_EQ(service_config.status().message(),
218 "errors validating service config: ["
219 "field:methodConfig[1].name[0] "
220 "error:duplicate default method config]")
221 << service_config.status();
222 }
223
TEST_F(ServiceConfigTest,ErrorDuplicateDefaultMethodConfigsWithNullService)224 TEST_F(ServiceConfigTest, ErrorDuplicateDefaultMethodConfigsWithNullService) {
225 const char* test_json =
226 "{\"methodConfig\": ["
227 " {\"name\":[{\"service\":null}]},"
228 " {\"name\":[{}]}"
229 "]}";
230 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
231 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
232 EXPECT_EQ(service_config.status().message(),
233 "errors validating service config: ["
234 "field:methodConfig[1].name[0] "
235 "error:duplicate default method config]")
236 << service_config.status();
237 }
238
TEST_F(ServiceConfigTest,ErrorDuplicateDefaultMethodConfigsWithEmptyService)239 TEST_F(ServiceConfigTest, ErrorDuplicateDefaultMethodConfigsWithEmptyService) {
240 const char* test_json =
241 "{\"methodConfig\": ["
242 " {\"name\":[{\"service\":\"\"}]},"
243 " {\"name\":[{}]}"
244 "]}";
245 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
246 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
247 EXPECT_EQ(service_config.status().message(),
248 "errors validating service config: ["
249 "field:methodConfig[1].name[0] "
250 "error:duplicate default method config]")
251 << service_config.status();
252 }
253
TEST_F(ServiceConfigTest,ValidMethodConfig)254 TEST_F(ServiceConfigTest, ValidMethodConfig) {
255 const char* test_json =
256 "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}]}]}";
257 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
258 ASSERT_TRUE(service_config.ok()) << service_config.status();
259 }
260
TEST_F(ServiceConfigTest,Parser1BasicTest1)261 TEST_F(ServiceConfigTest, Parser1BasicTest1) {
262 const char* test_json = "{\"global_param\":5}";
263 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
264 ASSERT_TRUE(service_config.ok()) << service_config.status();
265 EXPECT_EQ((static_cast<TestParsedConfig1*>(
266 (*service_config)->GetGlobalParsedConfig(0)))
267 ->value(),
268 5);
269 EXPECT_EQ((*service_config)
270 ->GetMethodParsedConfigVector(
271 grpc_slice_from_static_string("/TestServ/TestMethod")),
272 nullptr);
273 }
274
TEST_F(ServiceConfigTest,Parser1BasicTest2)275 TEST_F(ServiceConfigTest, Parser1BasicTest2) {
276 const char* test_json = "{\"global_param\":1000}";
277 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
278 ASSERT_TRUE(service_config.ok()) << service_config.status();
279 EXPECT_EQ((static_cast<TestParsedConfig1*>(
280 (*service_config)->GetGlobalParsedConfig(0)))
281 ->value(),
282 1000);
283 }
284
TEST_F(ServiceConfigTest,Parser1DisabledViaChannelArg)285 TEST_F(ServiceConfigTest, Parser1DisabledViaChannelArg) {
286 const ChannelArgs args = ChannelArgs().Set(GRPC_ARG_DISABLE_PARSING, 1);
287 const char* test_json = "{\"global_param\":5}";
288 auto service_config = ServiceConfigImpl::Create(args, test_json);
289 ASSERT_TRUE(service_config.ok()) << service_config.status();
290 EXPECT_EQ((*service_config)->GetGlobalParsedConfig(0), nullptr);
291 }
292
TEST_F(ServiceConfigTest,Parser1ErrorInvalidType)293 TEST_F(ServiceConfigTest, Parser1ErrorInvalidType) {
294 const char* test_json = "{\"global_param\":[]}";
295 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
296 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
297 EXPECT_EQ(service_config.status().message(),
298 "errors validating service config: ["
299 "field:global_param error:is not a number]")
300 << service_config.status();
301 }
302
TEST_F(ServiceConfigTest,Parser1ErrorInvalidValue)303 TEST_F(ServiceConfigTest, Parser1ErrorInvalidValue) {
304 const char* test_json = "{\"global_param\":-5}";
305 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
306 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
307 EXPECT_EQ(service_config.status().message(),
308 "errors validating service config: ["
309 "field:global_param error:failed to parse non-negative number]")
310 << service_config.status();
311 }
312
TEST_F(ServiceConfigTest,Parser2BasicTest)313 TEST_F(ServiceConfigTest, Parser2BasicTest) {
314 const char* test_json =
315 "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], "
316 "\"method_param\":5}]}";
317 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
318 ASSERT_TRUE(service_config.ok()) << service_config.status();
319 const auto* vector_ptr =
320 (*service_config)
321 ->GetMethodParsedConfigVector(
322 grpc_slice_from_static_string("/TestServ/TestMethod"));
323 ASSERT_NE(vector_ptr, nullptr);
324 auto parsed_config = ((*vector_ptr)[1]).get();
325 EXPECT_EQ(static_cast<TestParsedConfig1*>(parsed_config)->value(), 5);
326 }
327
TEST_F(ServiceConfigTest,Parser2DisabledViaChannelArg)328 TEST_F(ServiceConfigTest, Parser2DisabledViaChannelArg) {
329 const ChannelArgs args = ChannelArgs().Set(GRPC_ARG_DISABLE_PARSING, 1);
330 const char* test_json =
331 "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], "
332 "\"method_param\":5}]}";
333 auto service_config = ServiceConfigImpl::Create(args, test_json);
334 ASSERT_TRUE(service_config.ok()) << service_config.status();
335 const auto* vector_ptr =
336 (*service_config)
337 ->GetMethodParsedConfigVector(
338 grpc_slice_from_static_string("/TestServ/TestMethod"));
339 ASSERT_NE(vector_ptr, nullptr);
340 auto parsed_config = ((*vector_ptr)[1]).get();
341 EXPECT_EQ(parsed_config, nullptr);
342 }
343
TEST_F(ServiceConfigTest,Parser2ErrorInvalidType)344 TEST_F(ServiceConfigTest, Parser2ErrorInvalidType) {
345 const char* test_json =
346 "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], "
347 "\"method_param\":[]}]}";
348 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
349 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
350 EXPECT_EQ(service_config.status().message(),
351 "errors validating service config: ["
352 "field:methodConfig[0].method_param error:is not a number]")
353 << service_config.status();
354 }
355
TEST_F(ServiceConfigTest,Parser2ErrorInvalidValue)356 TEST_F(ServiceConfigTest, Parser2ErrorInvalidValue) {
357 const char* test_json =
358 "{\"methodConfig\": [{\"name\":[{\"service\":\"TestServ\"}], "
359 "\"method_param\":-5}]}";
360 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
361 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
362 EXPECT_EQ(service_config.status().message(),
363 "errors validating service config: ["
364 "field:methodConfig[0].method_param "
365 "error:failed to parse non-negative number]")
366 << service_config.status();
367 }
368
TEST(ServiceConfigParserDeathTest,DoubleRegistration)369 TEST(ServiceConfigParserDeathTest, DoubleRegistration) {
370 GTEST_FLAG_SET(death_test_style, "threadsafe");
371 CoreConfiguration::Reset();
372 ASSERT_DEATH_IF_SUPPORTED(
373 CoreConfiguration::WithSubstituteBuilder builder(
374 [](CoreConfiguration::Builder* builder) {
375 builder->service_config_parser()->RegisterParser(
376 std::make_unique<TestParser1>());
377 builder->service_config_parser()->RegisterParser(
378 std::make_unique<TestParser1>());
379 }),
380 "test_parser_1.*already registered");
381 }
382
383 // This parser always adds errors
384 class ErrorParser : public ServiceConfigParser::Parser {
385 public:
ErrorParser(absl::string_view name)386 explicit ErrorParser(absl::string_view name) : name_(name) {}
387
name() const388 absl::string_view name() const override { return name_; }
389
ParsePerMethodParams(const ChannelArgs &,const Json &,ValidationErrors * errors)390 std::unique_ptr<ServiceConfigParser::ParsedConfig> ParsePerMethodParams(
391 const ChannelArgs& /*arg*/, const Json& /*json*/,
392 ValidationErrors* errors) override {
393 ValidationErrors::ScopedField field(errors, absl::StrCat(".", name_));
394 errors->AddError("method error");
395 return nullptr;
396 }
397
ParseGlobalParams(const ChannelArgs &,const Json &,ValidationErrors * errors)398 std::unique_ptr<ServiceConfigParser::ParsedConfig> ParseGlobalParams(
399 const ChannelArgs& /*arg*/, const Json& /*json*/,
400 ValidationErrors* errors) override {
401 ValidationErrors::ScopedField field(errors, absl::StrCat(".", name_));
402 errors->AddError("global error");
403 return nullptr;
404 }
405
406 private:
407 absl::string_view name_;
408 };
409
410 // Test parsing with ErrorParsers which always add errors
411 class ErroredParsersScopingTest : public ::testing::Test {
412 protected:
SetUp()413 void SetUp() override {
414 builder_ = std::make_unique<CoreConfiguration::WithSubstituteBuilder>(
415 [](CoreConfiguration::Builder* builder) {
416 builder->service_config_parser()->RegisterParser(
417 std::make_unique<ErrorParser>("ep1"));
418 builder->service_config_parser()->RegisterParser(
419 std::make_unique<ErrorParser>("ep2"));
420 });
421 EXPECT_EQ(
422 CoreConfiguration::Get().service_config_parser().GetParserIndex("ep1"),
423 0);
424 EXPECT_EQ(
425 CoreConfiguration::Get().service_config_parser().GetParserIndex("ep2"),
426 1);
427 }
428
429 private:
430 std::unique_ptr<CoreConfiguration::WithSubstituteBuilder> builder_;
431 };
432
TEST_F(ErroredParsersScopingTest,GlobalParams)433 TEST_F(ErroredParsersScopingTest, GlobalParams) {
434 const char* test_json = "{}";
435 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
436 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
437 EXPECT_EQ(service_config.status().message(),
438 "errors validating service config: ["
439 "field:ep1 error:global error; field:ep2 error:global error]")
440 << service_config.status();
441 }
442
TEST_F(ErroredParsersScopingTest,MethodParams)443 TEST_F(ErroredParsersScopingTest, MethodParams) {
444 const char* test_json = "{\"methodConfig\": [{}]}";
445 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
446 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
447 EXPECT_EQ(service_config.status().message(),
448 "errors validating service config: ["
449 "field:ep1 error:global error; "
450 "field:ep2 error:global error; "
451 "field:methodConfig[0].ep1 error:method error; "
452 "field:methodConfig[0].ep2 error:method error]")
453 << service_config.status();
454 }
455
456 } // namespace testing
457 } // namespace grpc_core
458
main(int argc,char ** argv)459 int main(int argc, char** argv) {
460 ::testing::InitGoogleTest(&argc, argv);
461 grpc::testing::TestEnvironment env(&argc, argv);
462 grpc_init();
463 int ret = RUN_ALL_TESTS();
464 grpc_shutdown();
465 return ret;
466 }
467