Skip to content

Commit

Permalink
Merge pull request #4666 from wasmerio/deploy-integration-tests
Browse files Browse the repository at this point in the history
Add integration tests for deploy
  • Loading branch information
syrusakbary authored May 10, 2024
2 parents 932a084 + c98f060 commit 80aaa4b
Show file tree
Hide file tree
Showing 9 changed files with 340 additions and 68 deletions.
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

0 comments on commit 80aaa4b

Please sign in to comment.