mirror of
https://github.com/facebook/sapling.git
synced 2024-10-05 14:28:17 +03:00
Add ServiceAddress
to utils
Summary: This diff adds `ServiceAddress` to utils. This is a class that encapsulates an address of a service that could be a traditional hostname & port pair as well as an SMC tier name. This eliminates the needs of the manually resolving network address for remote services. Reviewed By: chadaustin Differential Revision: D14845257 fbshipit-source-id: 9fe9847cca4bba0170be94b9c209247342708574
This commit is contained in:
parent
08b96e33b3
commit
13da025c6c
80
eden/fs/utils/ServiceAddress.cpp
Normal file
80
eden/fs/utils/ServiceAddress.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2019-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
#include "eden/fs/utils/ServiceAddress.h"
|
||||
|
||||
#include <folly/Random.h>
|
||||
#include <folly/SocketAddress.h>
|
||||
#include <folly/String.h>
|
||||
#include <folly/logging/xlog.h>
|
||||
#include <optional>
|
||||
#include "eden/fs/eden-config.h"
|
||||
|
||||
#ifdef EDEN_HAVE_SERVICEROUTER
|
||||
#include <servicerouter/client/cpp2/ServiceRouter.h>
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace eden {
|
||||
|
||||
ServiceAddress::ServiceAddress(std::string name) : name_(std::move(name)){};
|
||||
|
||||
ServiceAddress::ServiceAddress(std::string hostname, uint16_t port)
|
||||
: name_(std::make_pair(std::move(hostname), port)){};
|
||||
|
||||
std::optional<SocketAddressWithHostname>
|
||||
ServiceAddress::getSocketAddressBlocking() {
|
||||
if (std::holds_alternative<HostPortPair>(name_)) {
|
||||
return addressFromHostname();
|
||||
}
|
||||
return addressFromSMCTier();
|
||||
}
|
||||
|
||||
std::optional<SocketAddressWithHostname> ServiceAddress::addressFromHostname() {
|
||||
auto hostPort = std::get<HostPortPair>(name_);
|
||||
auto addr = folly::SocketAddress();
|
||||
addr.setFromHostPort(hostPort.first, hostPort.second);
|
||||
return std::make_pair(addr, hostPort.first);
|
||||
}
|
||||
|
||||
std::optional<SocketAddressWithHostname> ServiceAddress::addressFromSMCTier(
|
||||
std::shared_ptr<facebook::servicerouter::ServiceCacheIf> selector) {
|
||||
#ifdef EDEN_HAVE_SERVICEROUTER
|
||||
auto selection = selector->getSelection(std::get<std::string>(name_));
|
||||
|
||||
if (selection.hosts.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// TODO(zeyi, t42568801): better host selection algorithm
|
||||
auto selected = folly::Random::rand32(selection.hosts.size());
|
||||
const auto& host = selection.hosts[selected];
|
||||
auto location = host->location();
|
||||
|
||||
return std::make_pair(
|
||||
folly::SocketAddress(location.getIpAddress(), location.getPort()),
|
||||
location.getHostname());
|
||||
#else
|
||||
return std::nullopt;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::optional<SocketAddressWithHostname> ServiceAddress::addressFromSMCTier() {
|
||||
#ifdef EDEN_HAVE_SERVICEROUTER
|
||||
auto& factory = servicerouter::cpp2::getClientFactory();
|
||||
auto selector = factory.getSelector();
|
||||
|
||||
return addressFromSMCTier(selector);
|
||||
#else
|
||||
return std::nullopt;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace eden
|
||||
} // namespace facebook
|
81
eden/fs/utils/ServiceAddress.h
Normal file
81
eden/fs/utils/ServiceAddress.h
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2019-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
namespace folly {
|
||||
class SocketAddress;
|
||||
} // namespace folly
|
||||
|
||||
namespace facebook {
|
||||
namespace servicerouter {
|
||||
class ServiceCacheIf;
|
||||
}
|
||||
|
||||
namespace eden {
|
||||
|
||||
// `folly::SocketAddress` represents the IP and port of the server, and the
|
||||
// `std::string` is the hostname the client should use.
|
||||
using SocketAddressWithHostname = std::pair<folly::SocketAddress, std::string>;
|
||||
|
||||
using HostPortPair = std::pair<std::string, uint16_t>;
|
||||
|
||||
/// This class represents a remote service that can be identified with a
|
||||
/// traditional hostname and port pair as well as a smc tier name. Users that
|
||||
/// only need a socket address can use this class to avoid worrying about
|
||||
/// underlying details.
|
||||
class ServiceAddress {
|
||||
public:
|
||||
/// Constructs a `ServiceAddress` from SMC tier name
|
||||
explicit ServiceAddress(std::string name);
|
||||
/// Constructs a `ServiceAddress` from a pair of hostname and port;
|
||||
ServiceAddress(std::string hostname, uint16_t port);
|
||||
|
||||
ServiceAddress(const ServiceAddress&) = default;
|
||||
ServiceAddress& operator=(const ServiceAddress& other) = default;
|
||||
|
||||
ServiceAddress(ServiceAddress&&) = default;
|
||||
ServiceAddress& operator=(ServiceAddress&& other) = default;
|
||||
|
||||
/// Synchronously gets the socket address and hostname of the service this
|
||||
/// object represents.
|
||||
///
|
||||
/// When `ServiceAddress` is `Type::Hostname`:
|
||||
///
|
||||
/// Throws `std::invalid_argument` if the hostname string is invalid.
|
||||
/// See `folly::SocketAddress::setFromHostPort` for details.
|
||||
///
|
||||
/// Throws `std::system_error` if the hostname is unabled to be resolved.
|
||||
///
|
||||
/// When `ServiceAddress` is `Type::SmcTier`:
|
||||
///
|
||||
/// Always returns `std::nullopt` when there is no ServiceRouter support.
|
||||
///
|
||||
/// Note: this function WILL block for performing DNS and SMC resolution.
|
||||
std::optional<SocketAddressWithHostname> getSocketAddressBlocking();
|
||||
|
||||
/// Test method
|
||||
std::optional<SocketAddressWithHostname> addressFromSMCTier(
|
||||
std::shared_ptr<facebook::servicerouter::ServiceCacheIf> selector);
|
||||
|
||||
private:
|
||||
std::optional<SocketAddressWithHostname> addressFromHostname();
|
||||
std::optional<SocketAddressWithHostname> addressFromSMCTier();
|
||||
|
||||
std::variant<HostPortPair, std::string> name_;
|
||||
};
|
||||
} // namespace eden
|
||||
} // namespace facebook
|
96
eden/fs/utils/test/ServiceAddressTest.cpp
Normal file
96
eden/fs/utils/test/ServiceAddressTest.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2018-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
#include "eden/fs/utils/ServiceAddress.h"
|
||||
|
||||
#include <folly/SocketAddress.h>
|
||||
#include <folly/logging/xlog.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "eden/fs/eden-config.h"
|
||||
|
||||
#ifdef EDEN_HAVE_SERVICEROUTER
|
||||
#include <servicerouter/client/cpp2/ServiceRouter.h>
|
||||
#endif
|
||||
|
||||
using namespace facebook::eden;
|
||||
|
||||
TEST(ServiceAddressTest, fromHostnameAndPort) {
|
||||
auto hostname = "::1";
|
||||
auto svc = ServiceAddress{hostname, 1234};
|
||||
auto result = svc.getSocketAddressBlocking();
|
||||
|
||||
EXPECT_EQ(result->first.getAddressStr(), "::1");
|
||||
EXPECT_EQ(result->first.getPort(), 1234);
|
||||
EXPECT_EQ(result->second, "::1");
|
||||
}
|
||||
|
||||
TEST(ServiceAddressTest, nonexistentHostname) {
|
||||
auto hostname = "this-hostname-should-never-exist";
|
||||
auto svc = ServiceAddress{hostname, 1234};
|
||||
EXPECT_THROW(svc.getSocketAddressBlocking(), std::system_error);
|
||||
}
|
||||
|
||||
#ifdef EDEN_HAVE_SERVICEROUTER
|
||||
|
||||
namespace {
|
||||
using namespace facebook::servicerouter;
|
||||
|
||||
class MockServiceCacheIf : public ServiceCacheIf {
|
||||
public:
|
||||
virtual Selection getSelection(
|
||||
const std::string& serviceName,
|
||||
const ServiceOptions& /* options */,
|
||||
const ConnConfigs& /* overrides */) override {
|
||||
Selection selection;
|
||||
|
||||
if (serviceName == "mononoke-apiserver") {
|
||||
auto location = std::make_shared<HostInfoLocation>("::1", 1234);
|
||||
location->setHostname("some-hostname");
|
||||
|
||||
selection.hosts.push_back(std::make_shared<HostInfo>(
|
||||
std::make_unique<HostInfoProperties>(), std::move(location)));
|
||||
}
|
||||
|
||||
return selection;
|
||||
}
|
||||
|
||||
virtual void getSelectionAsync(
|
||||
const std::string& /* serviceName */,
|
||||
DebugContext&& /* dbgCtx */,
|
||||
SelectionCacheCallback&& /* callback */,
|
||||
folly::EventBase* /* eventBase */,
|
||||
ServiceOptions&& /* options */,
|
||||
ConnConfigs&& /* overrides */) override {}
|
||||
|
||||
bool invalidateSelection(
|
||||
const string& /* serviceName */,
|
||||
const Config& /* cfg */) override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST(ServiceAddressTest, fromSMCTier) {
|
||||
auto tier = "mononoke-apiserver";
|
||||
auto svc = ServiceAddress{tier};
|
||||
auto result = svc.addressFromSMCTier(std::make_shared<MockServiceCacheIf>());
|
||||
|
||||
EXPECT_EQ(result->first.getAddressStr(), "::1");
|
||||
EXPECT_EQ(result->first.getPort(), 1234);
|
||||
EXPECT_EQ(result->second, "some-hostname");
|
||||
}
|
||||
|
||||
TEST(ServiceAddressTest, failFromSMCTier) {
|
||||
auto tier = "nonexistent-tier";
|
||||
auto svc = ServiceAddress{tier};
|
||||
auto result = svc.addressFromSMCTier(std::make_shared<MockServiceCacheIf>());
|
||||
EXPECT_EQ(result, std::nullopt);
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user