@@ -37,7 +37,10 @@ use internal_dns_types::names::ServiceName;
3737pub use nexus_client:: Client as NexusClient ;
3838use oximeter:: types:: ProducerRegistry ;
3939use propolis_api_types as api;
40- use propolis_api_types:: instance_spec:: SpecKey ;
40+ use propolis_api_types:: instance_spec:: {
41+ v0:: InstanceSpecGetResponseV0 , SpecKey ,
42+ } ;
43+ use propolis_api_types:: v0:: InstanceInitializationMethodV0 ;
4144use propolis_api_types:: InstanceInitializationMethod ;
4245use propolis_server_api:: PropolisServerApi ;
4346use rfb:: tungstenite:: BinaryWs ;
@@ -190,7 +193,15 @@ async fn find_local_nexus_client(
190193 }
191194}
192195
193- async fn instance_get_common (
196+ // DEPRECATED
197+ async fn v0_instance_get (
198+ rqctx : & RequestContext < Arc < DropshotEndpointContext > > ,
199+ ) -> Result < InstanceSpecGetResponseV0 , HttpError > {
200+ let ctx = rqctx. context ( ) ;
201+ ctx. vm . v0_get ( ) . await . ok_or_else ( not_created_error)
202+ }
203+
204+ async fn instance_get (
194205 rqctx : & RequestContext < Arc < DropshotEndpointContext > > ,
195206) -> Result < api:: InstanceSpecGetResponse , HttpError > {
196207 let ctx = rqctx. context ( ) ;
@@ -291,16 +302,111 @@ impl PropolisServerApi for PropolisServerImpl {
291302 } )
292303 }
293304
305+ async fn v0_instance_ensure (
306+ rqctx : RequestContext < Self :: Context > ,
307+ request : TypedBody < api:: v0:: InstanceEnsureRequestV0 > ,
308+ ) -> Result < HttpResponseCreated < api:: InstanceEnsureResponse > , HttpError >
309+ {
310+ let server_context = rqctx. context ( ) ;
311+ let api:: v0:: InstanceEnsureRequestV0 { properties, init } =
312+ request. into_inner ( ) ;
313+ let oximeter_registry = server_context
314+ . static_config
315+ . metrics
316+ . as_ref ( )
317+ . map ( |_| ProducerRegistry :: with_id ( properties. id ) ) ;
318+
319+ let nexus_client =
320+ find_local_nexus_client ( rqctx. server . local_addr , rqctx. log . clone ( ) )
321+ . await ;
322+
323+ let ensure_options = crate :: vm:: EnsureOptions {
324+ bootrom_path : server_context. static_config . bootrom_path . clone ( ) ,
325+ bootrom_version : server_context
326+ . static_config
327+ . bootrom_version
328+ . clone ( ) ,
329+ use_reservoir : server_context. static_config . use_reservoir ,
330+ metrics_config : server_context. static_config . metrics . clone ( ) ,
331+ oximeter_registry,
332+ nexus_client,
333+ vnc_server : server_context. vnc_server . clone ( ) ,
334+ local_server_addr : rqctx. server . local_addr ,
335+ } ;
336+
337+ let vm_init = match init {
338+ InstanceInitializationMethodV0 :: Spec { spec } => spec
339+ . try_into ( )
340+ . map ( |s| VmInitializationMethod :: Spec ( Box :: new ( s) ) )
341+ . map_err ( |e| {
342+ if let Some ( s) = e. source ( ) {
343+ format ! ( "{e}: {s}" )
344+ } else {
345+ e. to_string ( )
346+ }
347+ } ) ,
348+ InstanceInitializationMethodV0 :: MigrationTarget {
349+ migration_id,
350+ src_addr,
351+ replace_components,
352+ } => Ok ( VmInitializationMethod :: Migration ( MigrationTargetInfo {
353+ migration_id,
354+ src_addr,
355+ replace_components,
356+ } ) ) ,
357+ }
358+ . map_err ( |e| {
359+ HttpError :: for_bad_request (
360+ None ,
361+ format ! ( "failed to generate internal instance spec: {e}" ) ,
362+ )
363+ } ) ?;
364+
365+ let request = VmEnsureRequest { properties, init : vm_init } ;
366+ server_context
367+ . vm
368+ . ensure ( & server_context. log , request, ensure_options)
369+ . await
370+ . map ( HttpResponseCreated )
371+ . map_err ( |e| match e {
372+ VmError :: ResultChannelClosed => HttpError :: for_internal_error (
373+ "state driver unexpectedly dropped result channel"
374+ . to_string ( ) ,
375+ ) ,
376+ VmError :: WaitingToInitialize
377+ | VmError :: AlreadyInitialized
378+ | VmError :: RundownInProgress => HttpError :: for_client_error (
379+ Some ( api:: ErrorCode :: AlreadyInitialized . to_string ( ) ) ,
380+ ClientErrorStatusCode :: CONFLICT ,
381+ "instance already initialized" . to_string ( ) ,
382+ ) ,
383+ VmError :: InitializationFailed ( e) => {
384+ HttpError :: for_internal_error ( format ! (
385+ "VM initialization failed: {e}"
386+ ) )
387+ }
388+ _ => HttpError :: for_internal_error ( format ! (
389+ "unexpected error from VM controller: {e}"
390+ ) ) ,
391+ } )
392+ }
393+
294394 async fn instance_spec_get (
295395 rqctx : RequestContext < Self :: Context > ,
296396 ) -> Result < HttpResponseOk < api:: InstanceSpecGetResponse > , HttpError > {
297- Ok ( HttpResponseOk ( instance_get_common ( & rqctx) . await ?) )
397+ Ok ( HttpResponseOk ( instance_get ( & rqctx) . await ?) )
398+ }
399+
400+ async fn v0_instance_spec_get (
401+ rqctx : RequestContext < Self :: Context > ,
402+ ) -> Result < HttpResponseOk < InstanceSpecGetResponseV0 > , HttpError > {
403+ Ok ( HttpResponseOk ( v0_instance_get ( & rqctx) . await ?) )
298404 }
299405
300406 async fn instance_get (
301407 rqctx : RequestContext < Self :: Context > ,
302408 ) -> Result < HttpResponseOk < api:: InstanceGetResponse > , HttpError > {
303- instance_get_common ( & rqctx) . await . map ( |full| {
409+ instance_get ( & rqctx) . await . map ( |full| {
304410 HttpResponseOk ( api:: InstanceGetResponse {
305411 instance : api:: Instance {
306412 properties : full. properties ,
0 commit comments