Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 2 additions & 0 deletions RAW_RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,5 @@ final version.
namespace can be used by prepending '@' to a socket path.
* Added `GEORADIUS_RO` and `GEORADIUSBYMEMBER_RO` to the Redis command splitter whitelist.
* Added support for trusting additional hops in the X-Forwarded-For request header.
* Added setting host header value for http health check request.

3 changes: 3 additions & 0 deletions source/common/config/cds_json.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ void CdsJson::translateHealthCheck(const Json::Object& json_health_check,
const std::string hc_type = json_health_check.getString("type");
if (hc_type == "http") {
health_check.mutable_http_health_check()->set_path(json_health_check.getString("path"));
if (json_health_check.hasObject("host")) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we need v1 support? If so we need to add v1 docs. Otherwise I would just drop it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I have removed the v1 support in the following commit 5937131

health_check.mutable_http_health_check()->set_host(json_health_check.getString("host"));
}
if (json_health_check.hasObject("service_name")) {
health_check.mutable_http_health_check()->set_service_name(
json_health_check.getString("service_name"));
Expand Down
1 change: 1 addition & 0 deletions source/common/json/config_schemas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1415,6 +1415,7 @@ const std::string Json::Schema::CLUSTER_HEALTH_CHECK_SCHEMA(R"EOF(
"exclusiveMinimum" : true
},
"path" : {"type" : "string"},
"host" : {"type" : "string"},
"send" : {
"type" : "array",
"items" : {"$ref" : "#/definitions/health_check_bytes"}
Expand Down
5 changes: 3 additions & 2 deletions source/common/upstream/health_checker_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ HttpHealthCheckerImpl::HttpHealthCheckerImpl(const Cluster& cluster,
Runtime::Loader& runtime,
Runtime::RandomGenerator& random)
: HealthCheckerImplBase(cluster, config, dispatcher, runtime, random),
path_(config.http_health_check().path()) {
path_(config.http_health_check().path()), host_value_(config.http_health_check().host()) {
if (!config.http_health_check().service_name().empty()) {
service_name_.value(config.http_health_check().service_name());
}
Expand Down Expand Up @@ -332,7 +332,8 @@ void HttpHealthCheckerImpl::HttpActiveHealthCheckSession::onInterval() {

Http::HeaderMapImpl request_headers{
{Http::Headers::get().Method, "GET"},
{Http::Headers::get().Host, parent_.cluster_.info()->name()},
{Http::Headers::get().Host,
parent_.host_value_.empty() ? parent_.cluster_.info()->name() : parent_.host_value_},
{Http::Headers::get().Path, parent_.path_},
{Http::Headers::get().UserAgent, Http::Headers::get().UserAgentValues.EnvoyHealthChecker}};

Expand Down
1 change: 1 addition & 0 deletions source/common/upstream/health_checker_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ class HttpHealthCheckerImpl : public HealthCheckerImplBase {
}

const std::string path_;
const std::string host_value_;
Optional<std::string> service_name_;
};

Expand Down
67 changes: 67 additions & 0 deletions test/common/upstream/health_checker_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,29 @@ class HttpHealthCheckerImplTest : public testing::Test {
});
}

void setupServiceValidationWithCustomHostValueHC(const std::string& host) {
std::string json = fmt::format(R"EOF(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we drop v1 support, let's make this a v2 YAML config for testing?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

OK, I have it in v2 YAML on b3f2c8f. Thanks for reviewing @mattklein123!

{{
"type": "http",
"timeout_ms": 1000,
"interval_ms": 1000,
"service_name": "locations",
"interval_jitter_ms": 1000,
"unhealthy_threshold": 2,
"healthy_threshold": 2,
"path": "/healthcheck",
"host": "{0}"
}}
)EOF",
host);

health_checker_.reset(new TestHttpHealthCheckerImpl(*cluster_, parseHealthCheckFromJson(json),
dispatcher_, runtime_, random_));
health_checker_->addHostCheckCompleteCb([this](HostSharedPtr host, bool changed_state) -> void {
onHostStatus(host, changed_state);
});
}

void expectSessionCreate() {
// Expectations are in LIFO order.
TestSessionPtr new_test_session(new TestSession());
Expand Down Expand Up @@ -426,6 +449,50 @@ TEST_F(HttpHealthCheckerImplTest, SuccessServiceCheck) {
expectSessionCreate();
expectStreamCreate(0);
EXPECT_CALL(*test_sessions_[0]->timeout_timer_, enableTimer(_));
EXPECT_CALL(test_sessions_[0]->request_encoder_, encodeHeaders(_, true))
.WillOnce(Invoke([&](const Http::HeaderMap& headers, bool) {
EXPECT_TRUE(headers.Host());
EXPECT_TRUE(headers.Path());
EXPECT_NE(nullptr, headers.Host());
EXPECT_NE(nullptr, headers.Path());
EXPECT_EQ(headers.Host()->value().c_str(), std::string("fake_cluster"));
EXPECT_EQ(headers.Path()->value().c_str(), std::string("/healthcheck"));
}));
health_checker_->start();

EXPECT_CALL(runtime_.snapshot_, getInteger("health_check.max_interval", _));
EXPECT_CALL(runtime_.snapshot_, getInteger("health_check.min_interval", _))
.WillOnce(Return(45000));
EXPECT_CALL(*test_sessions_[0]->interval_timer_, enableTimer(std::chrono::milliseconds(45000)));
EXPECT_CALL(*test_sessions_[0]->timeout_timer_, disableTimer());
Optional<std::string> health_checked_cluster("locations-production-iad");
respond(0, "200", false, true, false, health_checked_cluster);
EXPECT_TRUE(cluster_->prioritySet().getMockHostSet(0)->hosts_[0]->healthy());
}

TEST_F(HttpHealthCheckerImplTest, SuccessServiceCheckWithCustomHostValue) {
std::string host = "www.envoyproxy.io";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: const

setupServiceValidationWithCustomHostValueHC(host);
EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100))
.WillOnce(Return(true));

EXPECT_CALL(*this, onHostStatus(_, false)).Times(1);

cluster_->prioritySet().getMockHostSet(0)->hosts_ = {
makeTestHost(cluster_->info_, "tcp://127.0.0.1:80")};
cluster_->info_->stats().upstream_cx_total_.inc();
expectSessionCreate();
expectStreamCreate(0);
EXPECT_CALL(*test_sessions_[0]->timeout_timer_, enableTimer(_));
EXPECT_CALL(test_sessions_[0]->request_encoder_, encodeHeaders(_, true))
.WillOnce(Invoke([&](const Http::HeaderMap& headers, bool) {
EXPECT_TRUE(headers.Host());
EXPECT_TRUE(headers.Path());
EXPECT_NE(nullptr, headers.Host());
EXPECT_NE(nullptr, headers.Path());
EXPECT_EQ(headers.Host()->value().c_str(), std::string(host));
EXPECT_EQ(headers.Path()->value().c_str(), std::string("/healthcheck"));
}));
health_checker_->start();

EXPECT_CALL(runtime_.snapshot_, getInteger("health_check.max_interval", _));
Expand Down