Basic RPC server wiring for dut_service.proto
BUG=None
TEST=go test
Change-Id: Ideea3e5ac6251519042976765562142ac7b400c7
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/dev-util/+/2900977
Tested-by: C Shapiro <shapiroc@chromium.org>
Commit-Queue: C Shapiro <shapiroc@chromium.org>
Reviewed-by: Seewai Fu <seewaifu@google.com>
diff --git a/test/src/chromiumos/dutservice/cmd/dutserver/dutserver.go b/test/src/chromiumos/dutservice/cmd/dutserver/dutserver.go
new file mode 100644
index 0000000..d57b2a7
--- /dev/null
+++ b/test/src/chromiumos/dutservice/cmd/dutserver/dutserver.go
@@ -0,0 +1,86 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Implements dut_service.proto (see proto for details)
+package main
+
+import (
+ "context"
+ "log"
+ "net"
+
+ "chromiumos/lro"
+
+ "go.chromium.org/chromiumos/config/go/longrunning"
+ "go.chromium.org/chromiumos/config/go/test/api"
+ "google.golang.org/grpc"
+)
+
+// DutServer implementation of dut_service.proto
+type DutServer struct {
+ Manager *lro.Manager
+ logger *log.Logger
+}
+
+// newDutServer creates a new dut service server to listen to rpc requests.
+func newDutServer(l net.Listener, logger *log.Logger) (*grpc.Server, error) {
+ s := &DutServer{
+ Manager: lro.New(),
+ logger: logger,
+ }
+ defer s.Manager.Close()
+ server := grpc.NewServer()
+ api.RegisterDutServiceServer(server, s)
+ longrunning.RegisterOperationsServer(server, s.Manager)
+ logger.Println("dutservice listen to request at ", l.Addr().String())
+ return server, nil
+}
+
+// ProvisionDut installs a specified version of Chrome OS on the DUT, along
+// with any specified DLCs.
+//
+// If the DUT is already on the specified version of Chrome OS, the OS will
+// not be provisioned.
+//
+// If the DUT already has the specified list of DLCs, only the missing DLCs
+// will be provisioned.
+func (s *DutServer) ProvisionDut(ctx context.Context, req *api.ProvisionDutRequest) (*longrunning.Operation, error) {
+ s.logger.Println("Received api.ProvisionDutRequest: ", *req)
+ op := s.Manager.NewOperation()
+ s.Manager.SetResult(op.Name, &api.ProvisionDutResponse{})
+ return op, nil
+}
+
+// ProvisionLacros installs a specified version of Lacros on the DUT.
+//
+// If the DUT already has the specified version of Lacros, Lacros will not be
+// provisioned.
+func (s *DutServer) ProvisionLacros(ctx context.Context, req *api.ProvisionLacrosRequest) (*longrunning.Operation, error) {
+ s.logger.Println("Received api.ProvisionLacrosRequest: ", *req)
+ op := s.Manager.NewOperation()
+ s.Manager.SetResult(op.Name, &api.ProvisionLacrosResponse{})
+ return op, nil
+}
+
+// ProvisionAsh installs a specified version of ash-chrome on the DUT.
+//
+// This directly overwrites the version of ash-chrome on the current root
+// disk partition.
+func (s *DutServer) ProvisionAsh(ctx context.Context, req *api.ProvisionAshRequest) (*longrunning.Operation, error) {
+ s.logger.Println("Received api.ProvisionAshRequest: ", *req)
+ op := s.Manager.NewOperation()
+ s.Manager.SetResult(op.Name, &api.ProvisionAshResponse{})
+ return op, nil
+}
+
+// ProvisionArc installs a specified version of ARC on the DUT.
+//
+// This directly overwrites the version of ARC on the current root
+// disk partition.
+func (s *DutServer) ProvisionArc(ctx context.Context, req *api.ProvisionArcRequest) (*longrunning.Operation, error) {
+ s.logger.Println("Received api.ProvisionArcRequest: ", *req)
+ op := s.Manager.NewOperation()
+ s.Manager.SetResult(op.Name, &api.ProvisionArcResponse{})
+ return op, nil
+}
diff --git a/test/src/chromiumos/dutservice/cmd/dutserver/dutserver_test.go b/test/src/chromiumos/dutservice/cmd/dutserver/dutserver_test.go
new file mode 100644
index 0000000..905c7bd
--- /dev/null
+++ b/test/src/chromiumos/dutservice/cmd/dutserver/dutserver_test.go
@@ -0,0 +1,44 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "context"
+ "log"
+ "net"
+ "testing"
+
+ "go.chromium.org/chromiumos/config/go/test/api"
+ "google.golang.org/grpc"
+)
+
+// TestDutServer_Empty tests if DutServer can handle emtpy requst without problem.
+func TestDutServer_Empty(t *testing.T) {
+ var logBuf bytes.Buffer
+ l, err := net.Listen("tcp", ":0")
+ if err != nil {
+ t.Fatal("Failed to create a net listener: ", err)
+ }
+
+ ctx := context.Background()
+ srv, err := newDutServer(l, log.New(&logBuf, "", log.LstdFlags|log.LUTC))
+ if err != nil {
+ t.Fatalf("Failed to start DutServer: %v", err)
+ }
+ go srv.Serve(l)
+ defer srv.Stop()
+
+ conn, err := grpc.Dial(l.Addr().String(), grpc.WithInsecure())
+ if err != nil {
+ t.Fatalf("Failed to dial: %v", err)
+ }
+ defer conn.Close()
+
+ cl := api.NewDutServiceClient(conn)
+ if _, err := cl.ProvisionDut(ctx, &api.ProvisionDutRequest{}); err != nil {
+ t.Fatalf("Failed at api.ProvisionDut: %v", err)
+ }
+}
diff --git a/test/src/chromiumos/dutservice/cmd/dutserver/main.go b/test/src/chromiumos/dutservice/cmd/dutserver/main.go
new file mode 100644
index 0000000..5c21bb1
--- /dev/null
+++ b/test/src/chromiumos/dutservice/cmd/dutserver/main.go
@@ -0,0 +1,77 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Package main implements the dutservice used to run tests in RTD.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "os"
+ "path/filepath"
+ "time"
+)
+
+// Version is the version info of this command. It is filled in during emerge.
+var Version = "<unknown>"
+
+// createLogFile creates a file and its parent directory for logging purpose.
+func createLogFile() (*os.File, error) {
+ t := time.Now()
+ fullPath := filepath.Join("/tmp/dutservice/", t.Format("20060102-150405"))
+ if err := os.MkdirAll(fullPath, 0755); err != nil {
+ return nil, fmt.Errorf("failed to create directory %v: %v", fullPath, err)
+ }
+
+ logFullPathName := filepath.Join(fullPath, "log.txt")
+
+ // Log the full output of the command to disk.
+ logFile, err := os.Create(logFullPathName)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create file %v: %v", fullPath, err)
+ }
+ return logFile, nil
+}
+
+// newLogger creates a logger. Using go default logger for now.
+func newLogger(logFile *os.File) *log.Logger {
+ mw := io.MultiWriter(logFile, os.Stderr)
+ return log.New(mw, "", log.LstdFlags|log.LUTC)
+}
+
+func main() {
+ os.Exit(func() int {
+ version := flag.Bool("version", false, "print version and exit")
+ flag.Parse()
+
+ if *version {
+ fmt.Println("dutservice version ", Version)
+ return 0
+ }
+
+ logFile, err := createLogFile()
+ if err != nil {
+ log.Fatalln("Failed to create log file: ", err)
+ }
+ defer logFile.Close()
+
+ logger := newLogger(logFile)
+ logger.Println("Starting dutservice version ", Version)
+ l, err := net.Listen("tcp", ":0")
+ if err != nil {
+ logger.Fatalln("Failed to create a net listener: ", err)
+ return 2
+ }
+ server, err := newDutServer(l, logger)
+ if err != nil {
+ logger.Fatalln("Failed to start dutservice server: ", err)
+ }
+
+ server.Serve(l)
+ return 0
+ }())
+}