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