@@ -259,6 +259,14 @@ const ALWAYS_ANSIBLE_ADDITIONAL_ARGS = ['-e', '\'ansible_ssh_common_args="-o Str
259259 *
260260 * The concrete instance type is not known by this class: a per-provider factory is used
261261 * to build each sub-managers.
262+ *
263+ * Each action function (stop, start, configure, provision, etc.) is a wrapper with retry around
264+ * low-level action functions (doStart, doStop, doConfigure, doProvision, etc.).
265+ * - a top-level action like deploy() may rely on other top-level actions like configure()
266+ * or call underlying low-level actions like doConfigure wrapped in a retry().
267+ * They may also container logic like retry pattern, starting instance before configuring, etc.
268+ * - a low-level action like doConfigure() must NOT call another low-level NOR top-level action.
269+ * These functions should only strictly do the required action without any mre logic.
262270 */
263271export class GenericInstanceManager < ST extends InstanceStateV1 > implements InstanceManager {
264272
@@ -295,9 +303,23 @@ export class GenericInstanceManager<ST extends InstanceStateV1> implements Insta
295303 [ currentState . configuration . input . ansible . additionalArgs ] : undefined
296304
297305 await this . addEvent ( InstanceEventEnum . ConfigurationBegin )
306+
307+ // before configuring, check instance is running as it's possible user would call configure() directly without starting the instance first
308+ // start it if needed
309+ const currentStatus = await this . getInstanceStatus ( )
310+ if ( currentStatus . serverStatus !== ServerRunningStatus . Running ) {
311+
312+ this . logger . debug ( `About to run configuration for instance ${ this . name ( ) } but it's not running, starting it first...` )
313+
314+ await this . doWithRetry ( async ( ) => {
315+ await this . doStart ( { wait : true } )
316+ } , 'Pre-configure start' , opts )
317+ }
318+
298319 await this . doWithRetry ( async ( ) => {
299320 await this . doConfigure ( configurationAnsibleAdditionalArgs )
300321 } , 'Configuration' , opts )
322+
301323 await this . addEvent ( InstanceEventEnum . ConfigurationEnd )
302324 }
303325
@@ -331,6 +353,15 @@ export class GenericInstanceManager<ST extends InstanceStateV1> implements Insta
331353
332354 this . logger . debug ( `Starting instance ${ this . name ( ) } ` )
333355
356+ // if deleteInstanceServerOnStop is enabled, instance server may have been deleted on last instance stop
357+ // so we need to re-provision and re-configure instance using specific Ansible args
358+ await this . doWithRetry ( async ( ) => {
359+ if ( this . args . options ?. deleteInstanceServerOnStop ?. enabled ) {
360+ await this . doProvision ( opts )
361+ await this . doConfigure ( this . args . options . deleteInstanceServerOnStop . postStartReconfigurationAnsibleAdditionalArgs )
362+ }
363+ } , 'Pre-start reconfiguration' , opts )
364+
334365 await this . addEvent ( InstanceEventEnum . StartBegin )
335366 await this . doWithRetry ( async ( ) => {
336367 await this . doStart ( opts )
@@ -401,6 +432,7 @@ export class GenericInstanceManager<ST extends InstanceStateV1> implements Insta
401432 const configurator = await this . buildConfigurator ( {
402433 additionalAnsibleArgs : ALWAYS_ANSIBLE_ADDITIONAL_ARGS . concat ( additionalAnsibleArgs ?? [ ] )
403434 } )
435+
404436 const output = await configurator . configure ( )
405437
406438 this . logger . debug ( `Configuration output for instance ${ this . name ( ) } : ${ JSON . stringify ( output ) } ` )
@@ -431,13 +463,6 @@ export class GenericInstanceManager<ST extends InstanceStateV1> implements Insta
431463 */
432464 async doStart ( opts ?: StartOptions ) : Promise < void > {
433465
434- // if deleteInstanceServerOnStop is enabled, instance server may have been deleted on last instance stop
435- // so we need to re-provision and re-configure instance using specific Ansible args
436- if ( this . args . options ?. deleteInstanceServerOnStop ?. enabled ) {
437- await this . doProvision ( opts )
438- await this . doConfigure ( this . args . options . deleteInstanceServerOnStop . postStartReconfigurationAnsibleAdditionalArgs )
439- }
440-
441466 // always start instance as provisioning and configuring may not start the server for all providers
442467 // and startOptions logic is ported by runner
443468 const runner = await this . buildRunner ( )
0 commit comments