bindings: allow choice between edenapi backends

Summary: Allow users to configure which HTTP client backend to use for the Eden API via the `edenapi.backend` config option. Valid options are `curl` and `hyper`, with `curl` being the default.

Reviewed By: quark-zju

Differential Revision: D14657871

fbshipit-source-id: 7a9972d2380fbbd5ed62d1accae764dc03ca4c29
This commit is contained in:
Arun Kulshreshtha 2019-04-05 17:29:49 -07:00 committed by Facebook Github Bot
parent 6c8c87dea1
commit 6705e2d120
7 changed files with 47 additions and 29 deletions

View File

@ -124,6 +124,8 @@ Configs for Eden API (HTTP data fetching):
``edenapi.url`` specifies the base URL of the API server.
``edenapi.backend`` specifies which HTTP client library to use.
Eden API TLS credentials are configured using the auth section:
``auth.edenapi.prefix``: base URL (without scheme) for which to set credentials.
@ -218,6 +220,7 @@ configitem("remotefilelog", "server", default=None)
# Config items for HTTP data fetching.
configitem("edenapi", "enabled", default=False)
configitem("edenapi", "url", default=None)
configitem("edenapi", "backend", default="curl")
testedwith = "ships-with-fb-hgext"

View File

@ -47,4 +47,5 @@ def initclient(ui, repo):
url = getbaseurl(ui)
creds = getcreds(ui, url)
cachepath = shallowutil.getcachepath(ui)
return edenapi.client(url, cachepath, repo.name, creds)
backend = ui.config("edenapi", "backend")
return edenapi.client(url, cachepath, repo.name, backend, creds)

View File

@ -22,6 +22,7 @@ encoding = { path = "../../../../lib/encoding" }
env_logger = "0.5.13"
failure = "0.1.3"
lz4-pyframe = { path = "../../../../lib/lz4-pyframe" }
log = "0.4.6"
mutationstore = { path = "../../../../lib/mutationstore" }
nodemap = { path = "../../../../lib/nodemap" }
pathmatcher = { path = "../../../../lib/pathmatcher" }

View File

