You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ga...@apache.org on 2018/09/20 22:04:56 UTC

[mesos] 02/02: Added a test to verify agent authentication retry backoff logic.

This is an automated email from the ASF dual-hosted git repository.

gaston pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git

commit 961d12219893aae2978d194140d3a71b0379d4ac
Author: Meng Zhu <mz...@mesosphere.io>
AuthorDate: Thu Sep 20 14:55:03 2018 -0700

    Added a test to verify agent authentication retry backoff logic.
    
    This test verifies that the agent backs-off properly when
    retrying authentication according to the configured parameters.
    
    Also mocked `Slave::authenticate()` for this test.
    
    Review: https://reviews.apache.org/r/68354/
---
 src/slave/slave.hpp                |   3 +-
 src/tests/authentication_tests.cpp | 109 +++++++++++++++++++++++++++++++++++++
 src/tests/mock_slave.cpp           |  10 ++++
 src/tests/mock_slave.hpp           |   8 +++
 4 files changed, 129 insertions(+), 1 deletion(-)

diff --git a/src/slave/slave.hpp b/src/slave/slave.hpp
index 6757eae..0bd3401 100644
--- a/src/slave/slave.hpp
+++ b/src/slave/slave.hpp
@@ -491,7 +491,8 @@ public:
   // not receive a ping.
   void pingTimeout(process::Future<Option<MasterInfo>> future);
 
-  void authenticate(Duration minTimeout, Duration maxTimeout);
+  // Made virtual for testing purpose.
+  virtual void authenticate(Duration minTimeout, Duration maxTimeout);
 
   // Helper routines to lookup a framework/executor.
   Framework* getFramework(const FrameworkID& frameworkId) const;
diff --git a/src/tests/authentication_tests.cpp b/src/tests/authentication_tests.cpp
index bbb7e10..1973810 100644
--- a/src/tests/authentication_tests.cpp
+++ b/src/tests/authentication_tests.cpp
@@ -44,6 +44,7 @@ using mesos::master::detector::StandaloneMasterDetector;
 using testing::_;
 using testing::Eq;
 using testing::Return;
