xref: /aosp_15_r20/external/pigweed/pw_target_runner/go/cmd/server.go (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1// Copyright 2019 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7//	https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14package main
15
16import (
17	"errors"
18	"flag"
19	"fmt"
20	"io/ioutil"
21	"log"
22
23	"google.golang.org/protobuf/encoding/prototext"
24	"pigweed/pw_target_runner"
25
26	pb "pigweed/proto/pw_target_runner/exec_server_config_pb"
27)
28
29// ServerOptions contains command-line options for the server.
30type ServerOptions struct {
31	// Path to a server configuration file.
32	config string
33
34	// Port on which to run.
35	port int
36}
37
38// configureServerFromFile sets up the server with workers specified in a
39// config file. The file contains a pw.target_runner.ServerConfig protobuf
40// message in canonical protobuf text format.
41func configureServerFromFile(s *pw_target_runner.Server, filepath string) error {
42	content, err := ioutil.ReadFile(filepath)
43	if err != nil {
44		return err
45	}
46
47	var config pb.ServerConfig
48	if err := prototext.Unmarshal(content, &config); err != nil {
49		return err
50	}
51
52	log.Printf("Parsed server configuration from %s\n", filepath)
53
54	runners := config.GetRunner()
55	if runners == nil {
56		return nil
57	}
58
59	// Create an exec worker for each of the runner messages listed in the
60	// config and register them with the server.
61	for i, runner := range runners {
62		// Build the complete command for the worker from its "command"
63		// and "args" fields in the proto message. The command is
64		// required; arguments are optional.
65		cmd := []string{runner.GetCommand()}
66		if cmd[0] == "" {
67			msg := fmt.Sprintf(
68				"ServerConfig.runner[%d] does not specify a command; skipping\n", i)
69			return errors.New(msg)
70		}
71
72		if args := runner.GetArgs(); args != nil {
73			cmd = append(cmd, args...)
74		}
75
76		worker := pw_target_runner.NewExecDeviceRunner(i, cmd)
77		s.RegisterWorker(worker)
78
79		log.Printf(
80			"Registered ExecDeviceRunner %s with args %v\n",
81			cmd[0],
82			cmd[1:])
83	}
84
85	return nil
86}
87
88func main() {
89	configPtr := flag.String("config", "", "Path to server configuration file")
90	portPtr := flag.Int("port", 8080, "Server port")
91
92	flag.Parse()
93
94	server := pw_target_runner.NewServer()
95
96	if *configPtr != "" {
97		if err := configureServerFromFile(server, *configPtr); err != nil {
98			log.Fatalf("Failed to parse config file %s: %v", *configPtr, err)
99		}
100	}
101
102	if err := server.Bind(*portPtr); err != nil {
103		log.Fatal(err)
104	}
105
106	if err := server.Serve(); err != nil {
107		log.Fatalf("Failed to start server: %v", err)
108	}
109}
110