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/client_channel/client_channel_service_config.h"
18
19 #include <grpc/grpc.h>
20 #include <grpc/slice.h>
21
22 #include "absl/status/status.h"
23 #include "absl/status/statusor.h"
24 #include "gtest/gtest.h"
25 #include "src/core/config/core_configuration.h"
26 #include "src/core/lib/channel/channel_args.h"
27 #include "src/core/service_config/service_config.h"
28 #include "src/core/service_config/service_config_impl.h"
29 #include "src/core/service_config/service_config_parser.h"
30 #include "src/core/util/time.h"
31 #include "test/core/test_util/test_config.h"
32
33 namespace grpc_core {
34 namespace testing {
35
36 // A regular expression to enter referenced or child errors.
37 #define CHILD_ERROR_TAG ".*children.*"
38
39 class ClientChannelParserTest : public ::testing::Test {
40 protected:
SetUp()41 void SetUp() override {
42 parser_index_ =
43 CoreConfiguration::Get().service_config_parser().GetParserIndex(
44 "client_channel");
45 }
46
47 size_t parser_index_;
48 };
49
TEST_F(ClientChannelParserTest,ValidLoadBalancingConfigPickFirst)50 TEST_F(ClientChannelParserTest, ValidLoadBalancingConfigPickFirst) {
51 const char* test_json = "{\"loadBalancingConfig\": [{\"pick_first\":{}}]}";
52 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
53 ASSERT_TRUE(service_config.ok()) << service_config.status();
54 const auto* parsed_config =
55 static_cast<internal::ClientChannelGlobalParsedConfig*>(
56 (*service_config)->GetGlobalParsedConfig(parser_index_));
57 auto lb_config = parsed_config->parsed_lb_config();
58 EXPECT_EQ(lb_config->name(), "pick_first");
59 }
60
TEST_F(ClientChannelParserTest,ValidLoadBalancingConfigRoundRobin)61 TEST_F(ClientChannelParserTest, ValidLoadBalancingConfigRoundRobin) {
62 const char* test_json =
63 "{\"loadBalancingConfig\": [{\"round_robin\":{}}, {}]}";
64 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
65 ASSERT_TRUE(service_config.ok()) << service_config.status();
66 auto parsed_config = static_cast<internal::ClientChannelGlobalParsedConfig*>(
67 (*service_config)->GetGlobalParsedConfig(parser_index_));
68 auto lb_config = parsed_config->parsed_lb_config();
69 EXPECT_EQ(lb_config->name(), "round_robin");
70 }
71
TEST_F(ClientChannelParserTest,ValidLoadBalancingConfigGrpclb)72 TEST_F(ClientChannelParserTest, ValidLoadBalancingConfigGrpclb) {
73 const char* test_json =
74 "{\"loadBalancingConfig\": "
75 "[{\"grpclb\":{\"childPolicy\":[{\"pick_first\":{}}]}}]}";
76 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
77 ASSERT_TRUE(service_config.ok()) << service_config.status();
78 const auto* parsed_config =
79 static_cast<internal::ClientChannelGlobalParsedConfig*>(
80 (*service_config)->GetGlobalParsedConfig(parser_index_));
81 auto lb_config = parsed_config->parsed_lb_config();
82 EXPECT_EQ(lb_config->name(), "grpclb");
83 }
84
TEST_F(ClientChannelParserTest,UnknownLoadBalancingConfig)85 TEST_F(ClientChannelParserTest, UnknownLoadBalancingConfig) {
86 const char* test_json = "{\"loadBalancingConfig\": [{\"unknown\":{}}]}";
87 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
88 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
89 EXPECT_EQ(service_config.status().message(),
90 "errors validating service config: ["
91 "field:loadBalancingConfig error:"
92 "No known policies in list: unknown]")
93 << service_config.status();
94 }
95
TEST_F(ClientChannelParserTest,InvalidGrpclbLoadBalancingConfig)96 TEST_F(ClientChannelParserTest, InvalidGrpclbLoadBalancingConfig) {
97 const char* test_json =
98 "{\"loadBalancingConfig\": ["
99 " {\"grpclb\":{\"childPolicy\":1}},"
100 " {\"round_robin\":{}}"
101 "]}";
102 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
103 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
104 EXPECT_EQ(service_config.status().message(),
105 "errors validating service config: ["
106 "field:loadBalancingConfig error:"
107 "errors validating grpclb LB policy config: ["
108 "field:childPolicy error:type should be array]]")
109 << service_config.status();
110 }
111
TEST_F(ClientChannelParserTest,ValidLoadBalancingPolicy)112 TEST_F(ClientChannelParserTest, ValidLoadBalancingPolicy) {
113 const char* test_json = "{\"loadBalancingPolicy\":\"pick_first\"}";
114 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
115 ASSERT_TRUE(service_config.ok()) << service_config.status();
116 const auto* parsed_config =
117 static_cast<internal::ClientChannelGlobalParsedConfig*>(
118 (*service_config)->GetGlobalParsedConfig(parser_index_));
119 EXPECT_EQ(parsed_config->parsed_deprecated_lb_policy(), "pick_first");
120 }
121
TEST_F(ClientChannelParserTest,ValidLoadBalancingPolicyAllCaps)122 TEST_F(ClientChannelParserTest, ValidLoadBalancingPolicyAllCaps) {
123 const char* test_json = "{\"loadBalancingPolicy\":\"PICK_FIRST\"}";
124 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
125 ASSERT_TRUE(service_config.ok()) << service_config.status();
126 const auto* parsed_config =
127 static_cast<internal::ClientChannelGlobalParsedConfig*>(
128 (*service_config)->GetGlobalParsedConfig(parser_index_));
129 EXPECT_EQ(parsed_config->parsed_deprecated_lb_policy(), "pick_first");
130 }
131
TEST_F(ClientChannelParserTest,UnknownLoadBalancingPolicy)132 TEST_F(ClientChannelParserTest, UnknownLoadBalancingPolicy) {
133 const char* test_json = "{\"loadBalancingPolicy\":\"unknown\"}";
134 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
135 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
136 EXPECT_EQ(service_config.status().message(),
137 "errors validating service config: ["
138 "field:loadBalancingPolicy error:unknown LB policy \"unknown\"]")
139 << service_config.status();
140 }
141
TEST_F(ClientChannelParserTest,LegacyLoadBalancingPolicySpecifiesPolicyThatRequiresConfig)142 TEST_F(ClientChannelParserTest,
143 LegacyLoadBalancingPolicySpecifiesPolicyThatRequiresConfig) {
144 const char* test_json = "{\"loadBalancingPolicy\":\"rls_experimental\"}";
145 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
146 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
147 EXPECT_EQ(service_config.status().message(),
148 "errors validating service config: ["
149 "field:loadBalancingPolicy error:LB policy "
150 "\"rls_experimental\" requires a config. Please "
151 "use loadBalancingConfig instead.]")
152 << service_config.status();
153 }
154
TEST_F(ClientChannelParserTest,ValidTimeout)155 TEST_F(ClientChannelParserTest, ValidTimeout) {
156 const char* test_json =
157 "{\n"
158 " \"methodConfig\": [ {\n"
159 " \"name\": [\n"
160 " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
161 " ],\n"
162 " \"timeout\": \"5s\"\n"
163 " } ]\n"
164 "}";
165 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
166 ASSERT_TRUE(service_config.ok()) << service_config.status();
167 const auto* vector_ptr =
168 (*service_config)
169 ->GetMethodParsedConfigVector(
170 grpc_slice_from_static_string("/TestServ/TestMethod"));
171 ASSERT_NE(vector_ptr, nullptr);
172 auto parsed_config = ((*vector_ptr)[parser_index_]).get();
173 EXPECT_EQ(
174 (static_cast<internal::ClientChannelMethodParsedConfig*>(parsed_config))
175 ->timeout(),
176 Duration::Seconds(5));
177 }
178
TEST_F(ClientChannelParserTest,InvalidTimeout)179 TEST_F(ClientChannelParserTest, InvalidTimeout) {
180 const char* test_json =
181 "{\n"
182 " \"methodConfig\": [ {\n"
183 " \"name\": [\n"
184 " { \"service\": \"service\", \"method\": \"method\" }\n"
185 " ],\n"
186 " \"timeout\": \"5sec\"\n"
187 " } ]\n"
188 "}";
189 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
190 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
191 EXPECT_EQ(service_config.status().message(),
192 "errors validating service config: ["
193 "field:methodConfig[0].timeout "
194 "error:Not a duration (no s suffix)]")
195 << service_config.status();
196 }
197
TEST_F(ClientChannelParserTest,ValidWaitForReady)198 TEST_F(ClientChannelParserTest, ValidWaitForReady) {
199 const char* test_json =
200 "{\n"
201 " \"methodConfig\": [ {\n"
202 " \"name\": [\n"
203 " { \"service\": \"TestServ\", \"method\": \"TestMethod\" }\n"
204 " ],\n"
205 " \"waitForReady\": true\n"
206 " } ]\n"
207 "}";
208 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
209 ASSERT_TRUE(service_config.ok()) << service_config.status();
210 const auto* vector_ptr =
211 (*service_config)
212 ->GetMethodParsedConfigVector(
213 grpc_slice_from_static_string("/TestServ/TestMethod"));
214 ASSERT_NE(vector_ptr, nullptr);
215 auto parsed_config = ((*vector_ptr)[parser_index_]).get();
216 ASSERT_TRUE(
217 (static_cast<internal::ClientChannelMethodParsedConfig*>(parsed_config))
218 ->wait_for_ready()
219 .has_value());
220 EXPECT_TRUE(
221 (static_cast<internal::ClientChannelMethodParsedConfig*>(parsed_config))
222 ->wait_for_ready()
223 .value());
224 }
225
TEST_F(ClientChannelParserTest,InvalidWaitForReady)226 TEST_F(ClientChannelParserTest, InvalidWaitForReady) {
227 const char* test_json =
228 "{\n"
229 " \"methodConfig\": [ {\n"
230 " \"name\": [\n"
231 " { \"service\": \"service\", \"method\": \"method\" }\n"
232 " ],\n"
233 " \"waitForReady\": \"true\"\n"
234 " } ]\n"
235 "}";
236 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
237 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
238 EXPECT_EQ(service_config.status().message(),
239 "errors validating service config: ["
240 "field:methodConfig[0].waitForReady error:is not a boolean]")
241 << service_config.status();
242 }
243
TEST_F(ClientChannelParserTest,ValidHealthCheck)244 TEST_F(ClientChannelParserTest, ValidHealthCheck) {
245 const char* test_json =
246 "{\n"
247 " \"healthCheckConfig\": {\n"
248 " \"serviceName\": \"health_check_service_name\"\n"
249 " }\n"
250 "}";
251 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
252 ASSERT_TRUE(service_config.ok()) << service_config.status();
253 const auto* parsed_config =
254 static_cast<internal::ClientChannelGlobalParsedConfig*>(
255 (*service_config)->GetGlobalParsedConfig(parser_index_));
256 ASSERT_NE(parsed_config, nullptr);
257 EXPECT_EQ(parsed_config->health_check_service_name(),
258 "health_check_service_name");
259 }
260
TEST_F(ClientChannelParserTest,InvalidHealthCheckMultipleEntries)261 TEST_F(ClientChannelParserTest, InvalidHealthCheckMultipleEntries) {
262 const char* test_json =
263 "{\n"
264 " \"healthCheckConfig\": {\n"
265 " \"serviceName\": \"health_check_service_name\"\n"
266 " },\n"
267 " \"healthCheckConfig\": {\n"
268 " \"serviceName\": \"health_check_service_name1\"\n"
269 " }\n"
270 "}";
271 auto service_config = ServiceConfigImpl::Create(ChannelArgs(), test_json);
272 EXPECT_EQ(service_config.status().code(), absl::StatusCode::kInvalidArgument);
273 EXPECT_EQ(service_config.status().message(),
274 "JSON parsing failed: ["
275 "duplicate key \"healthCheckConfig\" at index 82]")
276 << service_config.status();
277 }
278
279 } // namespace testing
280 } // namespace grpc_core
281
main(int argc,char ** argv)282 int main(int argc, char** argv) {
283 ::testing::InitGoogleTest(&argc, argv);
284 grpc::testing::TestEnvironment env(&argc, argv);
285 grpc_init();
286 int ret = RUN_ALL_TESTS();
287 grpc_shutdown();
288 return ret;
289 }
290