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