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

Various volumes CLI fixes #5026

Merged
merged 1 commit into from
Aug 21, 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
16 changes: 14 additions & 2 deletions lib/backend-api/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type User implements Node & PackageOwner & Owner {

"""Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."""
username: String!
registerIntent: String
isEmailValidated: Boolean!
bio: String
location: String
Expand Down Expand Up @@ -950,6 +951,7 @@ type DeployApp implements Node & Owner {
secrets(offset: Int, before: String, after: String, first: Int, last: Int): SecretConnection!
usageMetrics(forRange: MetricRange!, variant: MetricType!): [UsageMetric]!
s3Url: URL
s3Credentials: S3Credentials
deleted: Boolean!
favicon: URL
screenshot(viewportSize: AppScreenshotViewportSize, appearance: AppScreenshotAppearance): URL
Expand Down Expand Up @@ -1090,6 +1092,12 @@ character sequences.
"""
scalar URL

type S3Credentials {
accessKey: String!
secretKey: String!
endpoint: String!
}

enum AppScreenshotViewportSize {
MOBILE
DESKTOP
Expand Down Expand Up @@ -1124,15 +1132,17 @@ enum LogStream {
}

type AppVersionVolume {
id: ID!
name: String!
s3Url: String!
mountPaths: [AppVersionVolumeMountPath]!
size: Int
usedSize: Int
usedSize: BigInt
}

type AppVersionVolumeMountPath {
path: String!
subpath: String!
subpath: String
}

type PackageDistribution {
Expand Down Expand Up @@ -3570,6 +3580,7 @@ input RegisterUserInput {
username: CaseInsensitiveString!
password: String!
acceptedTos: Boolean
intent: String
clientMutationId: String
}

Expand All @@ -3583,6 +3594,7 @@ input SocialAuthJWTInput {
provider: String!
accessToken: String!
register: Boolean = false
registerIntent: String
clientMutationId: String
}

Expand Down
58 changes: 12 additions & 46 deletions lib/backend-api/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,63 +209,29 @@ pub async fn get_app_volumes(
Ok(volumes)
}

/// S3 credentials for an app.
///
/// Retrieved with [`get_app_s3_credentials`].
#[derive(Clone)]
pub struct AppS3Credentials {
pub url: String,
pub access_key: String,
pub secret_key: String,
}

/// Load the S3 credentials.
///
/// S3 can be used to get access to an apps volumes.
pub async fn get_app_s3_credentials(
client: &WasmerClient,
app_id: impl Into<String>,
) -> Result<AppS3Credentials, anyhow::Error> {
const ACCESS_KEY_NAME: &str = "WASMER_APP_S3_ACCESS_KEY";
const SECRET_KEY_NAME: &str = "WASMER_APP_S3_SECRET_KEY";

) -> Result<types::S3Credentials, anyhow::Error> {
let app_id = app_id.into();

// Firt load the app to get the s3 url.
let app = get_app_by_id(client, app_id.clone()).await?;
let url = app.s3_url.context("app has no volumes")?;

// Load the secrets.
let secrets =
get_all_app_secrets_filtered(client, app_id, [ACCESS_KEY_NAME, SECRET_KEY_NAME]).await?;
let app1 = get_app_by_id(client, app_id.clone()).await?;

let access_key_id = secrets
.iter()
.find(|s| s.name == ACCESS_KEY_NAME)
.context("missing access key")?
.id
.clone();

let secret_key_id = secrets
.iter()
.find(|s| s.name == SECRET_KEY_NAME)
.context("missing secret key")?
.id
.clone();

let access_key = get_app_secret_value_by_id(client, access_key_id.into_inner())
.await?
.with_context(|| format!("No value found for secret with name '{}'", ACCESS_KEY_NAME))?;

let secret_key = get_app_secret_value_by_id(client, secret_key_id.into_inner())
let vars = types::GetDeployAppVars {
owner: app1.owner.global_name,
name: app1.name,
};
client
.run_graphql_strict(types::GetDeployAppS3Credentials::build(vars))
.await?
.with_context(|| format!("No value found for secret with name '{}'", SECRET_KEY_NAME))?;

Ok(AppS3Credentials {
url: url.0,
access_key,
secret_key,
})
.get_deploy_app
.context("app not found")?
.s3_credentials
.context("app does not have S3 credentials")
}

/// Load all available regions.
Expand Down
22 changes: 21 additions & 1 deletion lib/backend-api/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,26 @@ mod queries {
pub get_deploy_app: Option<DeployApp>,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "Query", variables = "GetDeployAppVars")]
pub struct GetDeployAppS3Credentials {
#[arguments(owner: $owner, name: $name)]
pub get_deploy_app: Option<AppWithS3Credentials>,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "DeployApp", variables = "GetDeployAppVars")]
pub struct AppWithS3Credentials {
pub s3_credentials: Option<S3Credentials>,
}

#[derive(cynic::QueryFragment, Debug)]
pub struct S3Credentials {
pub access_key: String,
pub secret_key: String,
pub endpoint: String,
}

#[derive(cynic::QueryVariables, Debug, Clone)]
pub struct PaginationVars {
pub offset: Option<i32>,
Expand Down Expand Up @@ -804,7 +824,7 @@ mod queries {
pub struct AppVersionVolume {
pub name: String,
pub size: Option<i32>,
pub used_size: Option<i32>,
pub used_size: Option<BigInt>,
}

#[derive(cynic::QueryFragment, Debug)]
Expand Down
2 changes: 0 additions & 2 deletions lib/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,6 @@ http.workspace = true
is-terminal = "0.4.7"
colored = "2.0"
anyhow = "1.0"

# For the inspect subcommand
bytesize = "1.0"
cfg-if = "1.0"
tempfile = "3.6.0"
Expand Down
4 changes: 2 additions & 2 deletions lib/cli/src/commands/app/volumes/s3_credentials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ endpoint = {endpoint}
app_name = app.name,
access_key = creds.access_key,
secret_key = creds.secret_key,
endpoint = creds.url,
endpoint = creds.endpoint,
);

println!("{}", rclone_config);
} else {
println!("S3 credentials for app {}:\n", app.name);
println!(" S3 URL: {}", creds.url);
println!(" S3 URL: {}", creds.endpoint);
println!(" Access key: {}", creds.access_key);
println!(" Secret key: {}", creds.secret_key);
println!();
Expand Down
23 changes: 15 additions & 8 deletions lib/cli/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ impl CliRender for wasmer_api::types::AppVersionVolume {
vec!["Name".to_string(), self.name.clone()],
vec![
"Used size".to_string(),
format_disk_size_opt(self.used_size),
format_disk_size_opt(self.used_size.clone()),
],
]);
table.to_string()
Expand All @@ -173,18 +173,25 @@ impl CliRender for wasmer_api::types::AppVersionVolume {
fn render_list_table(items: &[Self]) -> String {
let mut table = Table::new();
table.set_header(vec!["Name".to_string(), "Used size".to_string()]);
table.add_rows(
items
.iter()
.map(|vol| vec![vol.name.clone(), format_disk_size_opt(vol.used_size)]),
);
table.add_rows(items.iter().map(|vol| {
vec![
vol.name.clone(),
format_disk_size_opt(vol.used_size.clone()),
]
}));
table.to_string()
}
}

fn format_disk_size_opt(value: Option<i32>) -> String {
fn format_disk_size_opt(value: Option<wasmer_api::types::BigInt>) -> String {
let value = value.and_then(|x| {
let y: Option<u64> = x.0.try_into().ok();
y
});

if let Some(v) = value {
format!("{}Mb", v)
let s = bytesize::ByteSize(v);
s.to_string()
} else {
"n/a".to_string()
}
Expand Down
Loading