Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions source/server/admin/config_dump_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -271,17 +271,22 @@ absl::optional<std::pair<Http::Code, std::string>> ConfigDumpHandler::addAllConf
Protobuf::FieldMask field_mask;
ProtobufUtil::FieldMaskUtil::FromString(mask.value(), &field_mask);
// We don't use trimMessage() above here since masks don't support
// indexing through repeated fields.
// indexing through repeated fields. We don't return error on failure
// because different callback return types will have different valid
// field masks.
if (!checkFieldMaskAndTrimMessage(field_mask, *message)) {
return absl::optional<std::pair<Http::Code, std::string>>{std::make_pair(
Http::Code::BadRequest, absl::StrCat("FieldMask ", field_mask.DebugString(),
" could not be successfully used."))};
continue;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT about just including in the output text the error message that we had before. IIUC you were taking a case that had an instructive error message before about what was wrong, and then making it just return an empty string.

Maybe collect the names of the fields that had invalid masks and return them in one error message?

We might also return an http error when none of the clusters had a useful mask. WDYT?

Copy link
Copy Markdown
Contributor Author

@paul-r-gall paul-r-gall Jul 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm considering the case in the test, when someone just supplies mask=bootstrap. Intended behavior there is just that the bootstrap config is returned, with no error message. However, that mask is invalid for the majority of protos in the dump (which is why this was failing before).

I think that returning an HTTP error when no protos had a useful mask is a good idea.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sgtm.

}
}

auto* config = dump.add_configs();
config->PackFrom(*message);
}
if (dump.configs().empty() && mask.has_value()) {
return absl::optional<std::pair<Http::Code, std::string>>{std::make_pair(
Http::Code::BadRequest,
absl::StrCat("FieldMask ", *mask, " could not be successfully applied to any configs."))};
}
return absl::nullopt;
}

Expand Down
51 changes: 44 additions & 7 deletions test/server/admin/config_dump_handler_test.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "test/server/admin/admin_instance.h"

using testing::HasSubstr;
using testing::Return;
using testing::ReturnPointee;
using testing::ReturnRef;
Expand Down Expand Up @@ -670,7 +671,7 @@ TEST_P(AdminInstanceTest, ConfigDumpNonExistentMask) {
EXPECT_EQ(Http::Code::BadRequest,
getCallback("/config_dump?resource=static_clusters&mask=bad", header_map, response));
std::string output = response.toString();
EXPECT_THAT(output, testing::HasSubstr("could not be successfully used"));
EXPECT_THAT(output, HasSubstr("could not be successfully used"));
}

// Test that a 404 Not found is returned if a non-existent resource is passed in as the
Expand Down Expand Up @@ -741,13 +742,49 @@ TEST_P(AdminInstanceTest, InvalidFieldMaskWithoutResourceDoesNotCrash) {
// `extensions` is a repeated field, and cannot be indexed through in a FieldMask.
EXPECT_EQ(Http::Code::BadRequest,
getCallback("/config_dump?mask=bootstrap.node.extensions.name", header_map, response));
EXPECT_EQ("FieldMask paths: \"bootstrap.node.extensions.name\"\n could not be "
"successfully used.",
EXPECT_THAT(response.toString(), HasSubstr("could not be successfully applied to any configs"));
}

TEST_P(AdminInstanceTest, FieldMasksWorkWhenFetchingAllResources) {
Buffer::OwnedImpl response;
Http::TestResponseHeaderMapImpl header_map;
auto bootstrap = admin_.getConfigTracker().add("bootstrap", [](const Matchers::StringMatcher&) {
auto msg = std::make_unique<envoy::admin::v3::BootstrapConfigDump>();
auto* bootstrap = msg->mutable_bootstrap();
bootstrap->mutable_node()->add_extensions()->set_name("ext1");
bootstrap->mutable_node()->add_extensions()->set_name("ext2");
return msg;
});
auto clusters = admin_.getConfigTracker().add("clusters", [](const Matchers::StringMatcher&) {
auto msg = std::make_unique<envoy::admin::v3::ClustersConfigDump>();
auto* static_cluster = msg->add_static_clusters();
envoy::config::cluster::v3::Cluster inner_cluster;
inner_cluster.set_name("cluster1");
static_cluster->mutable_cluster()->PackFrom(inner_cluster);
return msg;
});
EXPECT_EQ(Http::Code::OK, getCallback("/config_dump?mask=bootstrap", header_map, response));
EXPECT_EQ(R"EOF({
"configs": [
{
"@type": "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump",
"bootstrap": {
"node": {
"extensions": [
{
"name": "ext1"
},
{
"name": "ext2"
}
]
}
}
}
]
}
)EOF",
response.toString());
EXPECT_EQ(header_map.ContentType()->value().getStringView(),
Http::Headers::get().ContentTypeValues.Text);
EXPECT_EQ(header_map.get(Http::Headers::get().XContentTypeOptions)[0]->value(),
Http::Headers::get().XContentTypeOptionValues.Nosniff);
}

} // namespace Server
Expand Down