xref: /aosp_15_r20/external/pigweed/pw_target_runner/go/cmd/client.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	"context"
18	"errors"
19	"flag"
20	"fmt"
21	"log"
22	"os"
23	"time"
24
25	"google.golang.org/grpc"
26	"google.golang.org/grpc/codes"
27	"google.golang.org/grpc/status"
28
29	pb "pigweed/proto/pw_target_runner/target_runner_pb"
30)
31
32// Client is a gRPC client that communicates with a TargetRunner service.
33type Client struct {
34	conn *grpc.ClientConn
35}
36
37// NewClient creates a gRPC client which connects to a gRPC server hosted at the
38// specified address.
39func NewClient(host string, port int) (*Client, error) {
40	// The server currently only supports running locally over an insecure
41	// connection.
42	// TODO(frolv): Investigate adding TLS support to the server and client.
43	opts := []grpc.DialOption{
44		grpc.WithInsecure(),
45		grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(20 * 1024 * 1024)),
46	}
47
48	conn, err := grpc.Dial(fmt.Sprintf("%s:%d", host, port), opts...)
49	if err != nil {
50		return nil, err
51	}
52
53	return &Client{conn}, nil
54}
55
56// RunBinary sends a RunBinary RPC to the target runner service.
57func (c *Client) RunBinary(path string) error {
58	data, err := os.ReadFile(path)
59	if err != nil {
60		return err
61	}
62
63	client := pb.NewTargetRunnerClient(c.conn)
64	req := &pb.RunBinaryRequest{Binary: &pb.RunBinaryRequest_TestBinary{TestBinary: data}}
65
66	res, err := client.RunBinary(context.Background(), req)
67	if err != nil {
68		return err
69	}
70
71	fmt.Printf("%s\n", path)
72	fmt.Printf(
73		"Queued for %v, ran in %v\n\n",
74		time.Duration(res.QueueTimeNs),
75		time.Duration(res.RunTimeNs),
76	)
77	fmt.Println(string(res.Output))
78
79	if res.Result != pb.RunStatus_SUCCESS {
80		return errors.New("Binary run was unsuccessful")
81	}
82
83	return nil
84}
85
86func main() {
87	hostPtr := flag.String("host", "localhost", "Server host")
88	portPtr := flag.Int("port", 8080, "Server port")
89	pathPtr := flag.String("binary", "", "Path to executable file")
90	serverSuggestionPtr := flag.String("server_suggestion", "", "Suggested command to display if no server is available.")
91
92	flag.Parse()
93
94	if *pathPtr == "" {
95		log.Fatalf("Must provide -binary option")
96	}
97
98	cli, err := NewClient(*hostPtr, *portPtr)
99	if err != nil {
100		log.Fatalf("Failed to create gRPC client: %v", err)
101	}
102
103	if err := cli.RunBinary(*pathPtr); err != nil {
104		log.Println("Failed to run executable on target:")
105		log.Println("")
106
107		s, _ := status.FromError(err)
108		if s.Code() == codes.Unavailable {
109			log.Println("  No pw_target_runner_server is running.")
110			log.Println("  Check that a server has been started for your target.")
111			if *serverSuggestionPtr != "" {
112				log.Println("  Consider starting a test server with: ", *serverSuggestionPtr)
113			}
114		} else {
115			log.Printf("  %v\n", err)
116		}
117
118		log.Fatal("")
119	}
120}
121