Skip to content

Commit 05f2d9e

Browse files
authored
Merge pull request #629 from jichenjc/add_guestfish_support
Add coreos ignition through guestfs on s390 platform
2 parents a4b95a0 + ce1aa00 commit 05f2d9e

File tree

4 files changed

+406
-2
lines changed

4 files changed

+406
-2
lines changed

libvirt/domain.go

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package libvirt
22

33
import (
4+
"bytes"
45
"errors"
56
"fmt"
7+
"io"
68
"log"
79
"net"
810
"net/url"
11+
"os"
12+
"os/exec"
13+
"runtime"
914
"sort"
1015
"strconv"
1116
"strings"
@@ -230,7 +235,143 @@ func newDiskForCloudInit(virConn *libvirt.Connect, volumeKey string) (libvirtxml
230235
return disk, nil
231236
}
232237

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 {
234375
if ignition, ok := d.GetOk("coreos_ignition"); ok {
235376
ignitionKey, err := getIgnitionVolumeKeyFromTerraformID(ignition.(string))
236377
if err != nil {
@@ -256,6 +397,15 @@ func setCoreOSIgnition(d *schema.ResourceData, domainDef *libvirtxml.Domain) err
256397
return nil
257398
}
258399

400+
func updateCoreOSIgnition(d *schema.ResourceData, domainDef *libvirtxml.Domain, virConn *libvirt.Connect) error {
401+
if runtime.GOARCH == "s390x" || runtime.GOARCH == "s390" {
402+
// There is no support for fw_cfg in s390x, so instead of doing fw_cfg
403+
// we have to inline update the coreos ignition file
404+
return inlineUpdateCoreOsIgnition(d, domainDef, virConn)
405+
}
406+
return fwcfgUpdateCoreOSIgnition(d, domainDef)
407+
}
408+
259409
func setVideo(d *schema.ResourceData, domainDef *libvirtxml.Domain) error {
260410
prefix := "video.0"
261411
if _, ok := d.GetOk(prefix); ok {

0 commit comments

Comments
 (0)