Adding publishing service (part 2)

Implementation Skeleton

BUG=None
TEST=None

Cq-Depend: chromium:3098708
Change-Id: Ic2b003bec8a7ebe5a682bce4a450d17d89e4f347
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/dev-util/+/3098519
Tested-by: Jaques Clapauch <jaquesc@google.com>
Auto-Submit: Jaques Clapauch <jaquesc@google.com>
Commit-Queue: Jaques Clapauch <jaquesc@google.com>
Reviewed-by: Andrew Lamb <andrewlamb@chromium.org>
diff --git a/src/chromiumos/test/publish/cmd/main.go b/src/chromiumos/test/publish/cmd/main.go
new file mode 100644
index 0000000..b5bb090
--- /dev/null
+++ b/src/chromiumos/test/publish/cmd/main.go
@@ -0,0 +1,78 @@
+// 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 (
+	"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/publishserver/", 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 {
+		flag.NewFlagSet("version", flag.ExitOnError)
+		flag.Parse()
+
+		if os.Args[1] == "version" {
+			fmt.Println("publishservice 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 publishservice version ", Version)
+		l, err := net.Listen("tcp", ":0")
+		if err != nil {
+			logger.Fatalln("Failed to create a net listener: ", err)
+			return 2
+		}
+
+		server, destructor := newPublishServiceServer(l, logger)
+		defer destructor()
+
+		err = server.Serve(l)
+		if err != nil {
+			logger.Fatalln("Failed to initialize server: ", err)
+		}
+		return 0
+	}())
+}
diff --git a/src/chromiumos/test/publish/cmd/publishserver.go b/src/chromiumos/test/publish/cmd/publishserver.go
new file mode 100644
index 0000000..33f5e53
--- /dev/null
+++ b/src/chromiumos/test/publish/cmd/publishserver.go
@@ -0,0 +1,52 @@
+// 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 publish_service.proto (see proto for details)
+package main
+
+import (
+	"chromiumos/lro"
+	"context"
+	"log"
+	"net"
+
+	"go.chromium.org/chromiumos/config/go/longrunning"
+	"go.chromium.org/chromiumos/config/go/test/api"
+	"google.golang.org/grpc"
+)
+
+// PublishServiceServer implementation of publish_service.proto
+type PublishServiceServer struct {
+	manager *lro.Manager
+	logger  *log.Logger
+}
+
+// newPublishServiceServer creates a new publish service server to listen to rpc requests.
+func newPublishServiceServer(l net.Listener, logger *log.Logger) (*grpc.Server, func()) {
+	s := &PublishServiceServer{
+		manager: lro.New(),
+		logger:  logger,
+	}
+
+	server := grpc.NewServer()
+	destructor := func() {
+		s.manager.Close()
+	}
+
+	api.RegisterPublishServiceServer(server, s)
+	logger.Println("publishservice listen to request at ", l.Addr().String())
+	return server, destructor
+}
+
+// UploadToGS uploads the designated folder to the provided Google Cloud Storage
+// bucket/object
+//
+// TODO(jaquesc): Implement this
+func (s *PublishServiceServer) UploadToGS(ctx context.Context, req *api.UploadToGSRequest) (*longrunning.Operation, error) {
+	s.logger.Println("Received api.UploadToGSRequest: ", *req)
+	s.logger.Println("TODO(jaquesc): Implement")
+	op := s.manager.NewOperation()
+	s.manager.SetResult(op.Name, &api.UploadToGSResponse{})
+	return op, nil
+}