Skip to content
Closed
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
7 changes: 7 additions & 0 deletions src/api/data_types/snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ pub struct CreateSnapshotResponse {
pub image_count: u64,
}

/// Response from the preprod retention endpoint.
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PreprodRetentionResponse {
pub snapshots: u64,
}

// Keep in sync with https://github.com/getsentry/sentry/blob/master/src/sentry/preprod/snapshots/manifest.py
/// Manifest describing a set of snapshot images for an app.
#[derive(Debug, Serialize)]
Expand Down
7 changes: 6 additions & 1 deletion src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,12 @@ impl AuthenticatedApi<'_> {
);
self.get(&path)?.convert()
}

/// Fetches the preprod retention policy for an organization.
pub fn fetch_preprod_retention(&self, org: &str) -> ApiResult<PreprodRetentionResponse> {
let path = format!("/organizations/{}/preprod/retention/", PathArg(org),);
self.get(&path)?.convert()
}
}

/// Available datasets for fetching organization events
Expand Down Expand Up @@ -2035,5 +2041,4 @@ pub struct SnapshotsUploadOptions {
pub struct ObjectstoreUploadOptions {
pub url: String,
pub scopes: Vec<(String, String)>,
pub expiration_policy: String,
}
15 changes: 11 additions & 4 deletions src/commands/build/snapshots.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use std::str::FromStr as _;
use std::time::Duration;

use anyhow::{Context as _, Result};
use clap::{Arg, ArgMatches, Command};
Expand Down Expand Up @@ -88,14 +88,20 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
if images.len() == 1 { "file" } else { "files" }
);

// Fetch retention policy for snapshot uploads
let api = Api::current();
let authenticated_api = api.authenticated()?;
let retention = authenticated_api.fetch_preprod_retention(&org)?;
debug!("Snapshot retention: {} days", retention.snapshots);

// Upload image files to objectstore
println!(
"{} Uploading {} image {}",
style(">").dim(),
style(images.len()).yellow(),
if images.len() == 1 { "file" } else { "files" }
);
let manifest_entries = upload_images(images, &org, &project)?;
let manifest_entries = upload_images(images, &org, &project, retention.snapshots)?;

// Build manifest from discovered images
let manifest = SnapshotsManifest {
Expand Down Expand Up @@ -192,13 +198,14 @@ fn upload_images(
images: Vec<ImageInfo>,
org: &str,
project: &str,
retention_days: u64,
) -> Result<HashMap<String, ImageMetadata>> {
let api = Api::current();
let authenticated_api = api.authenticated()?;
let options = authenticated_api.fetch_snapshots_upload_options(org, project)?;

let expiration = ExpirationPolicy::from_str(&options.objectstore.expiration_policy)
.context("Failed to parse expiration policy from upload options")?;
let expiration =
ExpirationPolicy::TimeToLive(Duration::from_secs(retention_days * 24 * 60 * 60));

let client = ClientBuilder::new(options.objectstore.url)
.token({
Expand Down