+using testing::SaveArg;
 
 namespace mesos {
 namespace internal {
@@ -426,6 +427,114 @@ TEST_F(AuthenticationTest, RetrySlaveAuthentication)
   ASSERT_NE("", slaveRegisteredMessage->slave_id().value());
 }
 
+// Verify that the agent backs off properly when retrying authentication
+// according to the configured parameters.
+TEST_F(AuthenticationTest, SlaveAuthenticationRetryBackoff)
+{
+  Clock::pause();
+
+  Try<Owned<cluster::Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  Owned<MasterDetector> detector = master.get()->createDetector();
+
+  slave::Flags slaveFlags = CreateSlaveFlags();
+  slaveFlags.authentication_timeout_min = Seconds(5);
+  slaveFlags.authentication_timeout_max = Minutes(1);
+  slaveFlags.authentication_backoff_factor = Seconds(1);
+
+  // Expected retry timeout range:
+  //
+  //   [min, min + factor * 2^1]
+  //   [min, min + factor * 2^2]
+  //   ...
+  //   [min, min + factor * 2^N]
+  //   ...
+  //   [min, max] // Stop at max.
+  Duration expected[7][2] = {
+    {Seconds(5), Seconds(7)},
+    {Seconds(5), Seconds(9)},
+    {Seconds(5), Seconds(13)},
+    {Seconds(5), Seconds(21)},
+    {Seconds(5), Seconds(37)},
+    {Seconds(5), Minutes(1)},
+    {Seconds(5), Minutes(1)}
+  };
+
+  Try<Owned<cluster::Slave>> slave =
+    StartSlave(detector.get(), slaveFlags, true);
+  ASSERT_SOME(slave);
+
+  // Drop the first authentication attempt.
+  Future<Nothing> authenticate;
+  Duration minTimeout, maxTimeout;
+  EXPECT_CALL(*slave.get()->mock(), authenticate(_, _))
+    .WillOnce(DoAll(
+        SaveArg<0>(&minTimeout),
+        SaveArg<1>(&maxTimeout),
+        FutureSatisfy(&authenticate)))
+    .RetiresOnSaturation();
+
+  slave.get()->start();
+
+  // Trigger the first authentication request.
+  Clock::advance(slaveFlags.authentication_backoff_factor);
+  AWAIT_READY(authenticate);
+
+  for (int i = 0; i < 7; i++) {
+    EXPECT_EQ(minTimeout, expected[i][0]);
+    EXPECT_EQ(maxTimeout, expected[i][1]);
+
+    // Drop the authenticate message from the slave to incur retry.
+    Future<AuthenticateMessage> authenticateMessage =
+      DROP_PROTOBUF(AuthenticateMessage(), _, _);
+
+    // Drop the retry call and manually issue the retry call (instead
+    // of invoking the unmocked call directly in the expectation. We do this
+    // so that, even though clock is advanced for `authentication_timeout_max`
+    // in each iteration, we can still guarantee:
+    //
+    // (1) Retry only happens once in each iteration;
+    //
+    // (2) At the end of each iteration, the authentication timeout timer is
+    // not started. The timer will start only when we manually issue the
+    // retry call in the next iteration.
+    EXPECT_CALL(*slave.get()->mock(), authenticate(_, _))
+      .WillOnce(DoAll(
+          SaveArg<0>(&minTimeout),
+          SaveArg<1>(&maxTimeout),
+          FutureSatisfy(&authenticate)))
+      .RetiresOnSaturation();
+
+    slave.get()->mock()->unmocked_authenticate(minTimeout, maxTimeout);
+
+    // Slave should not retry until `slaveFlags.authentication_timeout_min`.
+    Clock::advance(slaveFlags.authentication_timeout_min - Milliseconds(1));
+    Clock::settle();
+    EXPECT_TRUE(authenticate.isPending());
+
+    // Slave will retry at least once in
+    // `slaveFlags.authentication_timeout_max`.
+    Clock::advance(
+        slaveFlags.authentication_timeout_max -
+        slaveFlags.authentication_timeout_min + Milliseconds(1));
+    Clock::settle();
+
+    AWAIT_READY(authenticateMessage);
+    AWAIT_READY(authenticate);
+  }
+
+  Future<AuthenticationCompletedMessage> authenticationCompletedMessage =
+    FUTURE_PROTOBUF(AuthenticationCompletedMessage(), _, _);
+
+  slave.get()->mock()->unmocked_authenticate(minTimeout, maxTimeout);
+
+  Clock::advance(slaveFlags.authentication_timeout_max);
+
+  // Slave should be able to get authenticated.
+  AWAIT_READY(authenticationCompletedMessage);
+}
+
 
 // This test ensures that when the master sees a new authentication
 // request for a particular agent or scheduler (we just test the
diff --git a/src/tests/mock_slave.cpp b/src/tests/mock_slave.cpp
index 94a5b0d..a78ca9c 100644
--- a/src/tests/mock_slave.cpp
+++ b/src/tests/mock_slave.cpp
@@ -134,6 +134,8 @@ MockSlave::MockSlave(
     .WillRepeatedly(Invoke(this, &MockSlave::unmocked_runTaskGroup));
   EXPECT_CALL(*this, killTask(_, _))
     .WillRepeatedly(Invoke(this, &MockSlave::unmocked_killTask));
+  EXPECT_CALL(*this, authenticate(_, _))
+    .WillRepeatedly(Invoke(this, &MockSlave::unmocked_authenticate));
   EXPECT_CALL(*this, removeFramework(_))
     .WillRepeatedly(Invoke(this, &MockSlave::unmocked_removeFramework));
   EXPECT_CALL(*this, __recover(_))
@@ -251,6 +253,14 @@ void MockSlave::unmocked_killTask(
 }
 
 
+void MockSlave::unmocked_authenticate(
+    Duration minTimeout,
+    Duration maxTimeout)
+{
+  slave::Slave::authenticate(minTimeout, maxTimeout);
+}
+
+
 void MockSlave::unmocked_removeFramework(slave::Framework* framework)
 {
   slave::Slave::removeFramework(framework);
diff --git a/src/tests/mock_slave.hpp b/src/tests/mock_slave.hpp
index 9a74bf3..3c0d602 100644
--- a/src/tests/mock_slave.hpp
+++ b/src/tests/mock_slave.hpp
@@ -191,6 +191,14 @@ public:
       const process::UPID& from,
       const KillTaskMessage& killTaskMessage);
 
+  MOCK_METHOD2(authenticate, void(
+      Duration minTimeout,
+      Duration maxTimeout));
+
+  void unmocked_authenticate(
+      Duration minTimeout,
+      Duration maxTimeout);
+
   MOCK_METHOD1(removeFramework, void(
       slave::Framework* framework));