blob: 2b69bff8b824c9db8fb2c2e435adba423cf3055f [file] [log] [blame]
//
// Copyright 2021 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <grpc/grpc.h>
#include "src/core/ext/service_config/service_config.h"
#include "src/core/lib/gpr/env.h"
#include "test/core/util/test_config.h"
// A regular expression to enter referenced or child errors.
#ifdef GRPC_ERROR_IS_ABSEIL_STATUS
#define CHILD_ERROR_TAG ".*children.*"
#else
#define CHILD_ERROR_TAG ".*referenced_errors.*"
#endif
namespace grpc_core {
namespace {
class RlsConfigParsingTest : public ::testing::Test {
public:
static void SetUpTestSuite() {
gpr_setenv("GRPC_EXPERIMENTAL_ENABLE_RLS_LB_POLICY", "true");
grpc_init();
}
static void TearDownTestSuite() {
grpc_shutdown_blocking();
gpr_unsetenv("GRPC_EXPERIMENTAL_ENABLE_RLS_LB_POLICY");
}
};
TEST_F(RlsConfigParsingTest, ValidConfig) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"rls\":{\n"
" \"routeLookupConfig\":{\n"
" \"lookupService\":\"rls.example.com:80\",\n"
" \"cacheSizeBytes\":1,\n"
" \"grpcKeybuilders\":[\n"
" {\n"
" \"names\":[\n"
" {\"service\":\"foo\"}\n"
" ]\n"
" }\n"
" ]\n"
" },\n"
" \"childPolicy\":[\n"
" {\"unknown\":{}},\n" // Okay, since the next one exists.
" {\"grpclb\":{}}\n"
" ],\n"
" \"childPolicyConfigTargetFieldName\":\"target\"\n"
" }\n"
" }]\n"
"}\n";
grpc_error_handle error = GRPC_ERROR_NONE;
auto service_config = ServiceConfig::Create(
/*args=*/nullptr, service_config_json, &error);
EXPECT_EQ(error, GRPC_ERROR_NONE) << grpc_error_std_string(error);
EXPECT_NE(service_config, nullptr);
}
//
// top-level fields
//
TEST_F(RlsConfigParsingTest, TopLevelRequiredFieldsMissing) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"rls\":{\n"
" }\n"
" }]\n"
"}\n";
grpc_error_handle error = GRPC_ERROR_NONE;
auto service_config = ServiceConfig::Create(
/*args=*/nullptr, service_config_json, &error);
EXPECT_THAT(
grpc_error_std_string(error),
::testing::ContainsRegex(
"errors parsing RLS LB policy config" CHILD_ERROR_TAG
"field:routeLookupConfig error:does not exist.*"
"field:childPolicyConfigTargetFieldName error:does not exist.*"
"field:childPolicy error:does not exist"));
GRPC_ERROR_UNREF(error);
}
TEST_F(RlsConfigParsingTest, TopLevelFieldsWrongTypes) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"rls\":{\n"
" \"routeLookupConfig\":1,\n"
" \"childPolicy\":1,\n"
" \"childPolicyConfigTargetFieldName\":1\n"
" }\n"
" }]\n"
"}\n";
grpc_error_handle error = GRPC_ERROR_NONE;
auto service_config = ServiceConfig::Create(
/*args=*/nullptr, service_config_json, &error);
EXPECT_THAT(
grpc_error_std_string(error),
::testing::ContainsRegex(
"errors parsing RLS LB policy config" CHILD_ERROR_TAG
"field:routeLookupConfig error:type should be OBJECT.*"
"field:childPolicyConfigTargetFieldName error:type should be STRING.*"
"field:childPolicy error:type should be ARRAY"));
GRPC_ERROR_UNREF(error);
}
TEST_F(RlsConfigParsingTest, TopLevelFieldsInvalidValues) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"rls\":{\n"
" \"childPolicy\":[\n"
" {\"unknown\":{}}\n"
" ],\n"
" \"childPolicyConfigTargetFieldName\":\"\"\n"
" }\n"
" }]\n"
"}\n";
grpc_error_handle error = GRPC_ERROR_NONE;
auto service_config = ServiceConfig::Create(
/*args=*/nullptr, service_config_json, &error);
EXPECT_THAT(
grpc_error_std_string(error),
::testing::ContainsRegex(
"errors parsing RLS LB policy config" CHILD_ERROR_TAG
"field:childPolicyConfigTargetFieldName error:must be non-empty.*"
"field:childPolicy" CHILD_ERROR_TAG
"No known policies in list: unknown"));
GRPC_ERROR_UNREF(error);
}
TEST_F(RlsConfigParsingTest, InvalidChildPolicyConfig) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"rls\":{\n"
" \"childPolicy\":[\n"
" {\"grpclb\":{\"childPolicy\":1}}\n"
" ],\n"
" \"childPolicyConfigTargetFieldName\":\"serviceName\"\n"
" }\n"
" }]\n"
"}\n";
grpc_error_handle error = GRPC_ERROR_NONE;
auto service_config = ServiceConfig::Create(
/*args=*/nullptr, service_config_json, &error);
EXPECT_THAT(
grpc_error_std_string(error),
::testing::ContainsRegex(
"errors parsing RLS LB policy config" CHILD_ERROR_TAG
"field:childPolicy" CHILD_ERROR_TAG "GrpcLb Parser" CHILD_ERROR_TAG
"field:childPolicy" CHILD_ERROR_TAG "type should be array"));
GRPC_ERROR_UNREF(error);
}
//
// routeLookupConfig fields
//
TEST_F(RlsConfigParsingTest, RouteLookupConfigRequiredFieldsMissing) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"rls\":{\n"
" \"routeLookupConfig\":{\n"
" }\n"
" }\n"
" }]\n"
"}\n";
grpc_error_handle error = GRPC_ERROR_NONE;
auto service_config = ServiceConfig::Create(
/*args=*/nullptr, service_config_json, &error);
EXPECT_THAT(grpc_error_std_string(error),
::testing::ContainsRegex(
"errors parsing RLS LB policy config" CHILD_ERROR_TAG
"field:routeLookupConfig" CHILD_ERROR_TAG
"field:grpcKeybuilders error:does not exist.*"
"field:lookupService error:does not exist"));
GRPC_ERROR_UNREF(error);
}
TEST_F(RlsConfigParsingTest, RouteLookupConfigFieldsWrongTypes) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"rls\":{\n"
" \"routeLookupConfig\":{\n"
" \"grpcKeybuilders\":1,\n"
" \"name\":1,\n"
" \"lookupService\":1,\n"
" \"lookupServiceTimeout\":{},\n"
" \"maxAge\":{},\n"
" \"staleAge\":{},\n"
" \"cacheSizeBytes\":\"xxx\",\n"
" \"defaultTarget\":1\n"
" }\n"
" }\n"
" }]\n"
"}\n";
grpc_error_handle error = GRPC_ERROR_NONE;
auto service_config = ServiceConfig::Create(
/*args=*/nullptr, service_config_json, &error);
EXPECT_THAT(grpc_error_std_string(error),
::testing::ContainsRegex(
"errors parsing RLS LB policy config" CHILD_ERROR_TAG
"field:routeLookupConfig" CHILD_ERROR_TAG
"field:grpcKeybuilders error:type should be ARRAY.*"
"field:lookupService error:type should be STRING.*"
"field:maxAge error:type should be STRING.*"
"field:staleAge error:type should be STRING.*"
"field:cacheSizeBytes error:type should be NUMBER.*"
"field:defaultTarget error:type should be STRING"));
GRPC_ERROR_UNREF(error);
}
TEST_F(RlsConfigParsingTest, RouteLookupConfigFieldsInvalidValues) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"rls\":{\n"
" \"routeLookupConfig\":{\n"
" \"lookupService\":\"\",\n"
" \"cacheSizeBytes\":0\n"
" }\n"
" }\n"
" }]\n"
"}\n";
grpc_error_handle error = GRPC_ERROR_NONE;
auto service_config = ServiceConfig::Create(
/*args=*/nullptr, service_config_json, &error);
EXPECT_THAT(grpc_error_std_string(error),
::testing::ContainsRegex(
"errors parsing RLS LB policy config" CHILD_ERROR_TAG
"field:routeLookupConfig" CHILD_ERROR_TAG
"field:lookupService error:must be valid gRPC target URI.*"
"field:cacheSizeBytes error:must be greater than 0"));
GRPC_ERROR_UNREF(error);
}
//
// grpcKeybuilder fields
//
TEST_F(RlsConfigParsingTest, GrpcKeybuilderRequiredFieldsMissing) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"rls\":{\n"
" \"routeLookupConfig\":{\n"
" \"grpcKeybuilders\":[\n"
" {\n"
" }\n"
" ]\n"
" }\n"
" }\n"
" }]\n"
"}\n";
grpc_error_handle error = GRPC_ERROR_NONE;
auto service_config = ServiceConfig::Create(
/*args=*/nullptr, service_config_json, &error);
EXPECT_THAT(
grpc_error_std_string(error),
::testing::ContainsRegex(
"errors parsing RLS LB policy config" CHILD_ERROR_TAG
"field:routeLookupConfig" CHILD_ERROR_TAG
"field:grpcKeybuilders" CHILD_ERROR_TAG "index:0" CHILD_ERROR_TAG
"field:names error:does not exist"));
GRPC_ERROR_UNREF(error);
}
TEST_F(RlsConfigParsingTest, GrpcKeybuilderWrongFieldTypes) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"rls\":{\n"
" \"routeLookupConfig\":{\n"
" \"grpcKeybuilders\":[\n"
" {\n"
" \"names\":1,\n"
" \"headers\":1,\n"
" \"extraKeys\":1,\n"
" \"constantKeys\":1\n"
" }\n"
" ]\n"
" }\n"
" }\n"
" }]\n"
"}\n";
grpc_error_handle error = GRPC_ERROR_NONE;
auto service_config = ServiceConfig::Create(
/*args=*/nullptr, service_config_json, &error);
EXPECT_THAT(
grpc_error_std_string(error),
::testing::ContainsRegex(
"errors parsing RLS LB policy config" CHILD_ERROR_TAG
"field:routeLookupConfig" CHILD_ERROR_TAG
"field:grpcKeybuilders" CHILD_ERROR_TAG "index:0" CHILD_ERROR_TAG
"field:names error:type should be ARRAY.*"
"field:headers error:type should be ARRAY.*"
"field:extraKeys error:type should be OBJECT.*"
"field:constantKeys error:type should be OBJECT"));
GRPC_ERROR_UNREF(error);
}
TEST_F(RlsConfigParsingTest, GrpcKeybuilderInvalidValues) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"rls\":{\n"
" \"routeLookupConfig\":{\n"
" \"grpcKeybuilders\":[\n"
" {\n"
" \"names\":[],\n"
" \"extraKeys\":{\n"
" \"host\":1,\n"
" \"service\":1,\n"
" \"method\":1\n"
" },\n"
" \"constantKeys\":{\n"
" \"key\":1\n"
" }\n"
" }\n"
" ]\n"
" }\n"
" }\n"
" }]\n"
"}\n";
grpc_error_handle error = GRPC_ERROR_NONE;
auto service_config = ServiceConfig::Create(
/*args=*/nullptr, service_config_json, &error);
EXPECT_THAT(grpc_error_std_string(error),
::testing::ContainsRegex(
"errors parsing RLS LB policy config" CHILD_ERROR_TAG
"field:routeLookupConfig" CHILD_ERROR_TAG
"field:grpcKeybuilders" CHILD_ERROR_TAG
"index:0" CHILD_ERROR_TAG "field:names error:list is empty.*"
"field:extraKeys" CHILD_ERROR_TAG
"field:host error:type should be STRING.*"
"field:service error:type should be STRING.*"
"field:method error:type should be STRING.*"
"field:constantKeys" CHILD_ERROR_TAG
"field:key error:type should be STRING"));
GRPC_ERROR_UNREF(error);
}
TEST_F(RlsConfigParsingTest, GrpcKeybuilderInvalidHeaders) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"rls\":{\n"
" \"routeLookupConfig\":{\n"
" \"grpcKeybuilders\":[\n"
" {\n"
" \"headers\":[\n"
" 1,\n"
" {\n"
" \"key\":1,\n"
" \"names\":1\n"
" },\n"
" {\n"
" \"names\":[]\n"
" },\n"
" {\n"
" \"key\":\"\",\n"
" \"names\":[1, \"\"]\n"
" }\n"
" ],\n"
" \"extraKeys\":{\n"
" \"host\": \"\"\n"
" },\n"
" \"constantKeys\":{\n"
" \"\":\"foo\"\n"
" }\n"
" }\n"
" ]\n"
" }\n"
" }\n"
" }]\n"
"}\n";
grpc_error_handle error = GRPC_ERROR_NONE;
auto service_config = ServiceConfig::Create(
/*args=*/nullptr, service_config_json, &error);
EXPECT_THAT(
grpc_error_std_string(error),
::testing::ContainsRegex(
"errors parsing RLS LB policy config" CHILD_ERROR_TAG
"field:routeLookupConfig" CHILD_ERROR_TAG
"field:grpcKeybuilders" CHILD_ERROR_TAG "index:0" CHILD_ERROR_TAG
"field:headers index:0 error:type should be OBJECT.*"
"field:headers index:1" CHILD_ERROR_TAG
"field:key error:type should be STRING.*"
"field:names error:type should be ARRAY.*"
"field:headers index:2" CHILD_ERROR_TAG
"field:key error:does not exist.*"
"field:names error:list is empty.*"
"field:headers index:3" CHILD_ERROR_TAG
"field:key error:must be non-empty.*"
"field:names index:0 error:type should be STRING.*"
"field:names index:1 error:header name must be non-empty.*"
"field:extraKeys" CHILD_ERROR_TAG
"field:host error:must be non-empty.*"
"field:constantKeys" CHILD_ERROR_TAG "error:keys must be non-empty"));
GRPC_ERROR_UNREF(error);
}
TEST_F(RlsConfigParsingTest, GrpcKeybuilderNameWrongFieldTypes) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"rls\":{\n"
" \"routeLookupConfig\":{\n"
" \"grpcKeybuilders\":[\n"
" {\n"
" \"names\":[\n"
" 1,\n"
" {\n"
" \"service\":1,\n"
" \"method\":1\n"
" }\n"
" ]\n"
" }\n"
" ]\n"
" }\n"
" }\n"
" }]\n"
"}\n";
grpc_error_handle error = GRPC_ERROR_NONE;
auto service_config = ServiceConfig::Create(
/*args=*/nullptr, service_config_json, &error);
EXPECT_THAT(
grpc_error_std_string(error),
::testing::ContainsRegex(
"errors parsing RLS LB policy config" CHILD_ERROR_TAG
"field:routeLookupConfig" CHILD_ERROR_TAG
"field:grpcKeybuilders" CHILD_ERROR_TAG "index:0" CHILD_ERROR_TAG
"field:names index:0 error:type should be OBJECT.*"
"field:names index:1" CHILD_ERROR_TAG
"field:service error:type should be STRING.*"
"field:method error:type should be STRING"));
GRPC_ERROR_UNREF(error);
}
TEST_F(RlsConfigParsingTest, DuplicateMethodNamesInSameKeyBuilder) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"rls\":{\n"
" \"routeLookupConfig\":{\n"
" \"grpcKeybuilders\":[\n"
" {\n"
" \"names\":[\n"
" {\n"
" \"service\":\"foo\",\n"
" \"method\":\"bar\"\n"
" },\n"
" {\n"
" \"service\":\"foo\",\n"
" \"method\":\"bar\"\n"
" }\n"
" ]\n"
" }\n"
" ]\n"
" }\n"
" }\n"
" }]\n"
"}\n";
grpc_error_handle error = GRPC_ERROR_NONE;
auto service_config = ServiceConfig::Create(
/*args=*/nullptr, service_config_json, &error);
EXPECT_THAT(
grpc_error_std_string(error),
::testing::ContainsRegex(
"errors parsing RLS LB policy config" CHILD_ERROR_TAG
"field:routeLookupConfig" CHILD_ERROR_TAG
"field:grpcKeybuilders" CHILD_ERROR_TAG "index:0" CHILD_ERROR_TAG
"field:names error:duplicate entry for /foo/bar"));
GRPC_ERROR_UNREF(error);
}
TEST_F(RlsConfigParsingTest, DuplicateMethodNamesInDifferentKeyBuilders) {
const char* service_config_json =
"{\n"
" \"loadBalancingConfig\":[{\n"
" \"rls\":{\n"
" \"routeLookupConfig\":{\n"
" \"grpcKeybuilders\":[\n"
" {\n"
" \"names\":[\n"
" {\n"
" \"service\":\"foo\",\n"
" \"method\":\"bar\"\n"
" }\n"
" ]\n"
" },\n"
" {\n"
" \"names\":[\n"
" {\n"
" \"service\":\"foo\",\n"
" \"method\":\"bar\"\n"
" }\n"
" ]\n"
" }\n"
" ]\n"
" }\n"
" }\n"
" }]\n"
"}\n";
grpc_error_handle error = GRPC_ERROR_NONE;
auto service_config = ServiceConfig::Create(
/*args=*/nullptr, service_config_json, &error);
EXPECT_THAT(
grpc_error_std_string(error),
::testing::ContainsRegex(
"errors parsing RLS LB policy config" CHILD_ERROR_TAG
"field:routeLookupConfig" CHILD_ERROR_TAG
"field:grpcKeybuilders" CHILD_ERROR_TAG "index:1" CHILD_ERROR_TAG
"field:names error:duplicate entry for /foo/bar"));
GRPC_ERROR_UNREF(error);
}
} // namespace
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
grpc::testing::TestEnvironment env(argc, argv);
return RUN_ALL_TESTS();
}