Skip to content

Commit b8040f1

Browse files
committed
Proper gRPC error handling
1 parent e7d4eaa commit b8040f1

File tree

8 files changed

+201
-141
lines changed

8 files changed

+201
-141
lines changed

cli/compile/compile.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/arduino/arduino-cli/cli/feedback"
2626
"github.com/arduino/arduino-cli/cli/output"
2727
"github.com/arduino/arduino-cli/configuration"
28+
"google.golang.org/grpc/status"
2829

2930
"github.com/arduino/arduino-cli/cli/errorcodes"
3031
"github.com/arduino/arduino-cli/cli/instance"
@@ -197,7 +198,7 @@ func run(cmd *cobra.Command, args []string) {
197198
ImportDir: buildPath,
198199
Programmer: programmer,
199200
}
200-
var err error
201+
var err *status.Status
201202
if output.OutputFormat == "json" {
202203
// TODO: do not print upload output in json mode
203204
uploadOut := new(bytes.Buffer)

client_example/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
dbg "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/debug/v1"
3333
"github.com/arduino/arduino-cli/rpc/cc/arduino/cli/settings/v1"
3434
"google.golang.org/grpc"
35+
"google.golang.org/grpc/status"
3536
)
3637

3738
var (

commands/daemon/daemon.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ func (s *ArduinoCoreServerImpl) Upload(req *rpc.UploadRequest, stream rpc.Arduin
314314
utils.FeedStreamTo(func(data []byte) { stream.Send(&rpc.UploadResponse{ErrStream: data}) }),
315315
)
316316
if err != nil {
317-
return err
317+
return err.Err()
318318
}
319319
return stream.Send(resp)
320320
}
@@ -327,7 +327,7 @@ func (s *ArduinoCoreServerImpl) UploadUsingProgrammer(req *rpc.UploadUsingProgra
327327
utils.FeedStreamTo(func(data []byte) { stream.Send(&rpc.UploadUsingProgrammerResponse{ErrStream: data}) }),
328328
)
329329
if err != nil {
330-
return err
330+
return err.Err()
331331
}
332332
return stream.Send(resp)
333333
}
@@ -340,7 +340,7 @@ func (s *ArduinoCoreServerImpl) BurnBootloader(req *rpc.BurnBootloaderRequest, s
340340
utils.FeedStreamTo(func(data []byte) { stream.Send(&rpc.BurnBootloaderResponse{ErrStream: data}) }),
341341
)
342342
if err != nil {
343-
return err
343+
return err.Err()
344344
}
345345
return stream.Send(resp)
346346
}

commands/upload/burnbootloader.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ import (
2222
"github.com/arduino/arduino-cli/commands"
2323
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2424
"github.com/sirupsen/logrus"
25+
"google.golang.org/grpc/status"
2526
)
2627

