Skip to content

Commit

Permalink
Add qemu API to stream journal, use it in devshell
Browse files Browse the repository at this point in the history
This came up in coreos/ignition-dracut#146
and since then we've been doing more "ad hoc unit writing to virtio"
in mantle, but let's add a general API that streams the journal.

This is just better for what devshell wants - we can more precisely
watch for sshd starting.  And more code in e.g. `testiso.go` could
use it too which can come later.

The immediate motivation here is I may add another kola test
which could use this.
  • Loading branch information
cgwalters committed May 1, 2020
1 parent e89b75d commit 2bbaef6
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 49 deletions.
63 changes: 14 additions & 49 deletions mantle/cmd/kola/devshell.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,32 +113,15 @@ func runDevShellSSH(builder *platform.QemuBuilder, conf *v3types.Config) error {
}
sshPubKey := v3types.SSHAuthorizedKey(strings.TrimSpace(string(sshPubKeyBuf)))

readinessSignalChan := "coreos.devshellready"
signalReadyUnit := fmt.Sprintf(`[Unit]
Requires=dev-virtio\\x2dports-%s.device
OnFailure=emergency.target
OnFailureJobMode=isolate
After=systemd-user-sessions.service
After=sshd.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/echo ready
StandardOutput=file:/dev/virtio-ports/%[1]s
[Install]
RequiredBy=multi-user.target`, readinessSignalChan)
signalPoweroffUnit := fmt.Sprintf(`[Unit]
Requires=dev-virtio\\x2dports-%s.device
OnFailure=emergency.target
OnFailureJobMode=isolate
Conflicts=reboot.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStop=/bin/echo poweroff
StandardOutput=file:/dev/virtio-ports/%[1]s
[Install]
WantedBy=multi-user.target`, readinessSignalChan)
// Await sshd startup;
// src/systemd/sd-messages.h
// 89:#define SD_MESSAGE_UNIT_STARTED SD_ID128_MAKE(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf)
journalConf, journalPipe, err := builder.VirtioJournal("-u sshd MESSAGE_ID=39f53479d3a045ac8e11786248231fbf")
if err != nil {
return err
}
confm := v3.Merge(*conf, *journalConf)
conf = &confm

devshellConfig := v3types.Config{
Ignition: v3types.Ignition{
Expand All @@ -154,29 +137,11 @@ WantedBy=multi-user.target`, readinessSignalChan)
},
},
},
Systemd: v3types.Systemd{
Units: []v3types.Unit{
{
Name: "coreos-devshell-signal-ready.service",
Contents: &signalReadyUnit,
Enabled: util.BoolToPtr(true),
},
{
Name: "coreos-devshell-signal-poweroff.service",
Contents: &signalPoweroffUnit,
Enabled: util.BoolToPtr(true),
},
},
},
}
confm := v3.Merge(*conf, devshellConfig)
confm = v3.Merge(*conf, devshellConfig)
conf = &confm

readyChan, err := builder.VirtioChannelRead(readinessSignalChan)
if err != nil {
return err
}
readyReader := bufio.NewReader(readyChan)
readyReader := bufio.NewReader(journalPipe)

builder.SetConfig(*conf, kola.Options.IgnitionVersion == "v2")

Expand Down Expand Up @@ -230,8 +195,8 @@ WantedBy=multi-user.target`, readinessSignalChan)
if err != nil {
errchan <- err
}
if readyMsg != "ready" {
errchan <- fmt.Errorf("Unexpected ready message: %s", readyMsg)
if !strings.Contains(readyMsg, "Started OpenSSH server daemon") {
errchan <- fmt.Errorf("Unexpected journal message: %s", readyMsg)
}
var s struct{}
readychan <- s
Expand Down Expand Up @@ -263,7 +228,7 @@ loop:
// Caught SIGINT, we're done
return fmt.Errorf("Caught SIGINT before successful login")
case _ = <-readychan:
fmt.Printf("\033[2K\rvirtio: connected\n")
fmt.Printf("\033[2K\rvirtio journal connected - sshd started\n")
break loop
}
}
Expand Down
37 changes: 37 additions & 0 deletions mantle/platform/qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,43 @@ func (builder *QemuBuilder) SerialPipe() (*os.File, error) {
return r, nil
}

// VirtioJournal configures the OS and VM to stream the systemd journal
// (post-switchroot) over a virtio-serial channel. The first return value
// is an Ignition fragment that should be included in the target config,
// and the file stream will be newline-separated JSON. The optional
// queryArguments filters the stream - see `man journalctl` for more information.
func (builder *QemuBuilder) VirtioJournal(queryArguments string) (*v3types.Config, *os.File, error) {
stream, err := builder.VirtioChannelRead("mantlejournal")
if err != nil {
return nil, nil, err
}
var streamJournalUnit = fmt.Sprintf(`[Unit]
Requires=dev-virtio\\x2dports-mantlejournal.device
[Service]
Type=simple
StandardOutput=file:/dev/virtio-ports/mantlejournal
ExecStart=/usr/bin/journalctl -q -b -f -o json --no-tail %s
[Install]
RequiredBy=multi-user.target
`, queryArguments)

conf := v3types.Config{
Ignition: v3types.Ignition{
Version: "3.0.0",
},
Systemd: v3types.Systemd{
Units: []v3types.Unit{
{
Name: "mantle-virtio-journal-stream.service",
Contents: &streamJournalUnit,
Enabled: util.BoolToPtr(true),
},
},
},
}
return &conf, stream, nil
}

func (builder *QemuBuilder) Exec() (*QemuInstance, error) {
builder.finalize()
var err error
Expand Down

0 comments on commit 2bbaef6

Please sign in to comment.