Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move EDS Clusters initialization after ADS #2760

Merged
merged 12 commits into from
Mar 11, 2018
32 changes: 16 additions & 16 deletions source/common/upstream/cluster_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,21 @@ ClusterManagerImpl::ClusterManagerImpl(const envoy::config::bootstrap::v2::Boots
}
}

// Now setup ADS if needed, this might rely on a primary cluster.
if (bootstrap.dynamic_resources().has_ads_config()) {
ads_mux_.reset(new Config::GrpcMuxImpl(
bootstrap.node(),
Config::Utility::factoryForApiConfigSource(
*async_client_manager_, bootstrap.dynamic_resources().ads_config(), stats)
->create(),
primary_dispatcher,
*Protobuf::DescriptorPool::generated_pool()->FindMethodByName(
"envoy.service.discovery.v2.AggregatedDiscoveryService.StreamAggregatedResources")));
} else {
ads_mux_.reset(new Config::NullGrpcMuxImpl());
}

// After ADS is initialized, load EDS static clusters as EDS config may potentially need ADS.
for (const auto& cluster : bootstrap.static_resources().clusters()) {
// Now load all the secondary clusters.
if (cluster.type() == envoy::api::v2::Cluster::EDS) {
Expand Down Expand Up @@ -255,21 +270,6 @@ ClusterManagerImpl::ClusterManagerImpl(const envoy::config::bootstrap::v2::Boots
return std::make_shared<ThreadLocalClusterManagerImpl>(*this, dispatcher, local_cluster_name);
});

// Now setup ADS if needed, this might rely on a primary cluster and the
// thread local cluster manager.
Copy link
Member

@htuch htuch Mar 8, 2018

Choose a reason for hiding this comment

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

This comment was there to point out that ADS init must happen after the above tls_->set. We need TLS to be able to initialize gRPC async client manager bits. I'm surprised the tests pass without this (specifically ads_integration_test with Envoy gRPC).

Copy link
Member

Choose a reason for hiding this comment

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

Actually, I think the need for this went away when I fixed the stats scope to be created on the main thread. Feel free to ignore this comment, if an issue arises I can go and fix it, since this is Grpc::AsyncClient implementation stuff.

if (bootstrap.dynamic_resources().has_ads_config()) {
ads_mux_.reset(new Config::GrpcMuxImpl(
bootstrap.node(),
Config::Utility::factoryForApiConfigSource(
*async_client_manager_, bootstrap.dynamic_resources().ads_config(), stats)
->create(),
primary_dispatcher,
*Protobuf::DescriptorPool::generated_pool()->FindMethodByName(
"envoy.service.discovery.v2.AggregatedDiscoveryService.StreamAggregatedResources")));
} else {
ads_mux_.reset(new Config::NullGrpcMuxImpl());
}

// We can now potentially create the CDS API once the backing cluster exists.
if (bootstrap.dynamic_resources().has_cds_config()) {
cds_api_ = factory_.createCds(bootstrap.dynamic_resources().cds_config(), eds_config_, *this);
Expand Down Expand Up @@ -820,4 +820,4 @@ ProdClusterManagerFactory::createCds(const envoy::api::v2::core::ConfigSource& c
}

} // namespace Upstream
} // namespace Envoy
} // namespace Envoy
54 changes: 54 additions & 0 deletions test/integration/ads_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ class AdsIntegrationTest : public HttpIntegrationTest, public Grpc::GrpcClientIn
TestEnvironment::runfilesPath("test/config/integration/certs/upstreamcacert.pem"));
}
});

Copy link
Member

Choose a reason for hiding this comment

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

Nit: superfluous whitespace change.

setUpstreamProtocol(FakeHttpConnection::Type::HTTP2);
HttpIntegrationTest::initialize();
ads_connection_ = fake_upstreams_[1]->waitForHttpConnection(*dispatcher_);
Expand Down Expand Up @@ -434,5 +435,58 @@ TEST_P(AdsFailIntegrationTest, ConnectDisconnect) {
ads_stream_->finishGrpcStream(Grpc::Status::Internal);
}

