Skip to content

Commit

Permalink
api/cli: support for HTTP proxies (#56)
Browse files Browse the repository at this point in the history
  • Loading branch information
mcobzarenco authored Mar 4, 2021
1 parent a33d888 commit eb44f2e
Show file tree
Hide file tree
Showing 11 changed files with 75 additions and 41 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Unreleased

# v0.5.2

- Add support for using an HTTP proxy for all requests. The proxy configuration is saved as part of the context.
Additionally, the proxy can be overridden / specified as a one off using a global command line argument
`--proxy https://proxy.example` (e.g. similar to `--endpoint`).

# v0.5.1

## Breaking
Expand Down
10 changes: 6 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ The [api](/api) directory contains a Rust client library for reinfer which can b

Statically linked binaries with no dependencies are provided for selected platforms:

- [Linux (x86_64-unknown-linux-musl)](https://reinfer.io/public/cli/bin/x86_64-unknown-linux-musl/0.5.1/re)
- [macOS (x86_64-apple-darwin)](https://reinfer.io/public/cli/bin/x86_64-apple-darwin/0.5.1/re)
- [Linux (x86_64-unknown-linux-musl)](https://reinfer.io/public/cli/bin/x86_64-unknown-linux-musl/0.5.2/re)
- [macOS (x86_64-apple-darwin)](https://reinfer.io/public/cli/bin/x86_64-apple-darwin/0.5.2/re)

### Debian / Ubuntu

You can download a `.deb` package [here](https://reinfer.io/public/cli/debian/reinfer-cli_0.5.1_amd64.deb).
You can download a `.deb` package [here](https://reinfer.io/public/cli/debian/reinfer-cli_0.5.2_amd64.deb).

### From Source

Expand Down Expand Up @@ -135,8 +135,9 @@ re --endpoint http://localhost:8000 --token $REINFER_TOKEN get datasets
Contexts help avoid having to manually specify the token and endpoint with every command. A _context_ is composed of

- The authentication token (which user?)
- The reinfer endpoint to talk to, typically `https://reinfer.io` (which cluster?)
- A human rememberable name
- The Re:infer cluster endpoint to talk to, typically `https://reinfer.io`
- (Optional) An HTTP proxy to use for all requests
- A memorable name which serves as an identifier for the "context"

Commands for managing _contexts_ are under `re config` and allow one to create, update, set and delete contexts. Run `re config -h` to see all the options.

Expand All @@ -150,7 +151,7 @@ W Be careful, API tokens are stored in cleartext in /home/marius/.config/reinfer
I New context `production` was created.
```

The token and endpoint for the current context will be used for all subsequent commands (these be overwritten as a one off using the `--token` and `--endpoint` arguments).
The current context will be used for all subsequent commands.

```
➜ re get datasets
Expand All @@ -161,6 +162,15 @@ The token and endpoint for the current context will be used for all subsequent c
InvestmentBank/margin-call-large 6d00b9f69ab059f6 2019-05-11 09:23:43 IB Margin Call Large
```

Any of the context settings can be overwritten as a one off using global flags such as `--token`, `--endpoint` and `--proxy`.

```
➜ re --proxy http://proxy.example get datasets
```

Adding a context with a name that already exists will allow you to update any of the saved settings.


### Uploading Comments

WIP
Expand Down
2 changes: 1 addition & 1 deletion api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "reinfer-client"
version = "0.5.1"
version = "0.5.2"
description = "API client for Re:infer"
homepage = "https://github.com/reinfer/cli"
readme = "README.md"
Expand Down
14 changes: 9 additions & 5 deletions api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use log::debug;
use reqwest::{
blocking::{multipart::Form, Client as HttpClient, Response as HttpResponse},
header::{HeaderMap, HeaderName, HeaderValue},
IntoUrl, Result as ReqwestResult,
IntoUrl, Proxy, Result as ReqwestResult,
};
use serde::{Deserialize, Serialize};
use serde_json::json;
Expand Down Expand Up @@ -90,6 +90,7 @@ pub struct Config {
pub endpoint: Url,
pub token: Token,
pub accept_invalid_certificates: bool,
pub proxy: Option<Url>,
/// Retry settings to use, if any. This will apply to all requests except for POST requests
/// which are not idempotent (as they cannot be naively retried).
pub retry_config: Option<RetryConfig>,
Expand All @@ -101,6 +102,7 @@ impl Default for Config {
endpoint: DEFAULT_ENDPOINT.clone(),
token: Token("".to_owned()),
accept_invalid_certificates: false,
proxy: None,
retry_config: None,
}
}
Expand Down Expand Up @@ -1168,11 +1170,13 @@ impl Endpoints {
}

fn build_http_client(config: &Config) -> Result<HttpClient> {
HttpClient::builder()
let mut builder = HttpClient::builder()
.gzip(true)
.danger_accept_invalid_certs(config.accept_invalid_certificates)
.build()
.map_err(Error::BuildHttpClient)
.danger_accept_invalid_certs(config.accept_invalid_certificates);
if let Some(proxy) = config.proxy.clone() {
builder = builder.proxy(Proxy::all(proxy).map_err(Error::BuildHttpClient)?);
}
builder.build().map_err(Error::BuildHttpClient)
}

fn build_headers(config: &Config) -> Result<HeaderMap> {
Expand Down
5 changes: 3 additions & 2 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "reinfer-cli"
version = "0.5.1"
version = "0.5.2"
description = "Command line interface for the reinfer platform."
homepage = "https://github.com/reinfer/cli"
readme = "README.md"
Expand Down Expand Up @@ -32,8 +32,9 @@ reqwest = { version = "0.11.0", default-features = false }
serde = { version = "1.0.114", features = ["derive"] }
serde_json = "1.0.55"
structopt = { version = "0.3.15", default-features = false }
url = {version= "2.2.1", features = ["serde"] }

reinfer-client = { version = "0.5.1", path = "../api" }
reinfer-client = { version = "0.5.2", path = "../api" }

[dev-dependencies]
uuid = { version = "0.8.1", features = ["v4"] }
Expand Down
5 changes: 5 additions & 0 deletions cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,15 @@ pub struct Args {
/// context, if any.
pub token: Option<String>,

#[structopt(long = "proxy")]
/// URL for an HTTP proxy that will be used for all requests if specified
pub proxy: Option<Url>,

#[structopt(subcommand)]
pub command: Command,
}

#[allow(clippy::large_enum_variant)]
#[derive(Debug, StructOpt)]
pub enum Command {
#[structopt(name = "completion")]
Expand Down
23 changes: 21 additions & 2 deletions cli/src/commands/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ pub enum ConfigArgs {
#[structopt(long = "accept-invalid-certificates", short = "k")]
/// Whether to accept invalid TLS certificates
accept_invalid_certificates: bool,

#[structopt(long = "proxy")]
/// URL for an HTTP proxy that will be used for all requests if specified
proxy: Option<Option<Url>>,
},

#[structopt(name = "current")]
Expand Down Expand Up @@ -71,7 +75,9 @@ pub fn run(
let mut contexts = config.get_all_contexts().clone();
contexts.sort_unstable_by(|lhs, rhs| lhs.name.cmp(&rhs.name));
let mut table = new_table();
table.set_titles(row![bFg => "Active", "Context", "Endpoint", "Insecure", "Token"]);
table.set_titles(
row![bFg => "Active", "Context", "Endpoint", "Insecure", "Token", "Proxy"],
);
for context in contexts.iter() {
let active = config
.get_current_context()
Expand All @@ -95,7 +101,12 @@ pub fn run(
context.token.clone().unwrap_or_else(String::new)
} else {
"<Hidden>".into()
}
},
context
.proxy
.clone()
.map(|url| url.to_string())
.unwrap_or_else(String::new)
]);
}
table.printstd();
Expand All @@ -108,12 +119,14 @@ pub fn run(
endpoint,
token,
accept_invalid_certificates,
proxy,
} => {
add_or_edit_context(
name,
token,
endpoint,
*accept_invalid_certificates,
proxy,
config.clone(),
config_path,
)?;
Expand Down Expand Up @@ -161,6 +174,7 @@ fn add_or_edit_context(
token: &Option<String>,
endpoint: &Option<Url>,
accept_invalid_certificates: bool,
proxy: &Option<Option<Url>>,
mut config: ReinferConfig,
config_path: impl AsRef<Path>,
) -> Result<()> {
Expand Down Expand Up @@ -228,6 +242,11 @@ fn add_or_edit_context(
endpoint,
token,
accept_invalid_certificates,
proxy: proxy.clone().unwrap_or_else(|| {
existing_context
.as_ref()
.and_then(|context| context.proxy.clone())
}),
};

let update_existing = existing_context.is_some();
Expand Down
5 changes: 1 addition & 4 deletions cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,10 @@ impl ReinferConfig {
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ContextConfig {
pub name: String,
#[serde(
deserialize_with = "crate::utils::deserialize_url",
serialize_with = "crate::utils::serialize_url"
)]
pub endpoint: Url,
pub token: Option<String>,
pub accept_invalid_certificates: bool,
pub proxy: Option<Url>,
}

pub fn read_reinfer_config(path: impl AsRef<Path>) -> Result<ReinferConfig> {
Expand Down
6 changes: 6 additions & 0 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ fn client_from_args(args: &Args, config: &ReinferConfig) -> Result<Client> {
));
}

let proxy = args
.proxy
.clone()
.or_else(|| current_context.and_then(|context| context.proxy.clone()));

// Retry everything but the very first request.
// Retry wait schedule is [5s, 10s, 20s, fail]. (Plus the time for each attempt to timeout.)
let retry_config = RetryConfig {
Expand All @@ -100,6 +105,7 @@ fn client_from_args(args: &Args, config: &ReinferConfig) -> Result<Client> {
endpoint,
token,
accept_invalid_certificates,
proxy,
retry_config: Some(retry_config),
})
.context("Failed to initialise the HTTP client.")
Expand Down
18 changes: 1 addition & 17 deletions cli/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ use colored::{ColoredString, Colorize};
use env_logger::{fmt::Formatter as LogFormatter, Builder as LogBuilder};
use lazy_static::lazy_static;
use log::{Level as LogLevel, LevelFilter as LogLevelFilter, Record as LogRecord};
use reqwest::Url;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::Serialize;
use std::{
env,
io::{self, Write},
Expand Down Expand Up @@ -100,18 +99,3 @@ lazy_static! {
pub static ref LOG_PREFIX_TRACE: ColoredString = "T".normal();
pub static ref LOG_PREFIX_INPUT: ColoredString = "*".blue().bold();
}

pub fn serialize_url<S>(value: &Url, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(value.as_str())
}

pub fn deserialize_url<'de, D>(deserializer: D) -> std::result::Result<Url, D::Error>
where
D: Deserializer<'de>,
{
let url_string: String = Deserialize::deserialize(deserializer)?;
Url::parse(&url_string).map_err(|error| serde::de::Error::custom(error.to_string()))
}

0 comments on commit eb44f2e

Please sign in to comment.