Skip to content

Commit

Permalink
Remove URL based image creation and import
Browse files Browse the repository at this point in the history
Remove both params::ImageSource::Url and params::ImportBlocksFromUrl
(along with associated HTTP endpoint): do not allow customers to create
an image from a URL, that was for development purposes only. Now that
Nexus supports importing blocks via the Pantry this is no longer
required.

Closes #2893
  • Loading branch information
jmpesp committed Dec 5, 2023
1 parent 7dca6fc commit cf90d82
Show file tree
Hide file tree
Showing 15 changed files with 36 additions and 1,165 deletions.
8 changes: 2 additions & 6 deletions end-to-end-tests/src/instance_launch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,8 @@ async fn instance_launch() -> Result<()> {
description: String::new(),
os: "debian".try_into().map_err(anyhow::Error::msg)?,
version: "propolis-blob".into(),
source: ImageSource::Url {
url:
"http://[fd00:1122:3344:101::1]:54321/debian-11-genericcloud-amd64.raw"
.into(),
block_size: 512.try_into().map_err(anyhow::Error::msg)?,
},
/// XXX this won't work, it needs cloud-init
source: ImageSource::YouCanBootAnythingAsLongAsItsAlpine,
})
.send()
.await?
Expand Down
26 changes: 0 additions & 26 deletions nexus/src/app/disk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,32 +369,6 @@ impl super::Nexus {
Ok(())
}

/// Import blocks from a URL into a disk
pub(crate) async fn import_blocks_from_url_for_disk(
self: &Arc<Self>,
opctx: &OpContext,
disk_lookup: &lookup::Disk<'_>,
params: params::ImportBlocksFromUrl,
) -> UpdateResult<()> {
let authz_disk: authz::Disk;

(.., authz_disk) =
disk_lookup.lookup_for(authz::Action::Modify).await?;

let saga_params = sagas::import_blocks_from_url::Params {
serialized_authn: authn::saga::Serialized::for_opctx(opctx),
disk_id: authz_disk.id(),

import_params: params.clone(),
};

self
.execute_saga::<sagas::import_blocks_from_url::SagaImportBlocksFromUrl>(saga_params)
.await?;

Ok(())
}

/// Move a disk from the "ImportReady" state to the "Importing" state,
/// blocking any import from URL jobs.
pub(crate) async fn disk_manual_import_start(
Expand Down
116 changes: 0 additions & 116 deletions nexus/src/app/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ use omicron_common::api::external::ListResultVec;
use omicron_common::api::external::LookupResult;
use omicron_common::api::external::NameOrId;
use omicron_common::api::external::UpdateResult;
use std::str::FromStr;
use std::sync::Arc;
use uuid::Uuid;

Expand Down Expand Up @@ -96,121 +95,6 @@ impl super::Nexus {
}
};
let new_image = match &params.source {
params::ImageSource::Url { url, block_size } => {
let db_block_size = db::model::BlockSize::try_from(*block_size)
.map_err(|e| Error::InvalidValue {
label: String::from("block_size"),
message: format!("block_size is invalid: {}", e),
})?;

let image_id = Uuid::new_v4();

let volume_construction_request =
sled_agent_client::types::VolumeConstructionRequest::Url {
id: image_id,
block_size: db_block_size.to_bytes().into(),
url: url.clone(),
};

let volume_data =
serde_json::to_string(&volume_construction_request)?;

// use reqwest to query url for size
let dur = std::time::Duration::from_secs(5);
let client = reqwest::ClientBuilder::new()
.connect_timeout(dur)
.timeout(dur)
.build()
.map_err(|e| {
Error::internal_error(&format!(
"failed to build reqwest client: {}",
e
))
})?;

let response = client.head(url).send().await.map_err(|e| {
Error::InvalidValue {
label: String::from("url"),
message: format!("error querying url: {}", e),
}
})?;

if !response.status().is_success() {
return Err(Error::InvalidValue {
label: String::from("url"),
message: format!(
"querying url returned: {}",
response.status()
),
});
}

// grab total size from content length
let content_length = response
.headers()
.get(reqwest::header::CONTENT_LENGTH)
.ok_or("no content length!")
.map_err(|e| Error::InvalidValue {
label: String::from("url"),
message: format!("error querying url: {}", e),
})?;

let total_size =
u64::from_str(content_length.to_str().map_err(|e| {
Error::InvalidValue {
label: String::from("url"),
message: format!("content length invalid: {}", e),
}
})?)
.map_err(|e| {
Error::InvalidValue {
label: String::from("url"),
message: format!("content length invalid: {}", e),
}
})?;

let size: external::ByteCount = total_size.try_into().map_err(
|e: external::ByteCountRangeError| Error::InvalidValue {
label: String::from("size"),
message: format!("total size is invalid: {}", e),
},
)?;

// validate total size is divisible by block size
let block_size: u64 = (*block_size).into();
if (size.to_bytes() % block_size) != 0 {
return Err(Error::InvalidValue {
label: String::from("size"),
message: format!(
"total size {} must be divisible by block size {}",
size.to_bytes(),
block_size
),
});
}

let new_image_volume =
db::model::Volume::new(Uuid::new_v4(), volume_data);
let volume =
self.db_datastore.volume_create(new_image_volume).await?;

db::model::Image {
identity: db::model::ImageIdentity::new(
image_id,
params.identity.clone(),
),
silo_id: authz_silo.id(),
project_id: maybe_authz_project.clone().map(|p| p.id()),
volume_id: volume.id(),
url: Some(url.clone()),
os: params.os.clone(),
version: params.version.clone(),
digest: None, // not computed for URL type
block_size: db_block_size,
size: size.into(),
}
}

params::ImageSource::Snapshot { id } => {
let image_id = Uuid::new_v4();

Expand Down
Loading

0 comments on commit cf90d82

Please sign in to comment.