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

Use less IDs in paths #117

Merged
merged 3 commits into from
Apr 19, 2022
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
84 changes: 19 additions & 65 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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"))?;

Expand All @@ -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"))?;

Expand Down Expand Up @@ -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"))?;
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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>(());
Expand Down Expand Up @@ -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?
}
}

Expand Down
25 changes: 5 additions & 20 deletions client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,37 +109,25 @@ impl Client {
&self,
request: &api::InstanceEnsureRequest,
) -> Result<api::InstanceEnsureResponse, Error> {
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
}

/// Returns information about an instance, by UUID.
pub async fn instance_get(
&self,
id: Uuid,
) -> Result<api::InstanceGetResponse, Error> {
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<Uuid, Error> {
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<api::InstanceStateMonitorResponse, Error> {
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(),
Expand All @@ -150,22 +138,19 @@ 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
}

/// Get the status of an ongoing migration
pub async fn instance_migrate_status(
&self,
id: Uuid,
migration_id: Uuid,
) -> Result<api::InstanceMigrateStatusResponse, Error> {
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,
Expand Down
12 changes: 1 addition & 11 deletions server/src/lib/migrate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,6 @@ struct Device {
/// connection and begin the migration in a separate task.
pub async fn source_start(
rqctx: Arc<RequestContext<Context>>,
instance_id: Uuid,
migration_id: Uuid,
) -> Result<Response<Body>, MigrateError> {
// Create a new log context for the migration
Expand All @@ -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(),
Expand Down Expand Up @@ -359,7 +354,6 @@ pub async fn source_start(
/// process (destination-side).
pub async fn dest_initiate(
rqctx: Arc<RequestContext<Context>>,
instance_id: Uuid,
migrate_info: api::InstanceMigrateInitiateRequest,
) -> Result<api::InstanceMigrateInitiateResponse, MigrateError> {
let migration_id = migrate_info.migration_id;
Expand All @@ -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
Expand All @@ -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);
Expand Down
Loading