Reland "[botanist] Expose a socket to proxy serial I/O"
This is a reland of Ice609af336852143fa9bc4e129c7310c6bba51da
Original change's description:
> [botanist] Expose a socket to proxy serial I/O
>
> Bug: 41930
> Bug: 41377
>
> Test: CQ consumes botanist from source.
>
> Change-Id: Ice609af336852143fa9bc4e129c7310c6bba51da
TBR=phosek@google.com,garymm@google.com,joshuaseaton@google.com
Change-Id: I827a1377d8ae045b192a657aa4062369b043b607
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 41930, 41377
diff --git a/tools/botanist/botanist.go b/tools/botanist/botanist.go
new file mode 100644
index 0000000..016eea1c
--- /dev/null
+++ b/tools/botanist/botanist.go
@@ -0,0 +1,14 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can
+// found in the LICENSE file.
+
+package botanist
+
+const (
+ // SerialLogBufferSize gives the amount of data to buffer when doing serial
+ // I/O.
+ // The size of the serial log of an entire test run is on the order of 1MB;
+ // accordingly, we allow a buffer size of an order of magnitude less for
+ // conservative estimate.
+ SerialLogBufferSize = (1000 * 1000) / 10
+)
diff --git a/tools/botanist/cmd/run.go b/tools/botanist/cmd/run.go
index eb54c46..13f784f 100644
--- a/tools/botanist/cmd/run.go
+++ b/tools/botanist/cmd/run.go
@@ -5,6 +5,8 @@
import (
"context"
+ "crypto/rand"
+ "encoding/hex"
"encoding/json"
"flag"
"fmt"
@@ -12,16 +14,19 @@
"io/ioutil"
"net"
"os"
+ "path/filepath"
"sync"
"time"
"go.fuchsia.dev/fuchsia/tools/bootserver/lib"
+ "go.fuchsia.dev/fuchsia/tools/botanist/lib"
"go.fuchsia.dev/fuchsia/tools/botanist/target"
"go.fuchsia.dev/fuchsia/tools/lib/command"
"go.fuchsia.dev/fuchsia/tools/lib/logger"
"go.fuchsia.dev/fuchsia/tools/lib/runner"
"go.fuchsia.dev/fuchsia/tools/lib/syslog"
"go.fuchsia.dev/fuchsia/tools/net/sshutil"
+ "go.fuchsia.dev/fuchsia/tools/serial"
"github.com/google/subcommands"
)
@@ -170,22 +175,40 @@
errs := make(chan error)
- serial := t0.Serial()
- if serial != nil && r.serialLogFile != "" {
+ var socketPath string
+ if t0.Serial() != nil {
// Modify the zirconArgs passed to the kernel on boot to enable serial on x64.
// arm64 devices should already be enabling kernel.serial at compile time.
r.zirconArgs = append(r.zirconArgs, "kernel.serial=legacy")
// Force serial output to the console instead of buffering it.
r.zirconArgs = append(r.zirconArgs, "kernel.bypass-debuglog=true")
- serialLog, err := os.Create(r.serialLogFile)
+ sOpts := serial.ServerOptions{
+ WriteBufferSize: botanist.SerialLogBufferSize,
+ }
+ if r.serialLogFile != "" {
+ serialLog, err := os.Create(r.serialLogFile)
+ if err != nil {
+ return err
+ }
+ defer serialLog.Close()
+ sOpts.AuxiliaryOutput = serialLog
+ }
+
+ s := serial.NewServer(t0.Serial(), sOpts)
+ socketPath = createSocketPath()
+ addr := &net.UnixAddr{Name: socketPath, Net: "unix"}
+ l, err := net.ListenUnix("unix", addr)
if err != nil {
return err
}
- defer serialLog.Close()
+ defer l.Close()
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
go func() {
- if _, err := io.Copy(serialLog, serial); err != nil {
- errs <- fmt.Errorf("failed to write serial log: %v", err)
+ if err := s.Run(ctx, l); err != nil {
+ errs <- err
+ return
}
}()
}
@@ -227,7 +250,7 @@
}
go func() {
wg.Wait()
- errs <- r.runAgainstTarget(ctx, t0, args)
+ errs <- r.runAgainstTarget(ctx, t0, args, socketPath)
}()
select {
@@ -238,9 +261,10 @@
return nil
}
-func (r *RunCommand) runAgainstTarget(ctx context.Context, t Target, args []string) error {
+func (r *RunCommand) runAgainstTarget(ctx context.Context, t Target, args []string, socketPath string) error {
subprocessEnv := map[string]string{
- "FUCHSIA_NODENAME": t.Nodename(),
+ "FUCHSIA_NODENAME": t.Nodename(),
+ "FUCHSIA_SERIAL_SOCKET": socketPath,
}
// If |netboot| is true, then we assume that fuchsia is not provisioned
@@ -321,6 +345,13 @@
return subcommands.ExitSuccess
}
+func createSocketPath() string {
+ // We randomly construct a socket path that is highly improbable to collide with anything.
+ randBytes := make([]byte, 16)
+ rand.Read(randBytes)
+ return filepath.Join(os.TempDir(), "serial"+hex.EncodeToString(randBytes)+".sock")
+}
+
func deriveTarget(ctx context.Context, obj []byte, opts target.Options) (Target, error) {
type typed struct {
Type string `json:"type"`