Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add integration tests for deploy #4666

Merged
merged 5 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/cli/src/commands/app/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ impl CmdAppCreate {
.interact()?)
{
let cmd_deploy = CmdAppDeploy {
quiet: false,
api: self.api.clone(),
env: self.env.clone(),
fmt: ItemFormatOpts {
Expand Down
159 changes: 92 additions & 67 deletions lib/cli/src/commands/app/deploy.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::AsyncCliCommand;
use super::{util::login_user, AsyncCliCommand};
use crate::{
commands::{app::create::CmdAppCreate, package::publish::PackagePublish, PublishWait},
opts::{ApiOpts, ItemFormatOpts, WasmerEnv},
Expand Down Expand Up @@ -82,6 +82,13 @@ pub struct CmdAppDeploy {
/// Whether or not to automatically bump the package version if publishing.
#[clap(long)]
pub bump: bool,

/// Don't print any message.
///
/// The only message that will be printed is the one signaling the successfullness of the
/// operation.
#[clap(long)]
pub quiet: bool,
}

impl CmdAppDeploy {
Expand All @@ -102,7 +109,7 @@ impl CmdAppDeploy {
let publish_cmd = PackagePublish {
env: self.env.clone(),
dry_run: false,
quiet: false,
quiet: self.quiet,
package_name: None,
package_version: None,
no_validate: false,
Expand All @@ -128,6 +135,7 @@ impl CmdAppDeploy {

async fn get_owner(
&self,
client: &WasmerClient,
app: &serde_yaml::Value,
app_config_path: &PathBuf,
) -> anyhow::Result<(String, String)> {
Expand All @@ -146,28 +154,19 @@ impl CmdAppDeploy {
anyhow::bail!("No owner specified: use --owner XXX");
}

match self.api.client() {
Ok(client) => {
let user = wasmer_api::query::current_user_with_namespaces(&client, None).await?;
let owner = crate::utils::prompts::prompt_for_namespace(
"Who should own this app?",
None,
Some(&user),
)?;

let new_raw_config = format!("owner: {owner}\n{r_ret}");
let user = wasmer_api::query::current_user_with_namespaces(client, None).await?;
let owner = crate::utils::prompts::prompt_for_namespace(
"Who should own this app?",
None,
Some(&user),
)?;

std::fs::write(app_config_path, &new_raw_config).with_context(|| {
format!("Could not write file: '{}'", app_config_path.display())
})?;
let new_raw_config = format!("owner: {owner}\n{r_ret}");

Ok((owner.clone(), new_raw_config))
std::fs::write(app_config_path, &new_raw_config)
.with_context(|| format!("Could not write file: '{}'", app_config_path.display()))?;

}
Err(e) => anyhow::bail!(
"Can't determine user info: {e}. Please, user `wasmer login` before deploying an app or use the --owner <owner> flag to signal the owner of the app to deploy."
),
}
Ok((owner.clone(), new_raw_config))
}
async fn create(&self) -> anyhow::Result<()> {
eprintln!("It seems you are trying to create a new app!");
Expand Down Expand Up @@ -201,10 +200,8 @@ impl AsyncCliCommand for CmdAppDeploy {
type Output = ();

async fn run_async(self) -> Result<Self::Output, anyhow::Error> {
let client = self
.api
.client()
.with_context(|| "Can't begin deploy flow")?;
let client =
login_user(&self.api, &self.env, !self.non_interactive, "deploy an app").await?;

let base_dir_path = self.path.clone().unwrap_or(std::env::current_dir()?);
let (app_config_path, base_dir_path) = {
Expand Down Expand Up @@ -241,7 +238,7 @@ impl AsyncCliCommand for CmdAppDeploy {

// We want to allow the user to specify the app name interactively.
let app_yaml: serde_yaml::Value = serde_yaml::from_str(&config_str)?;
let (owner, mut config_str) = self.get_owner(&app_yaml, &app_config_path).await?;
let (owner, mut config_str) = self.get_owner(&client, &app_yaml, &app_config_path).await?;

// We want to allow the user to specify the app name interactively.
let app_yaml: serde_yaml::Value = serde_yaml::from_str(&config_str)?;
Expand Down Expand Up @@ -272,8 +269,12 @@ impl AsyncCliCommand for CmdAppDeploy {
format!("Could not read file '{}'", &app_config_path.display())
})?;
} else {
eprintln!("The app.yaml does not specify any app name.");
eprintln!("Please, use the --app_name <app_name> to specify the name of the app.");
if !self.quiet {
eprintln!("The app.yaml does not specify any app name.");
eprintln!(
"Please, use the --app_name <app_name> to specify the name of the app."
);
}

anyhow::bail!(
"Cannot proceed with the deployment as the app spec in path {} does not have
Expand All @@ -297,17 +298,19 @@ impl AsyncCliCommand for CmdAppDeploy {
let mut app_cfg_new = app_config.clone();
let opts = match &app_cfg_new.package {
PackageSource::Path(ref path) => {
eprintln!(
"Loading local package (manifest path: {})",
PathBuf::from(path)
.canonicalize()?
.join("wasmer.toml")
.display()
);

let package_id = self
.publish(&client, owner.clone(), PathBuf::from(path))
.await?;
let path = PathBuf::from(path);

let path = if path.is_absolute() {
path
} else {
app_config_path.parent().unwrap().join(path)
};

if !self.quiet {
eprintln!("Loading local package (manifest path: {})", path.display());
}

let package_id = self.publish(&client, owner.clone(), path).await?;

app_cfg_new.package = package_id.into();

Expand All @@ -331,16 +334,20 @@ impl AsyncCliCommand for CmdAppDeploy {
if let Some(package) = &manifest.package {
if let Some(name) = &package.name {
if name == &n.full_name() {
eprintln!(
"Found local package (manifest path: {}).",
manifest_path.display()
);
eprintln!("The `package` field in `app.yaml` specified the same named package ({}).", name);
eprintln!("This behaviour is deprecated.");
if !self.quiet {
eprintln!(
"Found local package (manifest path: {}).",
manifest_path.display()
);
eprintln!("The `package` field in `app.yaml` specified the same named package ({}).", name);
eprintln!("This behaviour is deprecated.");
}

let theme = dialoguer::theme::ColorfulTheme::default();
if self.non_interactive {
eprintln!("Hint: replace `package: {}` with `package: .` to replicate the intended behaviour.", n);
if !self.quiet {
eprintln!("Hint: replace `package: {}` with `package: .` to replicate the intended behaviour.", n);
}
anyhow::bail!("deprecated deploy behaviour")
} else if Confirm::with_theme(&theme)
.with_prompt("Change package to '.' in app.yaml?")
Expand Down Expand Up @@ -380,10 +387,11 @@ impl AsyncCliCommand for CmdAppDeploy {
wait,
}
} else {
eprintln!(
if !self.quiet {
eprintln!(
"{}: the package will not be published and the deployment will fail if the package does not already exist.",
"Warning".yellow().bold()
);
"Warning".yellow().bold());
}
DeployAppOpts {
app: &app_config,
original_config: Some(
Expand Down Expand Up @@ -461,7 +469,9 @@ impl AsyncCliCommand for CmdAppDeploy {
app.name.bold().to_string()
};

eprintln!("\nDeploying app {} to Wasmer Edge...\n", pretty_name);
if !self.quiet {
eprintln!("\nDeploying app {} to Wasmer Edge...\n", pretty_name);
}

let app_version = deploy_app(&client, opts.clone()).await?;

Expand Down Expand Up @@ -490,7 +500,7 @@ impl AsyncCliCommand for CmdAppDeploy {
})?;
}

wait_app(&client, opts.clone(), app_version.clone()).await?;
wait_app(&client, opts.clone(), app_version.clone(), self.quiet).await?;

if self.fmt.format == crate::utils::render::ItemFormat::Json {
println!("{}", serde_json::to_string_pretty(&app_version)?);
Expand Down Expand Up @@ -558,6 +568,7 @@ pub async fn wait_app(
client: &WasmerClient,
opts: DeployAppOpts<'_>,
version: DeployAppVersion,
quiet: bool,
) -> Result<(DeployApp, DeployAppVersion), anyhow::Error> {
let wait = opts.wait;
let make_default = opts.make_default;
Expand All @@ -574,22 +585,26 @@ pub async fn wait_app(
.await
.context("could not fetch app from backend")?;

eprintln!(
"App {} ({}) was successfully deployed 🚀",
app.name.bold(),
app.owner.global_name.bold()
);
eprintln!("{}", app.url.blue().bold().underline());
eprintln!();
eprintln!("→ Unique URL: {}", version.url);
eprintln!("→ Dashboard: {}", app.admin_url);
if !quiet {
eprintln!(
"App {} ({}) was successfully deployed 🚀",
app.name.bold(),
app.owner.global_name.bold()
);
eprintln!("{}", app.url.blue().bold().underline());
eprintln!();
eprintln!("→ Unique URL: {}", version.url);
eprintln!("→ Dashboard: {}", app.admin_url);
}

match wait {
WaitMode::Deployed => {}
WaitMode::Reachable => {
eprintln!();
eprintln!("Waiting for new deployment to become available...");
eprintln!("(You can safely stop waiting now with CTRL-C)");
if !quiet {
eprintln!();
eprintln!("Waiting for new deployment to become available...");
eprintln!("(You can safely stop waiting now with CTRL-C)");
}

let stderr = std::io::stderr();

Expand All @@ -604,13 +619,18 @@ pub async fn wait_app(
loop {
let total_elapsed = start.elapsed();
if total_elapsed > Duration::from_secs(60 * 5) {
eprintln!();
if !quiet {
eprintln!();
}
anyhow::bail!("\nApp still not reachable after 5 minutes...");
}

{
let mut lock = stderr.lock();
write!(&mut lock, ".").unwrap();

if !quiet {
write!(&mut lock, ".").unwrap();
}
lock.flush().unwrap();
}

Expand All @@ -625,8 +645,13 @@ pub async fn wait_app(
.unwrap_or_default();

if header == version.id.inner() {
eprintln!("\nNew version is now reachable at {check_url}");
eprintln!("{} Deployment complete", "✔".green().bold());
if !quiet {
eprintln!();
}
eprintln!(
"{} Deployment complete, new version reachable at {check_url}",
"𖥔".yellow().bold()
);
break;
}

Expand Down
60 changes: 60 additions & 0 deletions lib/cli/src/commands/app/util.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
use anyhow::{bail, Context};
use colored::Colorize;
use dialoguer::Confirm;
use wasmer_api::{
global_id::{GlobalId, NodeKind},
types::DeployApp,
WasmerClient,
};
use wasmer_config::app::AppConfigV1;

use crate::{
commands::Login,
opts::{ApiOpts, WasmerEnv},
};

/// App identifier.
///
/// Can be either a namespace/name a plain name or an app id.
Expand Down Expand Up @@ -184,3 +191,56 @@ mod tests {
);
}
}

pub(super) async fn login_user(
api: &ApiOpts,
env: &WasmerEnv,
interactive: bool,
msg: &str,
) -> anyhow::Result<WasmerClient> {
if let Ok(client) = api.client() {
return Ok(client);
}

let theme = dialoguer::theme::ColorfulTheme::default();

if api.token.is_none() {
if interactive {
eprintln!(
"{}: You need to be logged in to {msg}.",
"WARN".yellow().bold()
);

if Confirm::with_theme(&theme)
.with_prompt("Do you want to login now?")
.interact()?
{
Login {
no_browser: false,
wasmer_dir: env.wasmer_dir.clone(),
registry: api
.registry
.clone()
.map(|l| wasmer_registry::wasmer_env::Registry::from(l.to_string())),
token: api.token.clone(),
cache_dir: Some(env.cache_dir.clone()),
}
.run_async()
.await?;
// self.api = ApiOpts::default();
} else {
anyhow::bail!("Stopping the flow as the user is not logged in.")
}
} else {
let bin_name = match std::env::args().next() {
Some(n) => n,
None => String::from("wasmer"),
};
eprintln!("You are not logged in. Use the `--token` flag or log in (use `{bin_name} login`) to {msg}.");

anyhow::bail!("Stopping execution as the user is not logged in.")
}
}

api.client()
}
2 changes: 1 addition & 1 deletion lib/cli/src/commands/package/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ pub(super) async fn login_user(
.await?;
// self.api = ApiOpts::default();
} else {
anyhow::bail!("Stopping the push flow as the user is not logged in.")
anyhow::bail!("Stopping the flow as the user is not logged in.")
}
} else {
let bin_name = self::macros::bin_name!();
Expand Down
Loading
Loading