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