class AdsConfigIntegrationTest : public HttpIntegrationTest,
Copy link
Member

Choose a reason for hiding this comment

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

I'm wondering if this could have been also done as a server unit test, i.e. just verifying the config loads. I think it's fine to do this here, but I'd also try and minify the amount of stuff we have; all we want to do is verify initialization with an ADS configuration. We don't care about setting up an ADS server or actually serving up ADS.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i agree. I thought about it being a unit test. I added it here so that we could add ADS related config tests that ensure ADS connection works with config changes in future if needed.

public Grpc::GrpcClientIntegrationParamTest {
public:
AdsConfigIntegrationTest()
: HttpIntegrationTest(Http::CodecClient::Type::HTTP2, ipVersion(), config) {}

void TearDown() override {
test_server_.reset();
fake_upstreams_.clear();
}

void createUpstreams() override {
HttpIntegrationTest::createUpstreams();
fake_upstreams_.emplace_back(new FakeUpstream(0, FakeHttpConnection::Type::HTTP2, version_));
}

void initialize() override {
config_helper_.addConfigModifier([this](envoy::config::bootstrap::v2::Bootstrap& bootstrap) {
auto* grpc_service =
bootstrap.mutable_dynamic_resources()->mutable_ads_config()->add_grpc_services();
setGrpcService(*grpc_service, "ads_cluster", fake_upstreams_.back()->localAddress());
auto* ads_cluster = bootstrap.mutable_static_resources()->add_clusters();
ads_cluster->MergeFrom(bootstrap.static_resources().clusters()[0]);
ads_cluster->set_name("ads_cluster");

// Add EDS Cluster that uses ADS as config Source.
Copy link
Member

Choose a reason for hiding this comment

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

Nit: s/Add EDS/Add EDS static/

auto* ads_eds_cluster = bootstrap.mutable_static_resources()->add_clusters();
ads_eds_cluster->set_name("ads_eds_cluster");
ads_eds_cluster->set_type(envoy::api::v2::Cluster::EDS);
auto* eds_cluster_config = ads_eds_cluster->mutable_eds_cluster_config();
auto* eds_config = eds_cluster_config->mutable_eds_config();
eds_config->mutable_ads()->InitAsDefaultInstance();
Copy link
Member

Choose a reason for hiding this comment

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

Nit: you can just write eds_config->mutable_ads();

});
setUpstreamProtocol(FakeHttpConnection::Type::HTTP2);
HttpIntegrationTest::initialize();
}

FakeHttpConnectionPtr ads_connection_;
FakeStreamPtr ads_stream_;
};

INSTANTIATE_TEST_CASE_P(IpVersionsClientType, AdsConfigIntegrationTest,
GRPC_CLIENT_INTEGRATION_PARAMS);

// Validate that we don't crash on EDS Cluster that uses ADS as ConfigSource.
Copy link
Member

Choose a reason for hiding this comment

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

More nits: don't crash on EDS static cluster...

This is a regression test for .

TEST_P(AdsConfigIntegrationTest, EdsClusterWithAdsConfigSource) {
initialize();
ads_connection_ = fake_upstreams_[1]->waitForHttpConnection(*dispatcher_);
ads_stream_ = ads_connection_->waitForNewStream(*dispatcher_);
ads_stream_->startGrpcStream();
ads_stream_->finishGrpcStream(Grpc::Status::Ok);
Copy link
Member

Choose a reason for hiding this comment

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

Do you need all of this just to verify initialization?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

with out this, some ipv6 tests are failing.

Copy link
Member

Choose a reason for hiding this comment

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

This seems surprising. If you reduce it to initialize() only, it fails on IPv6 abut not IPv4?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried reducing it to waitForHttpConnection then ipv6 test failed. If I reduce to initialise the test is passes with old code also. That’s why added these additional steps

}

} // namespace
} // namespace Envoy