Skip to content
1,950 changes: 758 additions & 1,192 deletions Cargo.lock

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions lib/backend-api/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -3577,6 +3577,12 @@ type Mutation {
"""Claim a perishable app as their own."""
claimPerishableApp(input: ClaimPerishableAppInput!): ClaimPerishableAppPayload
deployViaAutobuild(input: DeployViaAutobuildInput!): DeployViaAutobuildPayload

"""Create a new vault from yaml config"""
upsertVault(input: UpsertVaultInput!): UpsertVaultPayload

"""Attach vault to an app"""
attachVault(input: AttachVaultInput!): AttachVaultPayload
tokenAuth(input: ObtainJSONWebTokenInput!): ObtainJSONWebTokenPayload
generateDeployToken(input: GenerateDeployTokenInput!): GenerateDeployTokenPayload
verifyAccessToken(token: String): Verify
Expand Down Expand Up @@ -4451,6 +4457,43 @@ input JobDefinitionInput {
timeout: String = "10m"
}

"""Create a new vault from yaml config"""
type UpsertVaultPayload {
vault: Vault!
clientMutationId: String
}

type Vault implements Node {
createdAt: DateTime!
updatedAt: DateTime!
name: String!
app: DeployApp

"""The ID of the object"""
id: ID!
}

input UpsertVaultInput {
"""YAML Configuration for vault"""
config: String!
clientMutationId: String
}

"""Attach vault to an app"""
type AttachVaultPayload {
app: DeployApp!
clientMutationId: String
}

input AttachVaultInput {
"""ID of app to which the vault will be attached."""
appId: ID!

"""ID of vault to attach."""
vaultId: ID!
clientMutationId: String
}

type ObtainJSONWebTokenPayload {
payload: GenericScalar!
refreshExpiresIn: Int!
Expand Down
44 changes: 44 additions & 0 deletions lib/backend-api/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2132,3 +2132,47 @@ pub async fn upsert_domain_from_zone_file(

Ok(domain)
}


/// Upsert a vault from file
pub async fn upsert_vault(
client: &WasmerClient,
vault_contents: impl Into<&String>,
) -> Result<Vault, anyhow::Error> {
let vars = UpsertVaultVariables {
config: vault_contents.into()
};
let res = client
.run_graphql_strict(types::UpsertVault::build(vars))
.await?;

let vault = res
.upsert_vault
.context("Upserting vault from file failed")?
.vault;

Ok(vault)
}

/// Upsert a vault from file
pub async fn attach_vault(
client: &WasmerClient,
vault_id: cynic::Id,
app_id: cynic::Id,
) -> Result<DeployApp, anyhow::Error> {
let vars = AttachVaultVariables {
vault_id: &vault_id,
app_id: &app_id,
};
let res = client
.run_graphql_strict(types::AttachVault::build(vars))
.await?;

let app = res
.attach_vault
.context("Attaching vault to app failed")?
.app;

Ok(app)
}

43 changes: 43 additions & 0 deletions lib/backend-api/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,7 @@ mod queries {
#[derive(cynic::QueryFragment, Serialize, Debug, Clone)]
pub struct DeployApp {
pub id: cynic::Id,
pub global_name: String,
pub name: String,
pub created_at: DateTime,
pub updated_at: DateTime,
Expand Down Expand Up @@ -2307,6 +2308,48 @@ mod queries {
Unknown,
}

#[derive(cynic::QueryVariables, Debug)]
pub struct UpsertVaultVariables<'a> {
pub config: &'a str,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "Mutation", variables = "UpsertVaultVariables")]
pub struct UpsertVault {
#[arguments(input: { config: $config })]
pub upsert_vault: Option<UpsertVaultPayload>,
}

#[derive(cynic::QueryFragment, Debug)]
pub struct UpsertVaultPayload {
pub vault: Vault,
}

#[derive(cynic::QueryFragment, Debug)]
pub struct Vault {
pub id: cynic::Id,
pub name: String,
}

#[derive(cynic::QueryVariables, Debug)]
pub struct AttachVaultVariables<'a> {
pub app_id: &'a cynic::Id,
pub vault_id: &'a cynic::Id,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "Mutation", variables = "AttachVaultVariables")]
pub struct AttachVault {
#[arguments(input: { appId: $app_id, vaultId: $vault_id })]
pub attach_vault: Option<AttachVaultPayload>,
}

#[derive(cynic::QueryFragment, Debug)]
pub struct AttachVaultPayload {
pub app: DeployApp,
}


impl NodeDeployAppVersions {
pub fn into_app(self) -> Option<DeployAppVersionsById> {
match self {
Expand Down
39 changes: 39 additions & 0 deletions lib/cli/src/commands/app/attach_vault.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//! Attach a vault to the specified app

use anyhow::Context;
use super::util::AppIdentOpts;
use crate::{commands::AsyncCliCommand, config::WasmerEnv};

/// Attach a vault to the specified app
#[derive(clap::Parser, Debug)]
pub struct CmdAppAttachVault {
#[clap(flatten)]
env: WasmerEnv,

/// Vault ID to attach
vault_id: String,

#[clap(flatten)]
ident: AppIdentOpts,


}

#[async_trait::async_trait]
impl AsyncCliCommand for CmdAppAttachVault {
type Output = ();

async fn run_async(self) -> Result<(), anyhow::Error> {
let client = self.env.client()?;
let (_ident, app) = self.ident.load_app(&client).await?;

let resp_app = wasmer_backend_api::query::attach_vault(
&self.env.client()?,
self.vault_id.into(),
app.id,
).await.context("Attaching vault to app")?;

println!("Attached vault to app {}", resp_app.global_name);
Ok(())
}
}
2 changes: 2 additions & 0 deletions lib/cli/src/commands/app/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ impl CmdAppCreate {
redirect: None,
extra: IndexMap::new(),
jobs: None,
enable_email: None,
vault: None,
}
}

Expand Down
35 changes: 33 additions & 2 deletions lib/cli/src/commands/app/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use wasmer_backend_api::{
WasmerClient,
};
use wasmer_config::{
app::AppConfigV1,
app::{AppConfigV1, VaultFile},
package::{PackageIdent, PackageSource},
};

Expand Down Expand Up @@ -367,10 +367,41 @@ impl AsyncCliCommand for CmdAppDeploy {
&app_config_path,
serde_yaml::to_string(&original_app_config)?,
)
.with_context(|| format!("Could not write file: '{}'", app_config_path.display()))?;
.with_context(|| format!("Could not write file: '{}'", app_config_path.display()))?;

let mut app_config = original_app_config.clone();


app_config.vault = if let Some(vault) = &app_config.vault {
let filepath = app_config_path.parent().unwrap().join(vault);

if filepath.exists() && filepath.is_file() {
// It's a real file — treat it as such
let data = std::fs::read(&filepath).context("Unable to read vault file")?;
let vault_contents = String::from_utf8(data).context("Vault file is not valid UTF-8")?;


let mut vault: VaultFile = serde_yaml::from_str(&vault_contents)?;
if vault.owner.is_none() {
vault.owner = Some(owner.clone())
}

let created_vault = wasmer_backend_api::query::upsert_vault(
&self.env.client()?,
&serde_yaml::to_string(&vault)?,
)
.await
.context("Upserting vault")?;
Some(created_vault.id.into_inner())
} else {
// Not a file — treat it as vault name directly
Some(vault.clone())
}
} else {
None
};


app_config.owner = Some(owner.clone());

let wait = if self.no_wait {
Expand Down
3 changes: 3 additions & 0 deletions lib/cli/src/commands/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod regions;
pub mod secrets;
pub mod version;
pub mod volumes;
pub mod attach_vault;

mod util;

Expand Down Expand Up @@ -42,6 +43,7 @@ pub enum CmdApp {
Database(database::CmdAppDatabase),
#[clap(subcommand, alias = "deployments")]
Deployment(deployments::CmdAppDeployment),
AttachVault(attach_vault::CmdAppAttachVault),
}

#[async_trait::async_trait]
Expand Down Expand Up @@ -73,6 +75,7 @@ impl AsyncCliCommand for CmdApp {
Self::Volume(cmd) => cmd.run_async().await,
Self::Database(cmd) => cmd.run_async().await,
Self::Deployment(cmd) => cmd.run_async().await,
Self::AttachVault(cmd) => cmd.run_async().await,
}
}
}
2 changes: 1 addition & 1 deletion lib/cli/src/commands/app/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ pub fn get_app_config_from_dir(

if !app_config_path.exists() || !app_config_path.is_file() {
bail!(
"Could not find app.yaml at path: '{}'.\nPlease specify an app like 'wasmer app get <namespace>/<name>' or 'wasmer app get <name>`'",
"Could not find app.yaml at path: '{}'.\nPlease specify an app like '<namespace>/<name>'",
app_config_path.display()
);
}
Expand Down
8 changes: 8 additions & 0 deletions lib/cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ mod inspect;
#[cfg(feature = "journal")]
mod journal;
pub(crate) mod namespace;

pub(crate) mod vault;
mod package;
mod run;
mod self_update;
Expand Down Expand Up @@ -214,6 +216,7 @@ impl WasmerCmd {
Some(Cmd::Journal(journal)) => journal.run(),
Some(Cmd::Ssh(ssh)) => ssh.run(),
Some(Cmd::Namespace(namespace)) => namespace.run(),
Some(Cmd::Vault(vault)) => vault.run(),
Some(Cmd::Domain(namespace)) => namespace.run(),
None => {
WasmerCmd::command().print_long_help()?;
Expand Down Expand Up @@ -434,6 +437,11 @@ enum Cmd {
#[clap(subcommand, alias = "namespaces")]
Namespace(crate::commands::namespace::CmdNamespace),

/// Manage Wasmer namespaces
#[clap(subcommand, alias = "vaults")]
Vault(crate::commands::vault::CmdVault),


/// Manage DNS records
#[clap(subcommand, alias = "domains")]
Domain(crate::commands::domain::CmdDomain),
Expand Down
64 changes: 64 additions & 0 deletions lib/cli/src/commands/vault/create.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use std::path::PathBuf;

use anyhow::Context;
use crate::{commands::AsyncCliCommand, config::WasmerEnv, opts::ItemFormatOpts};

/// Create a new vault.
#[derive(clap::Parser, Debug)]
pub struct CmdVaultCreate {
#[clap(flatten)]
fmt: ItemFormatOpts,

#[clap(flatten)]
env: WasmerEnv,

/// File containing vault schema
#[clap(value_name = "FILE", default_value = "vault.yaml")]
filename: PathBuf,
}

#[async_trait::async_trait]
impl AsyncCliCommand for CmdVaultCreate {
type Output = ();

async fn run_async(self) -> Result<(), anyhow::Error> {

if !self.filename.try_exists().context("checking filename existance")? {
anyhow::bail!("Vault file does not exist");
}
if !self.filename.is_file() {
anyhow::bail!("File at path specified by `filename` argument is not valid. Maybe it's a directory?");
}

let data = std::fs::read(&self.filename).context("Unable to read file")?;
let vault_contents = String::from_utf8(data).context("Not a valid UTF-8 sequence")?;
let vault = wasmer_backend_api::query::upsert_vault(
&self.env.client()?,
&vault_contents
).await.context("Upserting vault")?;

println!("vault created: {}", vault.name);

let mut yaml_value: serde_yaml::Value = serde_yaml::from_str(&vault_contents)
.context("Parsing vault file into YAML")?;


if let serde_yaml::Value::Mapping(ref mut map) = yaml_value {
map.insert(
serde_yaml::Value::String("vault_id".to_string()),
serde_yaml::Value::String(vault.id.into_inner()),
);
} else {
anyhow::bail!("Vault file does not have a valid YAML object at root");
}

let updated_yaml = serde_yaml::to_string(&yaml_value).context("Serializing updated vault file")?;

std::fs::write(&self.filename, updated_yaml)
.context("Writing updated vault file with vault_id")?;

println!("vault_id written back into {}", self.filename.display());

Ok(())
}
}
Loading
Loading