Skip to content

Commit

Permalink
feat(cargo-shuttle): automatic login via console, login --prompt (#1913)
Browse files Browse the repository at this point in the history
* feat(cargo-shuttle): automatic login, login --input

* feat: use callbackPort instead

* feat: fix cors

* feat: new ws-based auth flow

* chore: update some deps

* fix: args, print token

* clippy

* fix: websocket reading logic, ping loop

* refa: common ws read function

* nit: clean up ws code

* nit

* --input -> --prompt
  • Loading branch information
jonaro00 authored Nov 8, 2024
1 parent c8e6d15 commit 792e681
Show file tree
Hide file tree
Showing 15 changed files with 449 additions and 340 deletions.
363 changes: 190 additions & 173 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ strum = { version = "0.26.1", features = ["derive"] }
tar = "0.4.38"
tempfile = "3.4.0"
test-context = "0.3.0"
thiserror = "1.0.37"
thiserror = "2"
tokio = "1.22.0"
tokio-stream = "0.1.11"
tokio-tungstenite = { version = "0.20.1", features = [
Expand Down
4 changes: 2 additions & 2 deletions admin/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ use std::{fs, io, path::PathBuf};

use clap::{Error, Parser, Subcommand};
use shuttle_common::{
constants::API_URL_PRODUCTION,
constants::API_URL_RS,
models::{project::ComputeTier, user::UserId},
};

#[derive(Parser, Debug)]
pub struct Args {
/// run this command against the api at the supplied url
#[arg(long, default_value = API_URL_PRODUCTION, env = "SHUTTLE_API")]
#[arg(long, default_value = API_URL_RS, env = "SHUTTLE_API")]
pub api_url: String,

#[command(subcommand)]
Expand Down
15 changes: 12 additions & 3 deletions api-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ impl ShuttleApiClient {
.context("parsing API version info")
}

pub async fn get_device_auth_ws(&self) -> Result<WebSocketStream<MaybeTlsStream<TcpStream>>> {
self.ws_get("/device-auth/ws".to_owned())
.await
.with_context(|| "failed to connect to auth endpoint")
}

pub async fn check_project_name(&self, project_name: &str) -> Result<bool> {
let url = format!("{}/projects/name/{project_name}", self.api_url);

Expand Down Expand Up @@ -483,14 +489,17 @@ impl ShuttleApiClient {
pub async fn ws_get(&self, path: String) -> Result<WebSocketStream<MaybeTlsStream<TcpStream>>> {
let ws_url = self.api_url.clone().replace("http", "ws");
let url = format!("{ws_url}{path}");
let mut request = url.into_client_request()?;
let mut req = url.into_client_request()?;

#[cfg(feature = "tracing")]
debug!("WS Request: {} {}", req.method(), req.uri());

if let Some(ref api_key) = self.api_key {
let auth_header = Authorization::bearer(api_key.as_ref())?;
request.headers_mut().typed_insert(auth_header);
req.headers_mut().typed_insert(auth_header);
}

let (stream, _) = connect_async(request).await.with_context(|| {
let (stream, _) = connect_async(req).await.with_context(|| {
#[cfg(feature = "tracing")]
error!("failed to connect to websocket");
"could not connect to websocket"
Expand Down
4 changes: 2 additions & 2 deletions api-client/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl ToJson for reqwest::Response {
.unwrap_or_else(|_| format!("[{} bytes]", bytes.len()));

#[cfg(feature = "tracing")]
tracing::trace!(response = %string, "Parsing response to JSON");
tracing::trace!(response = %string, "Parsing response as JSON");

if matches!(
status_code,
Expand All @@ -28,7 +28,7 @@ impl ToJson for reqwest::Response {
serde_json::from_str(&string).context("failed to parse a successful response")
} else {
#[cfg(feature = "tracing")]
tracing::trace!("Parsing response to common error");
tracing::trace!("Parsing response as API error");

let res: ApiError = match serde_json::from_str(&string) {
Ok(res) => res,
Expand Down
9 changes: 7 additions & 2 deletions cargo-shuttle/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,18 @@ dunce = { workspace = true }
flate2 = { workspace = true }
futures = { workspace = true }
git2 = { version = "0.19.0", default-features = false }
gix = { version = "0.66.0", default-features = false, features = [
gix = { version = "0.67.0", default-features = false, features = [
"blocking-http-transport-reqwest-rust-tls",
"worktree-mutation",
] }
globset = "0.4.13"
headers = { workspace = true }
home = { workspace = true }
# TODO: make local provisioner server use these:
# http = "1"
# http-body-util = "0.1"
# hyper-1 = { package = "hyper", version = "1.0", features = ["full"] }
# hyper-util = { version = "0.1.10", features = ["full"] }
hyper = { workspace = true }
ignore = "0.4.20"
indicatif = "0.17.3"
Expand Down Expand Up @@ -69,6 +74,6 @@ zip = "2.2.0"

[dev-dependencies]
assert_cmd = "2.0.6"
rexpect = "0.5.0"
rexpect = "0.6.0"
# Publication of this crate will fail if this is changed to a workspace dependency
shuttle-common-tests = { path = "../common-tests" }
32 changes: 20 additions & 12 deletions cargo-shuttle/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ use clap::{
Args, Parser, Subcommand, ValueEnum,
};
use clap_complete::Shell;
use shuttle_common::constants::{DEFAULT_IDLE_MINUTES, EXAMPLES_REPO};
use shuttle_common::constants::{DEFAULT_IDLE_MINUTES, EXAMPLES_REPO, SHUTTLE_CONSOLE_URL};
use shuttle_common::resource;

#[derive(Parser)]
#[command(
version,
next_help_heading = "Global options",
// Cargo passes in the subcommand name to the invoked executable. Use a
// hidden, optional positional argument to deal with it.
arg(clap::Arg::new("dummy")
Expand All @@ -26,10 +27,8 @@ use shuttle_common::resource;
.hide(true))
)]
pub struct ShuttleArgs {
#[command(flatten)]
pub project_args: ProjectArgs,
/// URL for the Shuttle API to target (mainly for development)
#[arg(global = true, long, env = "SHUTTLE_API")]
#[arg(global = true, long, env = "SHUTTLE_API", hide = true)]
pub api_url: Option<String>,
/// Disable network requests that are not strictly necessary. Limits some features.
#[arg(global = true, long, env = "SHUTTLE_OFFLINE")]
Expand All @@ -40,6 +39,8 @@ pub struct ShuttleArgs {
/// Target Shuttle's development environment
#[arg(global = true, long, env = "SHUTTLE_BETA", hide = true)]
pub beta: bool,
#[command(flatten)]
pub project_args: ProjectArgs,

#[command(subcommand)]
pub cmd: Command,
Expand All @@ -51,7 +52,7 @@ pub struct ProjectArgs {
/// Specify the working directory
#[arg(global = true, long, visible_alias = "wd", default_value = ".", value_parser = OsStringValueParser::new().try_map(parse_path))]
pub working_directory: PathBuf,
/// Specify the name or id of the project (overrides crate name)
/// Specify the name or id of the project
#[arg(global = true, long = "name", visible_alias = "id")]
// in alpha mode, this is always a name
pub name_or_id: Option<String>,
Expand Down Expand Up @@ -93,10 +94,9 @@ impl ProjectArgs {
}
}

/// A cargo command for the Shuttle platform (https://www.shuttle.rs/)
/// CLI for the Shuttle platform (https://www.shuttle.dev/)
///
/// See the CLI docs (https://docs.shuttle.rs/getting-started/shuttle-commands)
/// for more information.
/// See the CLI docs for more information: https://docs.shuttle.dev/guides/cli
#[derive(Subcommand)]
pub enum Command {
/// Generate a Shuttle project from a template
Expand Down Expand Up @@ -160,6 +160,7 @@ pub enum GenerateCommand {
}

#[derive(Args)]
#[command(next_help_heading = "Table options")]
pub struct TableArgs {
/// Output tables without borders
#[arg(long, default_value_t = false)]
Expand Down Expand Up @@ -197,12 +198,12 @@ pub enum ResourceCommand {
/// List the resources for a project
#[command(visible_alias = "ls")]
List {
#[command(flatten)]
table: TableArgs,

/// Show secrets from resources (e.g. a password in a connection string)
#[arg(long, default_value_t = false)]
show_secrets: bool,

#[command(flatten)]
table: TableArgs,
},
/// Delete a resource
#[command(visible_alias = "rm")]
Expand Down Expand Up @@ -307,10 +308,17 @@ pub struct ProjectStartArgs {
}

#[derive(Args, Clone, Debug, Default)]
#[command(next_help_heading = "Login options")]
pub struct LoginArgs {
/// API key for the Shuttle platform
/// Prompt to paste the API key instead of opening the browser
#[arg(long, conflicts_with = "api_key", alias = "input")]
pub prompt: bool,
/// Log in with this Shuttle API key
#[arg(long)]
pub api_key: Option<String>,
/// URL to the Shuttle Console for automatic login
#[arg(long, env = "SHUTTLE_CONSOLE", default_value = SHUTTLE_CONSOLE_URL, hide_default_value = true)]
pub console_url: String,
}

#[derive(Args, Clone, Debug)]
Expand Down
4 changes: 2 additions & 2 deletions cargo-shuttle/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use std::path::{Path, PathBuf};

use anyhow::{anyhow, Context, Result};
use serde::{Deserialize, Serialize};
use shuttle_common::constants::API_URL_BETA;
use shuttle_common::constants::API_URL_DEFAULT;
use shuttle_common::constants::API_URL_DEFAULT_BETA;
use tracing::trace;

use crate::args::ProjectArgs;
Expand Down Expand Up @@ -423,7 +423,7 @@ impl RequestContext {
} else if let Some(api_url) = self.global.as_ref().unwrap().api_url() {
api_url
} else if beta {
API_URL_BETA.to_string()
API_URL_DEFAULT_BETA.to_string()
} else {
API_URL_DEFAULT.to_string()
}
Expand Down
Loading

0 comments on commit 792e681

Please sign in to comment.