blob: 9bbf40a8ef6e57ed482baf122478d873335a36da [file] [log] [blame]
// 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 <sys/types.h>
#include <unistd.h>
#include <string>
#include <glog/logging.h>
#include <gtest/gtest.h>
#include "kudu/integration-tests/external_mini_cluster.h"
#include "kudu/gutil/stl_util.h"
#include "kudu/gutil/strings/substitute.h"
#include "kudu/gutil/strings/util.h"
#include "kudu/security/test/mini_kdc.h"
#include "kudu/util/metrics.h"
#include "kudu/util/net/net_util.h"
#include "kudu/util/test_util.h"
METRIC_DECLARE_entity(server);
METRIC_DECLARE_gauge_uint64(threads_running);
namespace kudu {
using std::string;
using strings::Substitute;
enum KerberosMode {
WITHOUT_KERBEROS, WITH_KERBEROS
};
class ExternalMiniClusterTest : public KuduTest,
public testing::WithParamInterface<KerberosMode> {};
INSTANTIATE_TEST_CASE_P(KerberosOnAndOff,
ExternalMiniClusterTest,
::testing::Values(WITHOUT_KERBEROS, WITH_KERBEROS));
TEST_P(ExternalMiniClusterTest, TestBasicOperation) {
ExternalMiniClusterOptions opts;
opts.enable_kerberos = GetParam() == WITH_KERBEROS;
// Hard-coded RPC ports for the masters. This is safe, as this unit test
// runs under a resource lock (see CMakeLists.txt in this directory).
// TODO we should have a generic method to obtain n free ports.
opts.master_rpc_ports = { 11010, 11011, 11012 };
opts.num_masters = opts.master_rpc_ports.size();
opts.num_tablet_servers = 3;
ExternalMiniCluster cluster(opts);
ASSERT_OK(cluster.Start());
// Verify each of the masters.
for (int i = 0; i < opts.num_masters; i++) {
SCOPED_TRACE(i);
ExternalMaster* master = CHECK_NOTNULL(cluster.master(i));
HostPort master_rpc = master->bound_rpc_hostport();
EXPECT_TRUE(HasPrefixString(master_rpc.ToString(), "127.0.0.1:")) << master_rpc.ToString();
HostPort master_http = master->bound_http_hostport();
EXPECT_TRUE(HasPrefixString(master_http.ToString(), "127.0.0.1:")) << master_http.ToString();
// Retrieve a thread metric, which should always be present on any master.
int64_t value;
ASSERT_OK(master->GetInt64Metric(&METRIC_ENTITY_server,
"kudu.master",
&METRIC_threads_running,
"value",
&value));
EXPECT_GT(value, 0);
}
// Verify each of the tablet servers.
for (int i = 0; i < opts.num_tablet_servers; i++) {
SCOPED_TRACE(i);
ExternalTabletServer* ts = CHECK_NOTNULL(cluster.tablet_server(i));
HostPort ts_rpc = ts->bound_rpc_hostport();
string expected_prefix = Substitute("$0:", cluster.GetBindIpForTabletServer(i));
EXPECT_NE(expected_prefix, "127.0.0.1") << "Should bind to unique per-server hosts";
EXPECT_TRUE(HasPrefixString(ts_rpc.ToString(), expected_prefix)) << ts_rpc.ToString();
HostPort ts_http = ts->bound_http_hostport();
EXPECT_TRUE(HasPrefixString(ts_http.ToString(), expected_prefix)) << ts_http.ToString();
// Retrieve a thread metric, which should always be present on any TS.
int64_t value;
ASSERT_OK(ts->GetInt64Metric(&METRIC_ENTITY_server,
"kudu.tabletserver",
&METRIC_threads_running,
"value",
&value));
EXPECT_GT(value, 0);
}
// Ensure that all of the tablet servers can register with the masters.
ASSERT_OK(cluster.WaitForTabletServerCount(opts.num_tablet_servers, MonoDelta::FromSeconds(30)));
// Restart a master and a tablet server. Make sure they come back up with the same ports.
ExternalMaster* master = cluster.master(0);
HostPort master_rpc = master->bound_rpc_hostport();
HostPort master_http = master->bound_http_hostport();
master->Shutdown();
ASSERT_OK(master->Restart());
ASSERT_EQ(master_rpc.ToString(), master->bound_rpc_hostport().ToString());
ASSERT_EQ(master_http.ToString(), master->bound_http_hostport().ToString());
ExternalTabletServer* ts = cluster.tablet_server(0);
HostPort ts_rpc = ts->bound_rpc_hostport();
HostPort ts_http = ts->bound_http_hostport();
ts->Shutdown();
ASSERT_OK(ts->Restart());
ASSERT_EQ(ts_rpc.ToString(), ts->bound_rpc_hostport().ToString());
ASSERT_EQ(ts_http.ToString(), ts->bound_http_hostport().ToString());
// Verify that, in a Kerberized cluster, if we drop our Kerberos environment,
// we can't make RPCs to a server.
if (opts.enable_kerberos) {
ASSERT_OK(cluster.kdc()->Kdestroy());
Status s = cluster.SetFlag(ts, "foo", "bar");
// The error differs depending on the version of Kerberos, so we match
// either message.
ASSERT_STR_MATCHES(s.ToString(), "Not authorized.*"
"(Credentials cache file.*not found|No Kerberos credentials)");
}
cluster.Shutdown();
}
} // namespace kudu