1
1
package libvirt
2
2
3
3
import (
4
+ "bytes"
4
5
"errors"
5
6
"fmt"
7
+ "io"
6
8
"log"
7
9
"net"
8
10
"net/url"
11
+ "os"
12
+ "os/exec"
13
+ "runtime"
9
14
"sort"
10
15
"strconv"
11
16
"strings"
@@ -230,7 +235,143 @@ func newDiskForCloudInit(virConn *libvirt.Connect, volumeKey string) (libvirtxml
230
235
return disk , nil
231
236
}
232
237
233
- func setCoreOSIgnition (d * schema.ResourceData , domainDef * libvirtxml.Domain ) error {
238
+ func getFirstDiskPath (d * schema.ResourceData , domainDef * libvirtxml.Domain , virConn * libvirt.Connect ) (string , error ) {
239
+ prefix := fmt .Sprintf ("disk.%d" , 0 )
240
+
241
+ if volumeKey , ok := d .GetOk (prefix + ".volume_id" ); ok {
242
+ diskVolume , err := virConn .LookupStorageVolByKey (volumeKey .(string ))
243
+ if err != nil {
244
+ return "" , fmt .Errorf ("Error lookup storage disk volume: %s" , err )
245
+ }
246
+ diskVolumeFile , err := diskVolume .GetPath ()
247
+ if err != nil {
248
+ return "" , fmt .Errorf ("Error get path of disk volume: %s" , err )
249
+ }
250
+ return diskVolumeFile , nil
251
+ }
252
+ return "" , fmt .Errorf ("failed to find disk 0" )
253
+ }
254
+
255
+ func getStderr (stderr io.ReadCloser ) string {
256
+ buf := new (bytes.Buffer )
257
+ buf .ReadFrom (stderr )
258
+ return buf .String ()
259
+ }
260
+
261
+ func inlineUpdateCoreOsIgnition (d * schema.ResourceData , domainDef * libvirtxml.Domain , virConn * libvirt.Connect ) error {
262
+ if ignition , ok := d .GetOk ("coreos_ignition" ); ok {
263
+
264
+ // Get the root disk (first disk) volume location
265
+ rootPath , err := getFirstDiskPath (d , domainDef , virConn )
266
+ if err != nil {
267
+ return err
268
+ }
269
+
270
+ // Get the ingition file to be uploaded
271
+ ignitionKey , err := getIgnitionVolumeKeyFromTerraformID (ignition .(string ))
272
+ if err != nil {
273
+ return err
274
+ }
275
+ diskVolume , err := virConn .LookupStorageVolByKey (ignitionKey )
276
+ if err != nil {
277
+ return err
278
+ }
279
+ fileToUpload , err := diskVolume .GetPath ()
280
+ if err != nil {
281
+ return err
282
+ }
283
+ err = guestfishExecution (rootPath , fileToUpload )
284
+ if err != nil {
285
+ return err
286
+ }
287
+ }
288
+ return nil
289
+ }
290
+
291
+ var execCommand = exec .Command
292
+
293
+ func guestfishExecution (rootPath string , fileToUpload string ) error {
294
+ // This is the file in coreos image going to be copied from local
295
+ fileRemoteLocation := "/ignition/config.ign"
296
+
297
+ // eval $(guestfish --listen -a $1)
298
+ cmd := execCommand ("guestfish" , "--listen" , "-a" , rootPath )
299
+ stdout , err := cmd .StdoutPipe ()
300
+ if err != nil {
301
+ return fmt .Errorf ("Get stdout failed: %v" , err )
302
+ }
303
+ stderr , err := cmd .StderrPipe ()
304
+ if err != nil {
305
+ return fmt .Errorf ("Get stderr failed: %v" , err )
306
+ }
307
+
308
+ err = cmd .Start ()
309
+ if err != nil {
310
+ return fmt .Errorf ("Start command failed: %v" , err )
311
+ }
312
+
313
+ // if the command start successfully, the output will be following format
314
+ // GUESTFISH_PID=xxxx; export GUESTFISH_PID, so we need get the PID and set by using os.Setenv
315
+ buf := new (bytes.Buffer )
316
+ buf .ReadFrom (stdout )
317
+ guestfishPidLabel := strings .Split (buf .String (), ";" )
318
+ guestfishPidValue := strings .Split (guestfishPidLabel [0 ], "=" )
319
+ os .Setenv (guestfishPidValue [0 ], guestfishPidValue [1 ])
320
+
321
+ // guestfish --remote -- run
322
+ cmd = execCommand ("guestfish" , "--remote" , "--" , "run" )
323
+ err = cmd .Run ()
324
+ if err != nil {
325
+ return fmt .Errorf ("Launch failed: %v, %s" , err , getStderr (stderr ))
326
+ }
327
+
328
+ // guestfish --remote -- exit
329
+ defer func () {
330
+ cmd := execCommand ("guestfish" , "--remote" , "--" , "exit" )
331
+ cmd .Run ()
332
+ }()
333
+
334
+ // guestfish --remote -- findfs-label
335
+ cmd = execCommand ("guestfish" , "--remote" , "--" , "findfs-label" , "boot" )
336
+ output , err := cmd .Output ()
337
+
338
+ if err != nil {
339
+ return fmt .Errorf ("Failed to find fs label: %v" , err )
340
+ }
341
+
342
+ bootDisk := strings .TrimSpace (string (output ))
343
+ if len (bootDisk ) == 0 {
344
+ return fmt .Errorf ("failed to get the boot filesystem" )
345
+ }
346
+
347
+ // guestfish --remote -- mount $2 /
348
+ cmd = execCommand ("guestfish" , "--remote" , "--" , "mount" , bootDisk , "/" )
349
+ err = cmd .Run ()
350
+ if err != nil {
351
+ return fmt .Errorf ("Mount failed: %v, %s" , err , getStderr (stderr ))
352
+ }
353
+
354
+ // This is the real command that upload the file from current location (the local file system)
355
+ // to remote file location (the coreos file system)
356
+ // guestfish --remote -- upload $4 $3/$4
357
+ cmd = execCommand ("guestfish" , "--remote" , "--" , "upload" , fileToUpload , fileRemoteLocation )
358
+ err = cmd .Run ()
359
+ if err != nil {
360
+ return fmt .Errorf ("Upload failed: %v, %s" , err , getStderr (stderr ))
361
+ }
362
+
363
+ // guestfish --remote -- umount-all
364
+ cmd = execCommand ("guestfish" , "--remote" , "--" , "umount-all" )
365
+ err = cmd .Run ()
366
+ if err != nil {
367
+ return fmt .Errorf ("Unmount failed: %v, %s" , err , getStderr (stderr ))
368
+ }
369
+
370
+ return nil
371
+ }
372
+
373
+ // Use fw_cfg device to update coresos
374
+ func fwcfgUpdateCoreOSIgnition (d * schema.ResourceData , domainDef * libvirtxml.Domain ) error {
234
375
if ignition , ok := d .GetOk ("coreos_ignition" ); ok {
235
376
ignitionKey , err := getIgnitionVolumeKeyFromTerraformID (ignition .(string ))
236
377
if err != nil {
@@ -252,6 +393,15 @@ func setCoreOSIgnition(d *schema.ResourceData, domainDef *libvirtxml.Domain) err
252
393
return nil
253
394
}
254
395
396
+ func updateCoreOSIgnition (d * schema.ResourceData , domainDef * libvirtxml.Domain , virConn * libvirt.Connect ) error {
397
+ if runtime .GOARCH == "s390x" || runtime .GOARCH == "s390" {
398
+ // There is no support for fw_cfg in s390x, so instead of doing fw_cfg
399
+ // we have to inline update the coreos ignition file
400
+ return inlineUpdateCoreOsIgnition (d , domainDef , virConn )
401
+ }
402
+ return fwcfgUpdateCoreOSIgnition (d , domainDef )
403
+ }
404
+
255
405
func setVideo (d * schema.ResourceData , domainDef * libvirtxml.Domain ) error {
256
406
prefix := "video.0"
257
407
if _ , ok := d .GetOk (prefix ); ok {
0 commit comments