@@ -7,6 +7,7 @@ package multipass
7
7
import (
8
8
"bytes"
9
9
"context"
10
+ "encoding/json"
10
11
"fmt"
11
12
"os"
12
13
"os/exec"
@@ -122,6 +123,12 @@ func (p *provisioner) Clean(ctx context.Context, _ runner.Config, instances []ru
122
123
123
124
// launch creates an instance.
124
125
func (p * provisioner ) launch (ctx context.Context , cfg runner.Config , batch runner.OSBatch ) error {
126
+ // check if instance already exists
127
+ err := p .ensureInstanceNotExist (ctx , batch )
128
+ if err != nil {
129
+ p .logger .Logf (
130
+ "could not check multipass instance %q does not exists, moving on anyway. Err: %v" , err )
131
+ }
125
132
args := []string {
126
133
"launch" ,
127
134
"-c" , "2" ,
@@ -145,9 +152,14 @@ func (p *provisioner) launch(ctx context.Context, cfg runner.Config, batch runne
145
152
return fmt .Errorf ("failed to marshal cloud-init configuration: %w" , err )
146
153
}
147
154
155
+ p .logger .Logf ("Launching multipass instance %s" , batch .ID )
148
156
var output bytes.Buffer
149
- p .logger .Logf ("Launching multipass image %s" , batch .ID )
150
- proc , err := process .Start ("multipass" , process .WithContext (ctx ), process .WithArgs (args ), process .WithCmdOptions (runner .AttachOut (& output ), runner .AttachErr (& output )))
157
+ proc , err := process .Start ("multipass" ,
158
+ process .WithContext (ctx ),
159
+ process .WithArgs (args ),
160
+ process .WithCmdOptions (
161
+ runner .AttachOut (& output ),
162
+ runner .AttachErr (& output )))
151
163
if err != nil {
152
164
return fmt .Errorf ("failed to run multipass launch: %w" , err )
153
165
}
@@ -162,14 +174,84 @@ func (p *provisioner) launch(ctx context.Context, cfg runner.Config, batch runne
162
174
}
163
175
_ = proc .Stdin .Close ()
164
176
ps := <- proc .Wait ()
165
- if ps .ExitCode () != 0 {
177
+ if ! ps .Success () {
166
178
// print the output so its clear what went wrong
167
179
fmt .Fprintf (os .Stdout , "%s\n " , output .Bytes ())
168
180
return fmt .Errorf ("failed to run multipass launch: exited with code: %d" , ps .ExitCode ())
169
181
}
170
182
return nil
171
183
}
172
184
185
+ func (p * provisioner ) ensureInstanceNotExist (ctx context.Context , batch runner.OSBatch ) error {
186
+ var output bytes.Buffer
187
+ var stdErr bytes.Buffer
188
+ proc , err := process .Start ("multipass" ,
189
+ process .WithContext (ctx ),
190
+ process .WithArgs ([]string {"list" , "--format" , "json" }),
191
+ process .WithCmdOptions (
192
+ runner .AttachOut (& output ),
193
+ runner .AttachErr (& stdErr )))
194
+ if err != nil {
195
+ return fmt .Errorf ("multipass list failed to run: %w" , err )
196
+ }
197
+
198
+ state := <- proc .Wait ()
199
+ if ! state .Success () {
200
+ msg := fmt .Sprintf ("multipass list exited with non-zero status: %s" ,
201
+ state .String ())
202
+ p .logger .Logf (msg )
203
+ p .logger .Logf ("output: %s" , output .String ())
204
+ p .logger .Logf ("stderr: %s" , stdErr .String ())
205
+ return fmt .Errorf (msg )
206
+ }
207
+ list := struct {
208
+ List []struct {
209
+ Ipv4 []string `json:"ipv4"`
210
+ Name string `json:"name"`
211
+ Release string `json:"release"`
212
+ State string `json:"state"`
213
+ } `json:"list"`
214
+ }{}
215
+ err = json .NewDecoder (& output ).Decode (& list )
216
+ if err != nil {
217
+ return fmt .Errorf ("could not decode mutipass list output: %w" , err )
218
+ }
219
+
220
+ for _ , i := range list .List {
221
+ if i .Name == batch .ID {
222
+ p .logger .Logf ("multipass trying to delete instance %s" , batch .ID )
223
+
224
+ output .Reset ()
225
+ stdErr .Reset ()
226
+ proc , err = process .Start ("multipass" ,
227
+ process .WithContext (ctx ),
228
+ process .WithArgs ([]string {"delete" , "--purge" , batch .ID }),
229
+ process .WithCmdOptions (
230
+ runner .AttachOut (& output ),
231
+ runner .AttachErr (& stdErr )))
232
+ if err != nil {
233
+ return fmt .Errorf (
234
+ "multipass instance %q already exist, state %q. Could not delete it: %w" ,
235
+ batch .ID , i .State , err )
236
+ }
237
+ state = <- proc .Wait ()
238
+ if ! state .Success () {
239
+ msg := fmt .Sprintf ("failed to delete and purge multipass instance %s: %s" ,
240
+ batch .ID ,
241
+ state .String ())
242
+ p .logger .Logf (msg )
243
+ p .logger .Logf ("output: %s" , output .String ())
244
+ p .logger .Logf ("stderr: %s" , stdErr .String ())
245
+ return fmt .Errorf (msg )
246
+ }
247
+
248
+ break
249
+ }
250
+ }
251
+
252
+ return nil
253
+ }
254
+
173
255
// delete deletes an instance.
174
256
func (p * provisioner ) delete (ctx context.Context , instance runner.Instance ) error {
175
257
args := []string {
0 commit comments