diff --git a/cli/src/main.rs b/cli/src/main.rs index 324b07d3c..85bdf649e 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -73,32 +73,20 @@ enum Command { }, /// Get the properties of a propolis instance - Get { - /// Instance name - name: String, - }, + Get, /// Transition the instance to a new state State { - /// Instance name - name: String, - /// The requested state #[structopt(parse(try_from_str = parse_state))] state: InstanceStateRequested, }, /// Drop to a Serial console connected to the instance - Serial { - /// Instance name - name: String, - }, + Serial, /// Migrate instance to new propolis-server Migrate { - /// Instance name - name: String, - /// Destination propolis-server address #[structopt(parse(try_from_str = resolve_host))] dst_server: IpAddr, @@ -193,16 +181,9 @@ async fn new_instance( Ok(()) } -async fn get_instance(client: &Client, name: String) -> anyhow::Result<()> { - // Grab the Instance UUID - let id = client - .instance_get_uuid(&name) - .await - .with_context(|| anyhow!("failed to get instance UUID"))?; - - // Get the rest of the Instance properties +async fn get_instance(client: &Client) -> anyhow::Result<()> { let res = client - .instance_get(id) + .instance_get() .await .with_context(|| anyhow!("failed to get instance properties"))?; @@ -213,17 +194,10 @@ async fn get_instance(client: &Client, name: String) -> anyhow::Result<()> { async fn put_instance( client: &Client, - name: String, state: InstanceStateRequested, ) -> anyhow::Result<()> { - // Grab the Instance UUID - let id = client - .instance_get_uuid(&name) - .await - .with_context(|| anyhow!("failed to get instance UUID"))?; - client - .instance_state_put(id, state) + .instance_state_put(state) .await .with_context(|| anyhow!("failed to set instance state"))?; @@ -342,18 +316,8 @@ async fn test_stdin_to_websockets_task() { assert!(wsrx.recv().await.is_none()); } -async fn serial( - client: &Client, - addr: SocketAddr, - name: String, -) -> anyhow::Result<()> { - // Grab the Instance UUID - let id = client - .instance_get_uuid(&name) - .await - .with_context(|| anyhow!("failed to get instance UUID"))?; - - let path = format!("ws://{}/instances/{}/serial", addr, id); +async fn serial(addr: SocketAddr) -> anyhow::Result<()> { + let path = format!("ws://{}/instance/serial", addr); let (mut ws, _) = tokio_tungstenite::connect_async(path) .await .with_context(|| anyhow!("failed to create serial websocket stream"))?; @@ -417,21 +381,15 @@ async fn serial( async fn migrate_instance( src_client: Client, dst_client: Client, - src_name: String, src_addr: SocketAddr, dst_uuid: Uuid, ) -> anyhow::Result<()> { - // Grab the src instance UUID - let src_uuid = src_client - .instance_get_uuid(&src_name) - .await - .with_context(|| anyhow!("failed to get src instance UUID"))?; - // Grab the instance details let src_instance = src_client - .instance_get(src_uuid) + .instance_get() .await .with_context(|| anyhow!("failed to get src instance properties"))?; + let src_uuid = src_instance.instance.properties.id; let request = InstanceEnsureRequest { properties: InstanceProperties { @@ -452,11 +410,11 @@ async fn migrate_instance( // Get the source instance ready src_client - .instance_state_put(src_uuid, InstanceStateRequested::MigrateStart) + .instance_state_put(InstanceStateRequested::MigrateStart) .await .with_context(|| { - anyhow!("failed to place src instance in migrate start state") - })?; + anyhow!("failed to place src instance in migrate start state") + })?; // Initiate the migration via the destination instance let migration_id = dst_client @@ -475,10 +433,8 @@ async fn migrate_instance( .map(|(role, client, id)| { tokio::spawn(async move { loop { - let state = client - .instance_migrate_status(id, migration_id) - .await? - .state; + let state = + client.instance_migrate_status(migration_id).await?.state; println!("{}({}) migration state={:?}", role, id, state); if state == MigrationState::Finish { return Ok::<_, anyhow::Error>(()); @@ -542,16 +498,14 @@ async fn main() -> anyhow::Result<()> { ) .await? } - Command::Get { name } => get_instance(&client, name).await?, - Command::State { name, state } => { - put_instance(&client, name, state).await? - } - Command::Serial { name } => serial(&client, addr, name).await?, - Command::Migrate { name, dst_server, dst_port, dst_uuid } => { + Command::Get => get_instance(&client).await?, + Command::State { state } => put_instance(&client, state).await?, + Command::Serial => serial(addr).await?, + Command::Migrate { dst_server, dst_port, dst_uuid } => { let dst_addr = SocketAddr::new(dst_server, dst_port); let dst_client = Client::new(dst_addr, log.clone()); let dst_uuid = dst_uuid.unwrap_or_else(Uuid::new_v4); - migrate_instance(client, dst_client, name, addr, dst_uuid).await? + migrate_instance(client, dst_client, addr, dst_uuid).await? } } diff --git a/client/src/lib.rs b/client/src/lib.rs index 2d1d0e40e..42d60f386 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -109,10 +109,7 @@ impl Client { &self, request: &api::InstanceEnsureRequest, ) -> Result { - let path = format!( - "http://{}/instances/{}", - self.address, request.properties.id - ); + let path = format!("http://{}/instance", self.address,); let body = Body::from(serde_json::to_string(&request).unwrap()); self.put(path, Some(body)).await } @@ -120,26 +117,17 @@ impl Client { /// Returns information about an instance, by UUID. pub async fn instance_get( &self, - id: Uuid, ) -> Result { - let path = format!("http://{}/instances/{}", self.address, id); - self.get(path, None).await - } - - /// Gets instance UUID, by name. - pub async fn instance_get_uuid(&self, name: &str) -> Result { - let path = format!("http://{}/instances/{}/uuid", self.address, name); + let path = format!("http://{}/instance", self.address); self.get(path, None).await } /// Long-poll for state changes. pub async fn instance_state_monitor( &self, - id: Uuid, gen: u64, ) -> Result { - let path = - format!("http://{}/instances/{}/state-monitor", self.address, id); + let path = format!("http://{}/instance/state-monitor", self.address); let body = Body::from( serde_json::to_string(&api::InstanceStateMonitorRequest { gen }) .unwrap(), @@ -150,10 +138,9 @@ impl Client { /// Puts an instance into a new state. pub async fn instance_state_put( &self, - id: Uuid, state: api::InstanceStateRequested, ) -> Result<(), Error> { - let path = format!("http://{}/instances/{}/state", self.address, id); + let path = format!("http://{}/instance/state", self.address); let body = Body::from(serde_json::to_string(&state).unwrap()); self.put_no_response(path, Some(body)).await } @@ -161,11 +148,9 @@ impl Client { /// Get the status of an ongoing migration pub async fn instance_migrate_status( &self, - id: Uuid, migration_id: Uuid, ) -> Result { - let path = - format!("http://{}/instances/{}/migrate/status", self.address, id); + let path = format!("http://{}/instance/migrate/status", self.address); let body = Body::from( serde_json::to_string(&api::InstanceMigrateStatusRequest { migration_id, diff --git a/server/src/lib/migrate/mod.rs b/server/src/lib/migrate/mod.rs index 2d16f30b8..0f4c01997 100644 --- a/server/src/lib/migrate/mod.rs +++ b/server/src/lib/migrate/mod.rs @@ -244,7 +244,6 @@ struct Device { /// connection and begin the migration in a separate task. pub async fn source_start( rqctx: Arc>, - instance_id: Uuid, migration_id: Uuid, ) -> Result, MigrateError> { // Create a new log context for the migration @@ -258,10 +257,6 @@ pub async fn source_start( let context = context.as_mut().ok_or_else(|| MigrateError::InstanceNotInitialized)?; - if instance_id != context.properties.id { - return Err(MigrateError::UuidMismatch); - } - // Bail if the instance hasn't been preset to Migrate Start state. if !matches!( context.instance.current_state(), @@ -359,7 +354,6 @@ pub async fn source_start( /// process (destination-side). pub async fn dest_initiate( rqctx: Arc>, - instance_id: Uuid, migrate_info: api::InstanceMigrateInitiateRequest, ) -> Result { let migration_id = migrate_info.migration_id; @@ -376,10 +370,6 @@ pub async fn dest_initiate( let context = context.as_mut().ok_or_else(|| MigrateError::InstanceNotInitialized)?; - if instance_id != context.properties.id { - return Err(MigrateError::UuidMismatch); - } - let mut migrate_task = rqctx.context().migrate_task.lock().await; // This should be a fresh propolis-server @@ -388,7 +378,7 @@ pub async fn dest_initiate( // TODO: https // TODO: We need to make sure the src_addr is a valid target let src_migrate_url = format!( - "http://{}/instances/{}/migrate/start", + "http://{}/instance/{}/migrate/start", migrate_info.src_addr, migrate_info.src_uuid ); info!(log, "Begin migration"; "src_migrate_url" => &src_migrate_url); diff --git a/server/src/lib/server.rs b/server/src/lib/server.rs index f615660db..5b6f6379b 100644 --- a/server/src/lib/server.rs +++ b/server/src/lib/server.rs @@ -3,7 +3,7 @@ use anyhow::Result; use dropshot::{ endpoint, ApiDescription, HttpError, HttpResponseCreated, HttpResponseOk, - HttpResponseUpdatedNoContent, Path, RequestContext, TypedBody, + HttpResponseUpdatedNoContent, RequestContext, TypedBody, }; use futures::future::Fuse; use futures::{FutureExt, SinkExt, StreamExt}; @@ -25,7 +25,6 @@ use tokio_tungstenite::tungstenite::{ self, handshake, protocol::Role, Message, }; use tokio_tungstenite::WebSocketStream; -use uuid::Uuid; use propolis::bhyve_api; use propolis::dispatch::AsyncCtx; @@ -189,28 +188,21 @@ fn slot_to_bdf(slot: api::Slot, ty: SlotType) -> Result { #[endpoint { method = PUT, - path = "/instances/{instance_id}", + path = "/instance", }] async fn instance_ensure( rqctx: Arc>, - path_params: Path, request: TypedBody, ) -> Result, HttpError> { let server_context = rqctx.context(); let request = request.into_inner(); - let instance_id = path_params.into_inner().instance_id; let (properties, nics, disks, cloud_init_bytes) = ( request.properties, request.nics, request.disks, request.cloud_init_bytes, ); - if instance_id != properties.id { - return Err(HttpError::for_internal_error( - "UUID mismatch (path did not match struct)".to_string(), - )); - } // Handle requests to an instance that has already been initialized. let mut context = server_context.context.lock().await; @@ -524,7 +516,7 @@ async fn instance_ensure( let migrate = if let Some(migrate_request) = request.migrate { // This is a migrate request and so we should try to establish a // connection with the source instance. - let res = migrate::dest_initiate(rqctx, instance_id, migrate_request) + let res = migrate::dest_initiate(rqctx, migrate_request) .await .map_err(<_ as Into>::into)?; Some(res) @@ -561,37 +553,10 @@ async fn instance_ensure( #[endpoint { method = GET, - path = "/instances/{instance_id}/uuid", - unpublished = true, -}] -async fn instance_get_uuid( - rqctx: Arc>, - path_params: Path, -) -> Result, HttpError> { - let context = rqctx.context().context.lock().await; - - let context = context.as_ref().ok_or_else(|| { - HttpError::for_internal_error( - "Server not initialized (no instance)".to_string(), - ) - })?; - - if path_params.into_inner().instance_id != context.properties.name { - return Err(HttpError::for_internal_error( - "Instance name mismatch (path did not match struct)".to_string(), - )); - } - - Ok(HttpResponseOk(context.properties.id)) -} - -#[endpoint { - method = GET, - path = "/instances/{instance_id}", + path = "/instance", }] async fn instance_get( rqctx: Arc>, - path_params: Path, ) -> Result, HttpError> { let context = rqctx.context().context.lock().await; @@ -601,11 +566,6 @@ async fn instance_get( ) })?; - if path_params.into_inner().instance_id != context.properties.id { - return Err(HttpError::for_internal_error( - "UUID mismatch (path did not match struct)".to_string(), - )); - } let instance_info = api::Instance { properties: context.properties.clone(), state: propolis_to_api_state(context.instance.current_state()), @@ -627,11 +587,10 @@ async fn instance_get( #[endpoint { method = GET, - path = "/instances/{instance_id}/state-monitor", + path = "/instance/state-monitor", }] async fn instance_state_monitor( rqctx: Arc>, - path_params: Path, request: TypedBody, ) -> Result, HttpError> { let (mut state_watcher, gen) = { @@ -641,12 +600,6 @@ async fn instance_state_monitor( "Server not initialized (no instance)".to_string(), ) })?; - let path_params = path_params.into_inner(); - if path_params.instance_id != context.properties.id { - return Err(HttpError::for_internal_error( - "UUID mismatch (path did not match struct)".to_string(), - )); - } let gen = request.into_inner().gen; let state_watcher = context.state_watcher.clone(); @@ -669,11 +622,10 @@ async fn instance_state_monitor( #[endpoint { method = PUT, - path = "/instances/{instance_id}/state", + path = "/instance/state", }] async fn instance_state_put( rqctx: Arc>, - path_params: Path, request: TypedBody, ) -> Result { let context = rqctx.context().context.lock().await; @@ -684,12 +636,6 @@ async fn instance_state_put( ) })?; - if path_params.into_inner().instance_id != context.properties.id { - return Err(HttpError::for_internal_error( - "UUID mismatch (path did not match struct)".to_string(), - )); - } - let state = api_to_propolis_state(request.into_inner()); context.instance.set_target_state(state).map_err(|err| { HttpError::for_internal_error(format!("Failed to set state: {:?}", err)) @@ -791,11 +737,10 @@ async fn instance_serial_task( #[endpoint { method = GET, - path = "/instances/{instance_id}/serial", + path = "/instance/serial", }] async fn instance_serial( rqctx: Arc>, - path_params: Path, ) -> Result, HttpError> { let mut context = rqctx.context().context.lock().await; @@ -804,11 +749,6 @@ async fn instance_serial( "Server not initialized (no instance)".to_string(), ) })?; - if path_params.into_inner().instance_id != context.properties.id { - return Err(HttpError::for_internal_error( - "UUID mismatch (path did not match struct)".to_string(), - )); - } if context.serial_task.as_ref().map_or(false, |s| s.is_attached()) { return Err(HttpError::for_unavail( None, @@ -923,11 +863,10 @@ async fn instance_serial( #[endpoint { method = PUT, - path = "/instances/{instance_id}/serial/detach", + path = "/instance/serial/detach", }] async fn instance_serial_detach( rqctx: Arc>, - path_params: Path, ) -> Result { let mut context = rqctx.context().context.lock().await; @@ -936,11 +875,6 @@ async fn instance_serial_detach( "Server not initialized (no instance)".to_string(), ) })?; - if path_params.into_inner().instance_id != context.properties.id { - return Err(HttpError::for_internal_error( - "UUID mismatch (path did not match struct)".to_string(), - )); - } let serial_task = context.serial_task.take().filter(|s| s.is_attached()).ok_or_else( @@ -972,28 +906,23 @@ async fn instance_serial_detach( // clients. #[endpoint { method = PUT, - path = "/instances/{instance_id}/migrate/start", + path = "/instance/migrate/start", unpublished = true, }] async fn instance_migrate_start( rqctx: Arc>, - path_params: Path, request: TypedBody, ) -> Result, HttpError> { - let instance_id = path_params.into_inner().instance_id; let migration_id = request.into_inner().migration_id; - migrate::source_start(rqctx, instance_id, migration_id) - .await - .map_err(Into::into) + migrate::source_start(rqctx, migration_id).await.map_err(Into::into) } #[endpoint { method = GET, - path = "/instances/{instance_id}/migrate/status" + path = "/instance/migrate/status" }] async fn instance_migrate_status( rqctx: Arc>, - _path_params: Path, request: TypedBody, ) -> Result, HttpError> { let migration_id = request.into_inner().migration_id; @@ -1007,7 +936,6 @@ async fn instance_migrate_status( pub fn api() -> ApiDescription { let mut api = ApiDescription::new(); api.register(instance_ensure).unwrap(); - api.register(instance_get_uuid).unwrap(); api.register(instance_get).unwrap(); api.register(instance_state_monitor).unwrap(); api.register(instance_state_put).unwrap();