@ -7,9 +7,11 @@ use std::str;
use cpython::*;
use cpython_failure::ResultPyErrExt;
use ::edenapi::{Config, EdenApi, EdenApiHyperClient};
use encoding::{local_bytes_to_path, path_to_local_bytes};
use failure::format_err;
use log;
use ::edenapi::{Config, EdenApi, EdenApiCurlClient, EdenApiHyperClient};
use types::{Key, Node};
pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
@ -20,18 +22,20 @@ pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
}
py_class!(class client |py| {
data inner: EdenApiHyperClient;
data inner: Box<dyn EdenApi>;
def __new__(
_cls,
base_url: &PyBytes,
cache_path: &PyBytes,
repo: &PyBytes,
backend: &PyBytes,
client_creds: Option<(&PyBytes, &PyBytes)> = None
) -> PyResult<client> {
let base_url = str::from_utf8(base_url.data(py)).map_pyerr::<exc::RuntimeError>(py)?;
let cache_path = str::from_utf8(cache_path.data(py)).map_pyerr::<exc::RuntimeError>(py)?;
let cache_path = local_bytes_to_path(&cache_path.data(py)).map_pyerr::<exc::RuntimeError>(py)?;
let repo = str::from_utf8(repo.data(py)).map_pyerr::<exc::RuntimeError>(py)?;
let backend = str::from_utf8(backend.data(py)).map_pyerr::<exc::RuntimeError>(py)?;
let mut config = Config::new()
.base_url_str(base_url)
@ -45,7 +49,22 @@ py_class!(class client |py| {
config = config.client_creds(cert, key);
}
let inner = EdenApiHyperClient::new(config).map_pyerr::<exc::RuntimeError>(py)?;
let inner = match backend {
"curl" => {
log::debug!("Using curl-backed Eden API client");
let client = EdenApiCurlClient::new(config).map_pyerr::<exc::RuntimeError>(py)?;
Box::new(client) as Box<dyn EdenApi>
},
"hyper" => {
log::debug!("Using hyper-backed Eden API client");
let client = EdenApiHyperClient::new(config).map_pyerr::<exc::RuntimeError>(py)?;
Box::new(client) as Box<dyn EdenApi>
},
invalid => {
return Err(format_err!("Invalid Eden API backend: {}", invalid)).map_pyerr::<exc::RuntimeError>(py);
},
};
client::create_instance(py, inner)
}

View File

@ -6,23 +6,25 @@ use failure::Fallible;
use types::Key;
pub trait EdenApi {
pub trait EdenApi: Send {
/// Hit the API server's /health_check endpoint.
/// Returns Ok(()) if the expected response is received, or an Error otherwise
/// (e.g., if there was a connection problem or an unexpected repsonse).
fn health_check(&self) -> Fallible<()>;
/// Fetch the content of the specified file from the API server and write
/// it to a datapack in the configured cache directory. Returns the path
/// Fetch the content of the specified files from the API server and write
/// them to a datapack in the configured cache directory. Returns the path
/// of the resulting packfile.
fn get_files(&self, keys: impl IntoIterator<Item = Key>) -> Fallible<PathBuf>;
///
/// Note that the keys are passed in as a `Vec` rather than using `IntoIterator`
/// in order to keep this trait object-safe.
fn get_files(&self, keys: Vec<Key>) -> Fallible<PathBuf>;
/// Fetch the history of the specified file from the API server and write
/// it to a historypack in the configured cache directory. Returns the path
/// Fetch the history of the specified files from the API server and write
/// them to a historypack in the configured cache directory. Returns the path
/// of the resulting packfile.
fn get_history(
&self,
keys: impl IntoIterator<Item = Key>,
max_depth: Option<u32>,
) -> Fallible<PathBuf>;
///
/// Note that the keys are passed in as a `Vec` rather than using `IntoIterator`
/// in order to keep this trait object-safe.
fn get_history(&self, keys: Vec<Key>, max_depth: Option<u32>) -> Fallible<PathBuf>;
}

View File

@ -101,7 +101,7 @@ impl EdenApi for EdenApiCurlClient {
Ok(())
}
fn get_files(&self, keys: impl IntoIterator<Item = Key>) -> Fallible<PathBuf> {
fn get_files(&self, keys: Vec<Key>) -> Fallible<PathBuf> {
let url = self.repo_base_url()?.join(paths::DATA)?;
let request = FileDataRequest {
@ -124,11 +124,7 @@ impl EdenApi for EdenApiCurlClient {
write_datapack(self.pack_cache_path(), response)
}
fn get_history(
&self,
keys: impl IntoIterator<Item = Key>,
max_depth: Option<u32>,
) -> Fallible<PathBuf> {
fn get_history(&self, keys: Vec<Key>, max_depth: Option<u32>) -> Fallible<PathBuf> {
let url = self.repo_base_url()?.join(paths::HISTORY)?;
let request = FileHistoryRequest {

View File

@ -126,7 +126,7 @@ impl EdenApi for EdenApiHyperClient {
/// Fetch the content of the specified file from the API server and write
/// it to a datapack in the configured cache directory. Returns the path
/// of the resulting packfile.
fn get_files(&self, keys: impl IntoIterator<Item = Key>) -> Fallible<PathBuf> {
fn get_files(&self, keys: Vec<Key>) -> Fallible<PathBuf> {
let client = Arc::clone(&self.client);
let prefix = self.repo_base_url()?.join(paths::GET_FILE)?;
@ -145,11 +145,7 @@ impl EdenApi for EdenApiHyperClient {
/// Fetch the history of the specified file from the API server and write
/// it to a historypack in the configured cache directory. Returns the path
/// of the resulting packfile.
fn get_history(
&self,
keys: impl IntoIterator<Item = Key>,
max_depth: Option<u32>,
) -> Fallible<PathBuf> {
fn get_history(&self, keys: Vec<Key>, max_depth: Option<u32>) -> Fallible<PathBuf> {
let client = Arc::clone(&self.client);
let prefix = self.repo_base_url()?.join(paths::GET_HISTORY)?;