blob: d542882c63457608a017f0549a98b0a555621c6a [file] [log] [blame]
#ifndef THIRD_PARTY_MILOTIC_INTERNAL_CC_PROXY_PROXY_BUILDER_H_
#define THIRD_PARTY_MILOTIC_INTERNAL_CC_PROXY_PROXY_BUILDER_H_
#include <cstddef>
#include <functional>
#include <list>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/functional/any_invocable.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "redfish_query_engine/file/uds.h"
#include "grpcpp/security/authorization_policy_provider.h"
#include "proxy.h"
#include "proxy_config.pb.h"
#include "redfish_plugin.h"
#include "resource_authz.h"
#include "ssh_client.h"
#include "voyager/deferrable_priority_queue.hpp"
#include "voyager/priority_queue.hpp"
namespace milotic {
namespace internal {
absl::StatusOr<milotic_grpc_proxy::Configuration> LoadConfig(
const std::string& config_file_path);
} // namespace internal
// A PluginFactory is a wrapper to register factory functions for plugins so
// that they can be linked to configuration items. Each plugin should have a
// corresponding configuration field in the milotic_grpc_proxy.Plugin message.
// The plugin should also create a static PluginFactory with a function that
// creates an instance of the plugin using the `Plugin` configuration proto.
// Usually, this is done using REGISTER_REDFISH_PLUGIN with the appropriate
// template parameters indicating the proto field and plugin type. See
// test_plugin.cc for an example.
class PluginFactory {
public:
using FactoryFn = std::function<std::unique_ptr<RedfishPlugin>(
const milotic_grpc_proxy::Plugin&)>;
// The constructor is not thread-safe. PluginFactory objects should usually be
// global statics, so they are initialized in the same thread.
explicit PluginFactory(FactoryFn&& f) {
GetFunctionList().push_back(std::move(f));
}
PluginFactory(const PluginFactory&) = delete;
static std::unique_ptr<RedfishPlugin> CreatePlugin(
const milotic_grpc_proxy::Plugin& plugin);
// TODO(shounak): This is much cleaner with static proto reflection, but that
// is not yet available in OSS.
template <auto Get, auto Has, typename PluginType>
static std::unique_ptr<RedfishPlugin> Default(
const milotic_grpc_proxy::Plugin& plugin_conf) {
if (!(plugin_conf.*Has)()) return nullptr;
return std::make_unique<PluginType>((plugin_conf.*Get)());
}
private:
static std::vector<FactoryFn>& GetFunctionList() {
static std::vector<FactoryFn> list;
return list;
}
};
#define REGISTER_REDFISH_PLUGIN(_name, _type) \
::milotic::PluginFactory _name##_plugin_factory( \
::milotic::PluginFactory::Default< \
&::milotic_grpc_proxy::Plugin::_name, \
&::milotic_grpc_proxy::Plugin::has_##_name, _type>)
class ProxyBuilder {
public:
struct Options {
using ExecutorFactory =
absl::AnyInvocable<std::unique_ptr<voyager::Executor>(
const milotic_grpc_proxy::QueueOptions& options) const>;
using PermissionCheckerFactory =
absl::AnyInvocable<std::unique_ptr<PermissionChecker>() const>;
Options() : is_safe_uds_root(ecclesia::IsSafeUnixDomainSocketRoot) {}
Options& SafeUdsRoot(
std::function<bool(const std::string&)> new_is_safe_uds_root) {
is_safe_uds_root = new_is_safe_uds_root;
return *this;
}
Options& AddCredentials(Proxy::GrpcCredentials creds) {
credentials.push_back(std::move(creds));
return *this;
}
Options& AddAuthorizationPolicy(
std::shared_ptr<
grpc::experimental::AuthorizationPolicyProviderInterface>
provider) {
authorization_providers.push_back(std::move(provider));
return *this;
}
template <typename SshClientType>
Options& SetSshClientType() {
ssh_client_factory = SshClientType::Create;
return *this;
}
Options& SetExecutorFactory(ExecutorFactory new_executor_factory) {
executor_factory = std::move(new_executor_factory);
return *this;
}
Options& SetPermissionCheckerFactory(
PermissionCheckerFactory new_permission_checker_factory) {
permission_checker_factory = std::move(new_permission_checker_factory);
return *this;
}
std::function<bool(const std::string&)> is_safe_uds_root;
std::vector<Proxy::GrpcCredentials> credentials;
std::vector<std::shared_ptr<
grpc::experimental::AuthorizationPolicyProviderInterface>>
authorization_providers;
absl::AnyInvocable<absl::StatusOr<std::unique_ptr<SshClient>>(
SshClient::CreationOptions options) const>
ssh_client_factory;
ExecutorFactory executor_factory =
[](const milotic_grpc_proxy::QueueOptions& options) {
return std::make_unique<voyager::DefaultExecutor>();
};
PermissionCheckerFactory permission_checker_factory;
};
ProxyBuilder(const ProxyBuilder&) = delete;
ProxyBuilder& operator=(const ProxyBuilder&) = delete;
// Movable to support factory functions
ProxyBuilder(ProxyBuilder&&) = default;
static absl::StatusOr<ProxyBuilder> CreateAndStart(
const milotic_grpc_proxy::Configuration& config,
const Options& options = Options());
static absl::StatusOr<ProxyBuilder> CreateAndStart(
const std::string& config_file_path, const Options& options = Options());
~ProxyBuilder();
absl::Status Wait();
void Shutdown();
const voyager::DeferrablePriorityQueue& queue() const { return *queue_; }
private:
ProxyBuilder(size_t queue_size, int quantum,
std::unique_ptr<voyager::Executor> executor)
: queue_(std::make_unique<voyager::DeferrablePriorityQueue>(
queue_size, std::move(executor))),
quantum_(quantum) {}
absl::Status AddProxy(
const milotic_grpc_proxy::ProxyConfiguration& proxy_config,
const Options& options);
std::list<Proxy> proxies_;
std::unique_ptr<voyager::DeferrablePriorityQueue> queue_;
int quantum_;
};
} // namespace milotic
#endif // THIRD_PARTY_MILOTIC_INTERNAL_CC_PROXY_PROXY_BUILDER_H_