From 29ea37b17049d9eeb57697eac9529942a1b7568b Mon Sep 17 00:00:00 2001 From: Matt Klein Date: Thu, 1 Sep 2016 12:26:56 -0700 Subject: [PATCH] alter authority when shadowing (#51) --- docs/configuration/http_conn_man/route_config/route.rst | 8 ++++++++ source/common/router/shadow_writer_impl.cc | 9 +++++++++ test/common/router/shadow_writer_impl_test.cc | 5 +++++ 3 files changed, 22 insertions(+) diff --git a/docs/configuration/http_conn_man/route_config/route.rst b/docs/configuration/http_conn_man/route_config/route.rst index c7e820328385..1f24a09fe7d0 100644 --- a/docs/configuration/http_conn_man/route_config/route.rst +++ b/docs/configuration/http_conn_man/route_config/route.rst @@ -161,6 +161,14 @@ global Shadow ------ +The router is capable of shadowing traffic from one cluster to another. The current implementation +is "fire and forget," meaning Envoy will not wait for the shadow cluster to respond before returning +the response from the primary cluster. All normal statistics are collected however for the shadow +cluster making thie feature useful for testing. + +During shadowing, the host/authority header is altered such that *-shadow* is appended. This is +useful for logging. For example, *cluster1* becomes *cluster1-shadow*. + .. code-block:: json { diff --git a/source/common/router/shadow_writer_impl.cc b/source/common/router/shadow_writer_impl.cc index 3bdf5148a164..07f405adc0e7 100644 --- a/source/common/router/shadow_writer_impl.cc +++ b/source/common/router/shadow_writer_impl.cc @@ -1,9 +1,18 @@ #include "shadow_writer_impl.h" +#include "common/common/assert.h" +#include "common/http/headers.h" + namespace Router { void ShadowWriterImpl::shadow(const std::string& cluster, Http::MessagePtr&& request, std::chrono::milliseconds timeout) { + // Switch authority to add a shadow postfix. This allows upstream logging to make a more sense. + std::string host = request->headers().get(Http::Headers::get().Host); + ASSERT(!host.empty()); + host += "-shadow"; + request->headers().replaceViaMoveValue(Http::Headers::get().Host, std::move(host)); + // Configuration should guarantee that cluster exists before calling here. This is basically // fire and forget. We don't handle cancelling. cm_.httpAsyncClientForCluster(cluster) diff --git a/test/common/router/shadow_writer_impl_test.cc b/test/common/router/shadow_writer_impl_test.cc index 5a6436654380..62f1116f46f7 100644 --- a/test/common/router/shadow_writer_impl_test.cc +++ b/test/common/router/shadow_writer_impl_test.cc @@ -1,3 +1,4 @@ +#include "common/http/headers.h" #include "common/router/shadow_writer_impl.h" #include "test/mocks/upstream/mocks.h" @@ -13,6 +14,7 @@ TEST(ShadowWriterImplTest, All) { // Success case Http::MessagePtr message(new Http::RequestMessageImpl()); + message->headers().addViaCopy(Http::Headers::get().Host, "cluster1"); EXPECT_CALL(cm, httpAsyncClientForCluster("foo")).WillOnce(ReturnRef(cm.async_client_)); Http::MockAsyncClientRequest request(&cm.async_client_); Http::AsyncClient::Callbacks* callback; @@ -22,6 +24,7 @@ TEST(ShadowWriterImplTest, All) { Invoke([&](Http::MessagePtr& inner_message, Http::AsyncClient::Callbacks& callbacks, const Optional&) -> Http::AsyncClient::Request* { EXPECT_EQ(message, inner_message); + EXPECT_EQ("cluster1-shadow", message->headers().get(Http::Headers::get().Host)); callback = &callbacks; return &request; })); @@ -32,6 +35,7 @@ TEST(ShadowWriterImplTest, All) { // Failure case message.reset(new Http::RequestMessageImpl()); + message->headers().addViaCopy(Http::Headers::get().Host, "cluster2"); EXPECT_CALL(cm, httpAsyncClientForCluster("bar")).WillOnce(ReturnRef(cm.async_client_)); EXPECT_CALL(cm.async_client_, send_(_, _, Optional(std::chrono::milliseconds(10)))) @@ -39,6 +43,7 @@ TEST(ShadowWriterImplTest, All) { Invoke([&](Http::MessagePtr& inner_message, Http::AsyncClient::Callbacks& callbacks, const Optional&) -> Http::AsyncClient::Request* { EXPECT_EQ(message, inner_message); + EXPECT_EQ("cluster2-shadow", message->headers().get(Http::Headers::get().Host)); callback = &callbacks; return &request; }));