• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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