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 <regex>
18
19 #include <gmock/gmock.h>
20 #include <gtest/gtest.h>
21
22 #include <grpc/grpc.h>
23 #include <grpc/slice.h>
24
25 #include "src/core/ext/filters/client_channel/xds/xds_bootstrap.h"
26 #include "test/core/util/test_config.h"
27
28 namespace grpc_core {
29 namespace testing {
30
VerifyRegexMatch(grpc_error * error,const std::regex & e)31 void VerifyRegexMatch(grpc_error* error, const std::regex& e) {
32 std::smatch match;
33 std::string s(grpc_error_string(error));
34 EXPECT_TRUE(std::regex_search(s, match, e));
35 GRPC_ERROR_UNREF(error);
36 }
37
TEST(XdsBootstrapTest,Basic)38 TEST(XdsBootstrapTest, Basic) {
39 const char* json_str =
40 "{"
41 " \"xds_servers\": ["
42 " {"
43 " \"server_uri\": \"fake:///lb\","
44 " \"channel_creds\": ["
45 " {"
46 " \"type\": \"fake\","
47 " \"ignore\": 0"
48 " }"
49 " ],"
50 " \"ignore\": 0"
51 " },"
52 " {"
53 " \"server_uri\": \"ignored\","
54 " \"channel_creds\": ["
55 " {"
56 " \"type\": \"ignored\","
57 " \"ignore\": 0"
58 " }"
59 " ],"
60 " \"ignore\": 0"
61 " }"
62 " ],"
63 " \"node\": {"
64 " \"id\": \"foo\","
65 " \"cluster\": \"bar\","
66 " \"locality\": {"
67 " \"region\": \"milky_way\","
68 " \"zone\": \"sol_system\","
69 " \"subzone\": \"earth\","
70 " \"ignore\": {}"
71 " },"
72 " \"metadata\": {"
73 " \"foo\": 1,"
74 " \"bar\": 2"
75 " },"
76 " \"ignore\": \"whee\""
77 " },"
78 " \"ignore\": {}"
79 "}";
80 grpc_error* error = GRPC_ERROR_NONE;
81 Json json = Json::Parse(json_str, &error);
82 ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error);
83 grpc_core::XdsBootstrap bootstrap(std::move(json), &error);
84 EXPECT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error);
85 EXPECT_EQ(bootstrap.server().server_uri, "fake:///lb");
86 ASSERT_EQ(bootstrap.server().channel_creds.size(), 1UL);
87 EXPECT_EQ(bootstrap.server().channel_creds[0].type, "fake");
88 EXPECT_EQ(bootstrap.server().channel_creds[0].config.type(),
89 Json::Type::JSON_NULL);
90 ASSERT_NE(bootstrap.node(), nullptr);
91 EXPECT_EQ(bootstrap.node()->id, "foo");
92 EXPECT_EQ(bootstrap.node()->cluster, "bar");
93 EXPECT_EQ(bootstrap.node()->locality_region, "milky_way");
94 EXPECT_EQ(bootstrap.node()->locality_zone, "sol_system");
95 EXPECT_EQ(bootstrap.node()->locality_subzone, "earth");
96 ASSERT_EQ(bootstrap.node()->metadata.type(), Json::Type::OBJECT);
97 EXPECT_THAT(bootstrap.node()->metadata.object_value(),
98 ::testing::ElementsAre(
99 ::testing::Pair(
100 ::testing::Eq("bar"),
101 ::testing::AllOf(
102 ::testing::Property(&Json::type, Json::Type::NUMBER),
103 ::testing::Property(&Json::string_value, "2"))),
104 ::testing::Pair(
105 ::testing::Eq("foo"),
106 ::testing::AllOf(
107 ::testing::Property(&Json::type, Json::Type::NUMBER),
108 ::testing::Property(&Json::string_value, "1")))));
109 }
110
TEST(XdsBootstrapTest,ValidWithoutChannelCredsAndNode)111 TEST(XdsBootstrapTest, ValidWithoutChannelCredsAndNode) {
112 const char* json_str =
113 "{"
114 " \"xds_servers\": ["
115 " {"
116 " \"server_uri\": \"fake:///lb\""
117 " }"
118 " ]"
119 "}";
120 grpc_error* error = GRPC_ERROR_NONE;
121 Json json = Json::Parse(json_str, &error);
122 ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error);
123 grpc_core::XdsBootstrap bootstrap(std::move(json), &error);
124 EXPECT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error);
125 EXPECT_EQ(bootstrap.server().server_uri, "fake:///lb");
126 EXPECT_EQ(bootstrap.server().channel_creds.size(), 0UL);
127 EXPECT_EQ(bootstrap.node(), nullptr);
128 }
129
TEST(XdsBootstrapTest,MissingXdsServers)130 TEST(XdsBootstrapTest, MissingXdsServers) {
131 grpc_error* error = GRPC_ERROR_NONE;
132 Json json = Json::Parse("{}", &error);
133 ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error);
134 grpc_core::XdsBootstrap bootstrap(std::move(json), &error);
135 gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
136 ASSERT_TRUE(error != GRPC_ERROR_NONE);
137 std::regex e(std::string("\"xds_servers\" field not present"));
138 VerifyRegexMatch(error, e);
139 }
140
TEST(XdsBootstrapTest,TopFieldsWrongTypes)141 TEST(XdsBootstrapTest, TopFieldsWrongTypes) {
142 const char* json_str =
143 "{"
144 " \"xds_servers\":1,"
145 " \"node\":1"
146 "}";
147 grpc_error* error = GRPC_ERROR_NONE;
148 Json json = Json::Parse(json_str, &error);
149 ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error);
150 grpc_core::XdsBootstrap bootstrap(std::move(json), &error);
151 gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
152 ASSERT_TRUE(error != GRPC_ERROR_NONE);
153 std::regex e(
154 std::string("\"xds_servers\" field is not an array(.*)"
155 "\"node\" field is not an object"));
156 VerifyRegexMatch(error, e);
157 }
158
TEST(XdsBootstrapTest,XdsServerMissingServerUri)159 TEST(XdsBootstrapTest, XdsServerMissingServerUri) {
160 const char* json_str =
161 "{"
162 " \"xds_servers\":[{}]"
163 "}";
164 grpc_error* error = GRPC_ERROR_NONE;
165 Json json = Json::Parse(json_str, &error);
166 ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error);
167 grpc_core::XdsBootstrap bootstrap(std::move(json), &error);
168 gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
169 ASSERT_TRUE(error != GRPC_ERROR_NONE);
170 std::regex e(
171 std::string("errors parsing \"xds_servers\" array(.*)"
172 "errors parsing index 0(.*)"
173 "\"server_uri\" field not present"));
174 VerifyRegexMatch(error, e);
175 }
176
TEST(XdsBootstrapTest,XdsServerUriAndCredsWrongTypes)177 TEST(XdsBootstrapTest, XdsServerUriAndCredsWrongTypes) {
178 const char* json_str =
179 "{"
180 " \"xds_servers\":["
181 " {"
182 " \"server_uri\":1,"
183 " \"channel_creds\":1"
184 " }"
185 " ]"
186 "}";
187 grpc_error* error = GRPC_ERROR_NONE;
188 Json json = Json::Parse(json_str, &error);
189 ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error);
190 grpc_core::XdsBootstrap bootstrap(std::move(json), &error);
191 gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
192 ASSERT_TRUE(error != GRPC_ERROR_NONE);
193 std::regex e(
194 std::string("errors parsing \"xds_servers\" array(.*)"
195 "errors parsing index 0(.*)"
196 "\"server_uri\" field is not a string(.*)"
197 "\"channel_creds\" field is not an array"));
198 VerifyRegexMatch(error, e);
199 }
200
TEST(XdsBootstrapTest,ChannelCredsFieldsWrongTypes)201 TEST(XdsBootstrapTest, ChannelCredsFieldsWrongTypes) {
202 const char* json_str =
203 "{"
204 " \"xds_servers\":["
205 " {"
206 " \"server_uri\":\"foo\","
207 " \"channel_creds\":["
208 " {"
209 " \"type\":0,"
210 " \"config\":1"
211 " }"
212 " ]"
213 " }"
214 " ]"
215 "}";
216 grpc_error* error = GRPC_ERROR_NONE;
217 Json json = Json::Parse(json_str, &error);
218 ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error);
219 grpc_core::XdsBootstrap bootstrap(std::move(json), &error);
220 gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
221 ASSERT_TRUE(error != GRPC_ERROR_NONE);
222 std::regex e(
223 std::string("errors parsing \"xds_servers\" array(.*)"
224 "errors parsing index 0(.*)"
225 "errors parsing \"channel_creds\" array(.*)"
226 "errors parsing index 0(.*)"
227 "\"type\" field is not a string(.*)"
228 "\"config\" field is not an object"));
229 VerifyRegexMatch(error, e);
230 }
231
TEST(XdsBootstrapTest,NodeFieldsWrongTypes)232 TEST(XdsBootstrapTest, NodeFieldsWrongTypes) {
233 const char* json_str =
234 "{"
235 " \"node\":{"
236 " \"id\":0,"
237 " \"cluster\":0,"
238 " \"locality\":0,"
239 " \"metadata\":0"
240 " }"
241 "}";
242 grpc_error* error = GRPC_ERROR_NONE;
243 Json json = Json::Parse(json_str, &error);
244 ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error);
245 grpc_core::XdsBootstrap bootstrap(std::move(json), &error);
246 gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
247 ASSERT_TRUE(error != GRPC_ERROR_NONE);
248 std::regex e(
249 std::string("errors parsing \"node\" object(.*)"
250 "\"id\" field is not a string(.*)"
251 "\"cluster\" field is not a string(.*)"
252 "\"locality\" field is not an object(.*)"
253 "\"metadata\" field is not an object"));
254 VerifyRegexMatch(error, e);
255 }
256
TEST(XdsBootstrapTest,LocalityFieldsWrongType)257 TEST(XdsBootstrapTest, LocalityFieldsWrongType) {
258 const char* json_str =
259 "{"
260 " \"node\":{"
261 " \"locality\":{"
262 " \"region\":0,"
263 " \"zone\":0,"
264 " \"subzone\":0"
265 " }"
266 " }"
267 "}";
268 grpc_error* error = GRPC_ERROR_NONE;
269 Json json = Json::Parse(json_str, &error);
270 ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error);
271 grpc_core::XdsBootstrap bootstrap(std::move(json), &error);
272 gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
273 ASSERT_TRUE(error != GRPC_ERROR_NONE);
274 std::regex e(
275 std::string("errors parsing \"node\" object(.*)"
276 "errors parsing \"locality\" object(.*)"
277 "\"region\" field is not a string(.*)"
278 "\"zone\" field is not a string(.*)"
279 "\"subzone\" field is not a string"));
280 VerifyRegexMatch(error, e);
281 }
282
283 } // namespace testing
284 } // namespace grpc_core
285
main(int argc,char ** argv)286 int main(int argc, char** argv) {
287 // Regexes don't work in old libstdc++ versions, so just skip testing in those
288 // cases
289 #if defined(__GLIBCXX__) && (__GLIBCXX__ <= 20150623)
290 gpr_log(GPR_ERROR,
291 "Skipping xds_bootstrap_test since std::regex is not supported on "
292 "this system.");
293 return 0;
294 #endif
295 ::testing::InitGoogleTest(&argc, argv);
296 grpc::testing::TestEnvironment env(argc, argv);
297 grpc_init();
298 int ret = RUN_ALL_TESTS();
299 grpc_shutdown();
300 return ret;
301 }
302