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

Reply via email to