This is an automated email from the ASF dual-hosted git repository. awong pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/kudu.git
commit aa3d7134eb5cce0556ce427d9398f1257bb51984 Author: Alexey Serbin <[email protected]> AuthorDate: Sun Sep 22 12:42:24 2019 -0700 [clock] utility to reserve port and start chronyd Introduced a utility to reserve a port and start chronyd serving NTP requests at the reserved port using MiniCluster's reservation technique (bind a socket with SO_REUSEPORT option). The newly introduced functionality is now used in mini_chronyd tests. Change-Id: I3f8165bbe3b1378dff97115fbb59a0effcae1450 Reviewed-on: http://gerrit.cloudera.org:8080/14279 Tested-by: Kudu Jenkins Reviewed-by: Adar Dembo <[email protected]> --- src/kudu/clock/CMakeLists.txt | 12 ++++- src/kudu/clock/test/mini_chronyd-test.cc | 60 +++++++++------------- src/kudu/clock/test/mini_chronyd_test_util.cc | 74 +++++++++++++++++++++++++++ src/kudu/clock/test/mini_chronyd_test_util.h | 42 +++++++++++++++ 4 files changed, 150 insertions(+), 38 deletions(-) diff --git a/src/kudu/clock/CMakeLists.txt b/src/kudu/clock/CMakeLists.txt index af56567..652cd08 100644 --- a/src/kudu/clock/CMakeLists.txt +++ b/src/kudu/clock/CMakeLists.txt @@ -52,7 +52,17 @@ target_link_libraries(mini_chronyd kudu_test_util kudu_util) -SET_KUDU_TEST_LINK_LIBS(clock mini_chronyd) +set(MINI_CHRONYD_TEST_UTIL_SRCS test/mini_chronyd_test_util.cc) +add_library(mini_chronyd_test_util ${MINI_CHRONYD_TEST_UTIL_SRCS}) +target_link_libraries(mini_chronyd_test_util + gutil + kudu_test_util + kudu_util + mini_cluster) + +SET_KUDU_TEST_LINK_LIBS(clock) ADD_KUDU_TEST(hybrid_clock-test PROCESSORS 3) ADD_KUDU_TEST(logical_clock-test) + +SET_KUDU_TEST_LINK_LIBS(clock mini_chronyd mini_chronyd_test_util) ADD_KUDU_TEST(test/mini_chronyd-test) diff --git a/src/kudu/clock/test/mini_chronyd-test.cc b/src/kudu/clock/test/mini_chronyd-test.cc index 8f6accf..feba53f 100644 --- a/src/kudu/clock/test/mini_chronyd-test.cc +++ b/src/kudu/clock/test/mini_chronyd-test.cc @@ -17,9 +17,6 @@ #include "kudu/clock/test/mini_chronyd.h" -#include <unistd.h> - -#include <cstdint> #include <ctime> #include <memory> #include <string> @@ -28,6 +25,7 @@ #include <gtest/gtest.h> +#include "kudu/clock/test/mini_chronyd_test_util.h" #include "kudu/util/net/net_util.h" #include "kudu/util/status.h" #include "kudu/util/test_macros.h" @@ -47,22 +45,22 @@ class MiniChronydTest: public KuduTest { // reference NTP server present. Such server cannot be a good NTP source // because its clock is unsynchronized. TEST_F(MiniChronydTest, UnsynchronizedServer) { - MiniChronydOptions options; - options.bindaddress = GetBindIpForDaemon(1, kDefaultBindMode); - options.port = 10123 + getpid() % 1000; - options.local = false; - MiniChronyd chrony(options); - ASSERT_OK(chrony.Start()); + unique_ptr<MiniChronyd> chrony; + { + MiniChronydOptions options; + options.local = false; + ASSERT_OK(StartChronydAtAutoReservedPort(&chrony, &options)); + } // No client has talked to the NTP server yet. { MiniChronyd::ServerStats stats; - ASSERT_OK(chrony.GetServerStats(&stats)); + ASSERT_OK(chrony->GetServerStats(&stats)); ASSERT_EQ(0, stats.ntp_packets_received); } auto s = MiniChronyd::CheckNtpSource( - { HostPort(chrony.options().bindaddress, chrony.options().port) }); + { HostPort(chrony->options().bindaddress, chrony->options().port) }); ASSERT_TRUE(s.IsRuntimeError()) << s.ToString(); ASSERT_STR_CONTAINS(s.ToString(), "failed measure clock offset from reference NTP servers"); @@ -70,7 +68,7 @@ TEST_F(MiniChronydTest, UnsynchronizedServer) { // Make sure the client has communicated with the server. { MiniChronyd::ServerStats stats; - ASSERT_OK(chrony.GetServerStats(&stats)); + ASSERT_OK(chrony->GetServerStats(&stats)); ASSERT_LT(0, stats.ntp_packets_received); } } @@ -79,16 +77,13 @@ TEST_F(MiniChronydTest, UnsynchronizedServer) { // start, stop, manually setting the reference time, etc. TEST_F(MiniChronydTest, BasicSingleServerInstance) { // Start chronyd at the specified port, making sure it's serving requests. - MiniChronydOptions options; - options.bindaddress = GetBindIpForDaemon(1, kDefaultBindMode); - options.port = 10123 + getpid() % 1000; - MiniChronyd chrony(options); - ASSERT_OK(chrony.Start()); + unique_ptr<MiniChronyd> chrony; + ASSERT_OK(StartChronydAtAutoReservedPort(&chrony)); // A chronyd that uses the system clock as a reference lock should present // itself as reliable NTP server. - const HostPort ntp_endpoint(chrony.options().bindaddress, - chrony.options().port); + const HostPort ntp_endpoint(chrony->options().bindaddress, + chrony->options().port); { // Make sure the server opens ports to listen and serve requests // from NTP clients. @@ -98,12 +93,12 @@ TEST_F(MiniChronydTest, BasicSingleServerInstance) { // Set time manually using chronyc and verify that chronyd tracks the time as // expected. - ASSERT_OK(chrony.SetTime(time(nullptr) - 60)); + ASSERT_OK(chrony->SetTime(time(nullptr) - 60)); // Sanity check: make sure chronyd receives NTP packets which were sent // by chronyc and the chronyd running in client-only mode. MiniChronyd::ServerStats stats; - ASSERT_OK(chrony.GetServerStats(&stats)); + ASSERT_OK(chrony->GetServerStats(&stats)); ASSERT_LT(0, stats.ntp_packets_received); ASSERT_LT(0, stats.cmd_packets_received); const auto ntp_packets_received = stats.ntp_packets_received; @@ -118,7 +113,7 @@ TEST_F(MiniChronydTest, BasicSingleServerInstance) { // information on the system clock synchronisation status should generate // additional NTP packets which should have been received by the NTP server. MiniChronyd::ServerStats stats; - ASSERT_OK(chrony.GetServerStats(&stats)); + ASSERT_OK(chrony->GetServerStats(&stats)); ASSERT_GT(stats.ntp_packets_received, ntp_packets_received); } } @@ -129,14 +124,11 @@ TEST_F(MiniChronydTest, BasicSingleServerInstance) { TEST_F(MiniChronydTest, BasicMultipleServerInstances) { vector<unique_ptr<MiniChronyd>> servers; vector<HostPort> ntp_endpoints; - const uint16_t base_port = 10123 + getpid() % 1000; for (int idx = 0; idx < 5; ++idx) { MiniChronydOptions options; options.index = idx; - options.bindaddress = GetBindIpForDaemon(idx + 1, kDefaultBindMode); - options.port = base_port + idx * 10; - unique_ptr<MiniChronyd> chrony(new MiniChronyd(options)); - ASSERT_OK(chrony->Start()); + unique_ptr<MiniChronyd> chrony; + ASSERT_OK(StartChronydAtAutoReservedPort(&chrony, &options)); ntp_endpoints.emplace_back(chrony->options().bindaddress, chrony->options().port); servers.emplace_back(std::move(chrony)); @@ -194,17 +186,13 @@ TEST_F(MiniChronydTest, BasicMultipleServerInstances) { // as the source for synchronisation. Both set of servers should be a good clock // source to serve NTP clients. TEST_F(MiniChronydTest, MultiTierBasic) { - const uint16_t base_port = 10123 + getpid() % 1000; - vector<unique_ptr<MiniChronyd>> servers_0; vector<HostPort> ntp_endpoints_0; for (auto idx = 0; idx < 3; ++idx) { MiniChronydOptions options; options.index = idx; - options.bindaddress = GetBindIpForDaemon(idx + 1, kDefaultBindMode); - options.port = base_port + idx * 10; - unique_ptr<MiniChronyd> chrony(new MiniChronyd(options)); - ASSERT_OK(chrony->Start()); + unique_ptr<MiniChronyd> chrony; + ASSERT_OK(StartChronydAtAutoReservedPort(&chrony, &options)); ntp_endpoints_0.emplace_back(chrony->options().bindaddress, chrony->options().port); servers_0.emplace_back(std::move(chrony)); @@ -215,8 +203,6 @@ TEST_F(MiniChronydTest, MultiTierBasic) { for (auto idx = 3; idx < 5; ++idx) { MiniChronydOptions options; options.index = idx; - options.bindaddress = GetBindIpForDaemon(idx + 1, kDefaultBindMode); - options.port = base_port + idx * 10; options.local = false; for (const auto& ref : servers_0) { MiniChronydServerOptions server_options; @@ -224,8 +210,8 @@ TEST_F(MiniChronydTest, MultiTierBasic) { server_options.address = ref->options().bindaddress; options.servers.emplace_back(std::move(server_options)); } - unique_ptr<MiniChronyd> chrony(new MiniChronyd(options)); - ASSERT_OK(chrony->Start()); + unique_ptr<MiniChronyd> chrony; + ASSERT_OK(StartChronydAtAutoReservedPort(&chrony, &options)); ntp_endpoints_1.emplace_back(chrony->options().bindaddress, chrony->options().port); servers_1.emplace_back(std::move(chrony)); diff --git a/src/kudu/clock/test/mini_chronyd_test_util.cc b/src/kudu/clock/test/mini_chronyd_test_util.cc new file mode 100644 index 0000000..17d8ac5 --- /dev/null +++ b/src/kudu/clock/test/mini_chronyd_test_util.cc @@ -0,0 +1,74 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 "kudu/clock/test/mini_chronyd_test_util.h" + +#include <cstdint> +#include <memory> +#include <string> +#include <utility> + +#include "kudu/clock/test/mini_chronyd.h" +#include "kudu/mini-cluster/mini_cluster.h" +#include "kudu/util/net/net_util.h" +#include "kudu/util/net/sockaddr.h" +#include "kudu/util/net/socket.h" +#include "kudu/util/status.h" + +using kudu::cluster::MiniCluster; +using std::string; +using std::unique_ptr; + +namespace kudu { +namespace clock { + +namespace { + +// Reserve and bind port for NTP server socket, returning the bound address +// and port. The 'socket' object has outer lifecycle: it's is necessary +// to keep the port reserved. +Status ReservePort(int index, unique_ptr<Socket>* socket, + string* address, uint16_t* port) { + RETURN_NOT_OK(MiniCluster::ReserveDaemonSocket( + MiniCluster::EXTERNAL_SERVER, index, kDefaultBindMode, socket)); + Sockaddr addr; + RETURN_NOT_OK((*socket)->GetSocketAddress(&addr)); + *address = addr.host(); + *port = static_cast<uint16_t>(addr.port()); + return Status::OK(); +} + +} // anonymous namespace + +Status StartChronydAtAutoReservedPort(unique_ptr<MiniChronyd>* chronyd, + MiniChronydOptions* options) { + MiniChronydOptions opts; + if (options) { + opts = *options; + } + unique_ptr<Socket> socket; + RETURN_NOT_OK(ReservePort(opts.index, &socket, &opts.bindaddress, &opts.port)); + chronyd->reset(new MiniChronyd(opts)); + RETURN_NOT_OK((*chronyd)->Start()); + if (options) { + *options = std::move(opts); + } + return Status::OK(); +} + +} // namespace clock +} // namespace kudu diff --git a/src/kudu/clock/test/mini_chronyd_test_util.h b/src/kudu/clock/test/mini_chronyd_test_util.h new file mode 100644 index 0000000..66d66f8 --- /dev/null +++ b/src/kudu/clock/test/mini_chronyd_test_util.h @@ -0,0 +1,42 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +#pragma once + +#include <memory> + +#include "kudu/gutil/port.h" +#include "kudu/util/status.h" + +namespace kudu { +namespace clock { + +class MiniChronyd; +struct MiniChronydOptions; + +// Reserve a port and start chronyd, outputting the result chronyd object +// wrapped into std::unique_ptr smart pointer. The 'options' parameter is +// in-out: the bound port and address are output into the 'port' and +// 'bindaddress' fields correspondingly. All other fields of the 'options' +// parameter are untouched, and the only field affective the port reservation +// process is the 'index' field. +Status StartChronydAtAutoReservedPort( + std::unique_ptr<MiniChronyd>* chronyd, + MiniChronydOptions* options = nullptr) WARN_UNUSED_RESULT; + +} // namespace clock +} // namespace kudu
