@@ -448,9 +448,33 @@ export const handlers = makeHandlers({
448448 } )
449449 }
450450
451- /////////////////////////////////////////////////////////////
452- // DB write stuff starts here
453- /////////////////////////////////////////////////////////////
451+ // validate floating IP attachments before we actually do anything
452+ body . external_ips ?. forEach ( ( ip ) => {
453+ if ( ip . type === 'floating' ) {
454+ // throw if floating IP doesn't exist
455+ const floatingIp = lookup . floatingIp ( {
456+ project : project . id ,
457+ floatingIp : ip . floating_ip ,
458+ } )
459+ if ( floatingIp . instance_id ) {
460+ throw 'floating IP cannot be attached to one instance while still attached to another'
461+ }
462+ } else {
463+ // just make sure we can get one. technically this will only throw
464+ // if there are no ranges in the pool or if the pool doesn't exist,
465+ // which aren't quite as good as checking that there are actually IPs
466+ // available, but they are good things to check
467+ getIpFromPool ( ip . pool )
468+ }
469+ } )
470+
471+ //////////////////////////////////////////////////////////////////////////
472+ // DB WRITES START HERE
473+ //
474+ // We don't have transactions or sagas, so we need to make sure we do all
475+ // our validation and throw any errors about bad input before we make any
476+ // changes to the DB that would have to be undone on failure.
477+ //////////////////////////////////////////////////////////////////////////
454478
455479 for ( const diskParams of allDisks ) {
456480 if ( diskParams . type === 'create' ) {
@@ -515,26 +539,15 @@ export const handlers = makeHandlers({
515539 )
516540 }
517541
518- const newInstance : Json < Api . Instance > = {
519- id : instanceId ,
520- project_id : project . id ,
521- ...R . pick ( body , [ 'name' , 'description' , 'hostname' , 'memory' , 'ncpus' ] ) ,
522- ...getTimestamps ( ) ,
523- run_state : 'creating' ,
524- time_run_state_updated : new Date ( ) . toISOString ( ) ,
525- boot_disk_id : bootDiskId ,
526- auto_restart_enabled : true ,
527- }
528-
542+ // actually set up IPs. looks very similar to validation step but this time
543+ // we are writing to the DB
529544 body . external_ips ?. forEach ( ( ip ) => {
530545 if ( ip . type === 'floating' ) {
531546 const floatingIp = lookup . floatingIp ( {
532547 project : project . id ,
533548 floatingIp : ip . floating_ip ,
534549 } )
535- if ( floatingIp . instance_id ) {
536- throw 'floating IP cannot be attached to one instance while still attached to another'
537- }
550+ // we've already validated that the IP isn't attached
538551 floatingIp . instance_id = instanceId
539552 } else if ( ip . type === 'ephemeral' ) {
540553 const firstAvailableAddress = getIpFromPool ( ip . pool )
@@ -548,6 +561,17 @@ export const handlers = makeHandlers({
548561 }
549562 } )
550563
564+ const newInstance : Json < Api . Instance > = {
565+ id : instanceId ,
566+ project_id : project . id ,
567+ ...R . pick ( body , [ 'name' , 'description' , 'hostname' , 'memory' , 'ncpus' ] ) ,
568+ ...getTimestamps ( ) ,
569+ run_state : 'creating' ,
570+ time_run_state_updated : new Date ( ) . toISOString ( ) ,
571+ boot_disk_id : bootDiskId ,
572+ auto_restart_enabled : true ,
573+ }
574+
551575 setTimeout ( ( ) => {
552576 newInstance . run_state = 'starting'
553577 } , 500 )
0 commit comments