@@ -112,6 +112,7 @@ type startVMMock struct {
112
112
create func (* proxmox.VmRef , proxmox.ConfigQemu , multistep.StateBag ) error
113
113
startVm func (* proxmox.VmRef ) (string , error )
114
114
setVmConfig func (* proxmox.VmRef , map [string ]interface {}) (interface {}, error )
115
+ getNextID func (id int ) (int , error )
115
116
}
116
117
117
118
func (m * startVMMock ) Create (vmRef * proxmox.VmRef , config proxmox.ConfigQemu , state multistep.StateBag ) error {
@@ -123,8 +124,8 @@ func (m *startVMMock) StartVm(vmRef *proxmox.VmRef) (string, error) {
123
124
func (m * startVMMock ) SetVmConfig (vmRef * proxmox.VmRef , config map [string ]interface {}) (interface {}, error ) {
124
125
return m .setVmConfig (vmRef , config )
125
126
}
126
- func (m * startVMMock ) GetNextID (int ) (int , error ) {
127
- return 1 , nil
127
+ func (m * startVMMock ) GetNextID (id int ) (int , error ) {
128
+ return m . getNextID ( id )
128
129
}
129
130
130
131
func TestStartVM (t * testing.T ) {
@@ -170,6 +171,96 @@ func TestStartVM(t *testing.T) {
170
171
setVmConfig : func (* proxmox.VmRef , map [string ]interface {}) (interface {}, error ) {
171
172
return nil , nil
172
173
},
174
+ getNextID : func (id int ) (int , error ) {
175
+ return 1 , nil
176
+ },
177
+ }
178
+ state := new (multistep.BasicStateBag )
179
+ state .Put ("ui" , packersdk .TestUi (t ))
180
+ state .Put ("config" , c .config )
181
+ state .Put ("proxmoxClient" , mock )
182
+ s := stepStartVM {vmCreator : mock }
183
+
184
+ action := s .Run (context .TODO (), state )
185
+ if action != c .expectedAction {
186
+ t .Errorf ("Expected action %s, got %s" , c .expectedAction , action )
187
+ }
188
+ })
189
+ }
190
+ }
191
+
192
+ func TestStartVMRetryOnDuplicateID (t * testing.T ) {
193
+ newDuplicateError := func (id int ) error {
194
+ return fmt .Errorf ("unable to create VM %d - VM %d already exists on node 'test'" , id , id )
195
+ }
196
+ cs := []struct {
197
+ name string
198
+ config * Config
199
+ createErrorGenerator func (id int ) error
200
+ expectedCallsToCreate int
201
+ expectedAction multistep.StepAction
202
+ }{
203
+ {
204
+ name : "Succeed immediately if non-duplicate" ,
205
+ config : & Config {},
206
+ createErrorGenerator : func (id int ) error { return nil },
207
+ expectedCallsToCreate : 1 ,
208
+ expectedAction : multistep .ActionContinue ,
209
+ },
210
+ {
211
+ name : "Fail immediately if duplicate and VMID explicitly configured" ,
212
+ config : & Config {VMID : 1 },
213
+ createErrorGenerator : func (id int ) error { return newDuplicateError (id ) },
214
+ expectedCallsToCreate : 1 ,
215
+ expectedAction : multistep .ActionHalt ,
216
+ },
217
+ {
218
+ name : "Fail immediately if error not caused by duplicate ID" ,
219
+ config : & Config {},
220
+ createErrorGenerator : func (id int ) error { return fmt .Errorf ("Something else went wrong" ) },
221
+ expectedCallsToCreate : 1 ,
222
+ expectedAction : multistep .ActionHalt ,
223
+ },
224
+ {
225
+ name : "Retry if error caused by duplicate ID" ,
226
+ config : & Config {},
227
+ createErrorGenerator : func (id int ) error {
228
+ if id < 2 {
229
+ return newDuplicateError (id )
230
+ }
231
+ return nil
232
+ },
233
+ expectedCallsToCreate : 2 ,
234
+ expectedAction : multistep .ActionContinue ,
235
+ },
236
+ {
237
+ name : "Retry only up to maxDuplicateIDRetries times" ,
238
+ config : & Config {},
239
+ createErrorGenerator : func (id int ) error {
240
+ return newDuplicateError (id )
241
+ },
242
+ expectedCallsToCreate : maxDuplicateIDRetries ,
243
+ expectedAction : multistep .ActionHalt ,
244
+ },
245
+ }
246
+
247
+ for _ , c := range cs {
248
+ t .Run (c .name , func (t * testing.T ) {
249
+ createCalls := 0
250
+ mock := & startVMMock {
251
+ create : func (vmRef * proxmox.VmRef , config proxmox.ConfigQemu , state multistep.StateBag ) error {
252
+ createCalls ++
253
+ return c .createErrorGenerator (vmRef .VmId ())
254
+ },
255
+ startVm : func (* proxmox.VmRef ) (string , error ) {
256
+ return "" , nil
257
+ },
258
+ setVmConfig : func (* proxmox.VmRef , map [string ]interface {}) (interface {}, error ) {
259
+ return nil , nil
260
+ },
261
+ getNextID : func (id int ) (int , error ) {
262
+ return createCalls + 1 , nil
263
+ },
173
264
}
174
265
state := new (multistep.BasicStateBag )
175
266
state .Put ("ui" , packersdk .TestUi (t ))
@@ -181,6 +272,9 @@ func TestStartVM(t *testing.T) {
181
272
if action != c .expectedAction {
182
273
t .Errorf ("Expected action %s, got %s" , c .expectedAction , action )
183
274
}
275
+ if createCalls != c .expectedCallsToCreate {
276
+ t .Errorf ("Expected %d calls to create, got %d" , c .expectedCallsToCreate , createCalls )
277
+ }
184
278
})
185
279
}
186
280
}
0 commit comments