2728
// BurnBootloader FIXMEDOC
28-
func BurnBootloader(ctx context.Context, req *rpc.BurnBootloaderRequest, outStream io.Writer, errStream io.Writer) (*rpc.BurnBootloaderResponse, error) {
29+
func BurnBootloader(ctx context.Context, req *rpc.BurnBootloaderRequest, outStream io.Writer, errStream io.Writer) (*rpc.BurnBootloaderResponse, *status.Status) {
2930
logrus.
3031
WithField("fqbn", req.GetFqbn()).
3132
WithField("port", req.GetPort()).

commands/upload/upload.go

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,25 @@ import (
3535
properties "github.com/arduino/go-properties-orderedmap"
3636
"github.com/pkg/errors"
3737
"github.com/sirupsen/logrus"
38+
"google.golang.org/grpc/codes"
39+
"google.golang.org/grpc/status"
3840
)
3941

4042
// Upload FIXMEDOC
41-
func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, errStream io.Writer) (*rpc.UploadResponse, error) {
43+
func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, errStream io.Writer) (*rpc.UploadResponse, *status.Status) {
4244
logrus.Tracef("Upload %s on %s started", req.GetSketchPath(), req.GetFqbn())
4345

4446
// TODO: make a generic function to extract sketch from request
4547
// and remove duplication in commands/compile.go
4648
sketchPath := paths.New(req.GetSketchPath())
4749
sk, err := sketch.New(sketchPath)
4850
if err != nil && req.GetImportDir() == "" && req.GetImportFile() == "" {
49-
return nil, fmt.Errorf("opening sketch: %s", err)
51+
return nil, status.Newf(codes.InvalidArgument, "Sketch not found: %s", err)
5052
}
5153

5254
pm := commands.GetPackageManager(req.GetInstance().GetId())
5355

54-
err = runProgramAction(
56+
return &rpc.UploadResponse{}, runProgramAction(
5557
pm,
5658
sk,
5759
req.GetImportFile(),
@@ -66,18 +68,14 @@ func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, er
6668
errStream,
6769
req.GetDryRun(),
6870
)
69-
if err != nil {
70-
return nil, err
71-
}
72-
return &rpc.UploadResponse{}, nil
7371
}
7472

7573
// UsingProgrammer FIXMEDOC
76-
func UsingProgrammer(ctx context.Context, req *rpc.UploadUsingProgrammerRequest, outStream io.Writer, errStream io.Writer) (*rpc.UploadUsingProgrammerResponse, error) {
74+
func UsingProgrammer(ctx context.Context, req *rpc.UploadUsingProgrammerRequest, outStream io.Writer, errStream io.Writer) (*rpc.UploadUsingProgrammerResponse, *status.Status) {
7775
logrus.Tracef("Upload using programmer %s on %s started", req.GetSketchPath(), req.GetFqbn())
7876

7977
if req.GetProgrammer() == "" {
80-
return nil, errors.New("programmer not specified")
78+
return nil, status.New(codes.InvalidArgument, "Programmer not specified")
8179
}
8280
_, err := Upload(ctx, &rpc.UploadRequest{
8381
Instance: req.GetInstance(),
@@ -99,17 +97,17 @@ func runProgramAction(pm *packagemanager.PackageManager,
9997
programmerID string,
10098
verbose, verify, burnBootloader bool,
10199
outStream, errStream io.Writer,
102-
dryRun bool) error {
100+
dryRun bool) *status.Status {
103101

104102
if burnBootloader && programmerID == "" {
105-
return fmt.Errorf("no programmer specified for burning bootloader")
103+
return status.New(codes.InvalidArgument, "Programmer not specified")
106104
}
107105

108106
// FIXME: make a specification on how a port is specified via command line
109107
if port == "" && sk != nil && sk.Metadata != nil {
110108
deviceURI, err := url.Parse(sk.Metadata.CPU.Port)
111109
if err != nil {
112-
return fmt.Errorf("invalid Device URL format: %s", err)
110+
return status.Newf(codes.InvalidArgument, "Invalid Device URL format: %s", err)
113111
}
114112
if deviceURI.Scheme == "serial" {
115113
port = deviceURI.Host + deviceURI.Path
@@ -121,18 +119,18 @@ func runProgramAction(pm *packagemanager.PackageManager,
121119
fqbnIn = sk.Metadata.CPU.Fqbn
122120
}
123121
if fqbnIn == "" {
124-
return fmt.Errorf("no Fully Qualified Board Name provided")
122+
return status.New(codes.InvalidArgument, "No FQBN (Fully Qualified Board Name) provided")
125123
}
126124
fqbn, err := cores.ParseFQBN(fqbnIn)
127125
if err != nil {
128-
return fmt.Errorf("incorrect FQBN: %s", err)
126+
return status.Newf(codes.InvalidArgument, fmt.Sprintf("Invalid FQBN: %s", err))
129127
}
130128
logrus.WithField("fqbn", fqbn).Tracef("Detected FQBN")
131129

132130
// Find target board and board properties
133131
_, boardPlatform, board, boardProperties, buildPlatform, err := pm.ResolveFQBN(fqbn)
134132
if err != nil {
135-
return fmt.Errorf("incorrect FQBN: %s", err)
133+
return status.Newf(codes.InvalidArgument, "Could not resolve FQBN: %s", err)
136134
}
137135
logrus.
138136
WithField("boardPlatform", boardPlatform).
@@ -149,7 +147,7 @@ func runProgramAction(pm *packagemanager.PackageManager,
149147
programmer = buildPlatform.Programmers[programmerID]
150148
}
151149
if programmer == nil {
152-
return fmt.Errorf("programmer '%s' not available", programmerID)
150+
return status.Newf(codes.InvalidArgument, "Programmer '%s' not available", programmerID)
153151
}
154152
}
155153

@@ -174,7 +172,7 @@ func runProgramAction(pm *packagemanager.PackageManager,
174172
if t, ok := props.GetOk(toolProperty); ok {
175173
uploadToolID = t
176174
} else {
177-
return fmt.Errorf("cannot get programmer tool: undefined '%s' property", toolProperty)
175+
return status.Newf(codes.FailedPrecondition, "Cannot get programmer tool: undefined '%s' property", toolProperty)
178176
}
179177
}
180178

@@ -190,7 +188,7 @@ func runProgramAction(pm *packagemanager.PackageManager,
190188
Trace("Upload tool")
191189

192190
if split := strings.Split(uploadToolID, ":"); len(split) > 2 {
193-
return fmt.Errorf("invalid 'upload.tool' property: %s", uploadToolID)
191+
return status.Newf(codes.FailedPrecondition, "Invalid 'upload.tool' property: %s", uploadToolID)
194192
} else if len(split) == 2 {
195193
uploadToolID = split[1]
196194
uploadToolPlatform = pm.GetInstalledPlatformRelease(
@@ -229,7 +227,10 @@ func runProgramAction(pm *packagemanager.PackageManager,
229227
}
230228

231229
if !uploadProperties.ContainsKey("upload.protocol") && programmer == nil {
232-
return fmt.Errorf("a programmer is required to upload for this board")
230+
err, _ := status.
231+
Newf(codes.InvalidArgument, "A programmer is required to upload on this board").
232+
WithDetails(&rpc.ProgrammerIsRequiredForUploadError{})
233+
return err
233234
}
234235

235236
// Set properties for verbose upload
@@ -277,13 +278,13 @@ func runProgramAction(pm *packagemanager.PackageManager,
277278
if !burnBootloader {
278279
importPath, sketchName, err := determineBuildPathAndSketchName(importFile, importDir, sk, fqbn)
279280
if err != nil {
280-
return errors.Errorf("retrieving build artifacts: %s", err)
281+
return status.Newf(codes.Internal, "Error finding build artifacts: %s", err)
281282
}
282283
if !importPath.Exist() {
283-
return fmt.Errorf("compiled sketch not found in %s", importPath)
284+
return status.Newf(codes.Internal, "Compiled sketch not found in %s", importPath)
284285
}
285286
if !importPath.IsDir() {
286-
return fmt.Errorf("expected compiled sketch in directory %s, but is a file instead", importPath)
287+
return status.Newf(codes.Internal, "Expected compiled sketch in directory %s, but is a file instead", importPath)
287288
}
288289
uploadProperties.SetPath("build.path", importPath)
289290
uploadProperties.Set("build.project_name", sketchName)
@@ -367,18 +368,18 @@ func runProgramAction(pm *packagemanager.PackageManager,
367368
// Run recipes for upload
368369
if burnBootloader {
369370
if err := runTool("erase.pattern", uploadProperties, outStream, errStream, verbose, dryRun); err != nil {
370-
return fmt.Errorf("chip erase error: %s", err)
371+
return status.Newf(codes.Internal, "Failed chip erase: %s", err)
371372
}
372373
if err := runTool("bootloader.pattern", uploadProperties, outStream, errStream, verbose, dryRun); err != nil {
373-
return fmt.Errorf("burn bootloader error: %s", err)
374+
return status.Newf(codes.Internal, "Failed to burn bootloader: %s", err)
374375
}
375376
} else if programmer != nil {
376377
if err := runTool("program.pattern", uploadProperties, outStream, errStream, verbose, dryRun); err != nil {
377-
return fmt.Errorf("programming error: %s", err)
378+
return status.Newf(codes.Internal, "Failed programming: %s", err)
378379
}
379380
} else {
380381
if err := runTool("upload.pattern", uploadProperties, outStream, errStream, verbose, dryRun); err != nil {
381-
return fmt.Errorf("uploading error: %s", err)
382+
return status.Newf(codes.Internal, "Failed uploading: %s", err)
382383
}
383384
}
384385

commands/upload/upload_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ func TestUploadPropertiesComposition(t *testing.T) {
177177
testRunner := func(t *testing.T, test test, verboseVerify bool) {
178178
outStream := &bytes.Buffer{}
179179
errStream := &bytes.Buffer{}
180-
err := runProgramAction(
180+
status := runProgramAction(
181181
pm,
182182
nil, // sketch
183183
"", // importFile
@@ -197,9 +197,9 @@ func TestUploadPropertiesComposition(t *testing.T) {
197197
verboseVerifyOutput = "quiet noverify"
198198
}
199199
if test.expectedOutput == "FAIL" {
200-
require.Error(t, err)
200+
require.NotNil(t, status)
201201
} else {
202-
require.NoError(t, err)
202+
require.Nil(t, status)
203203
outFiltered := strings.ReplaceAll(outStream.String(), "\r", "")
204204
outFiltered = strings.ReplaceAll(outFiltered, "\\", "/")
205205
require.Contains(t, outFiltered, strings.ReplaceAll(test.expectedOutput, "$$VERBOSE-VERIFY$$", verboseVerifyOutput))

0 commit comments

Comments
 (0)