Skip to content

Commit

Permalink
Proper gRPC error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
cmaglie committed Jun 23, 2021
1 parent 83d71c8 commit 3a17f7f
Show file tree
Hide file tree
Showing 13 changed files with 249 additions and 174 deletions.
3 changes: 2 additions & 1 deletion cli/compile/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/arduino/arduino-cli/cli/feedback"
"github.com/arduino/arduino-cli/cli/output"
"github.com/arduino/arduino-cli/configuration"
"google.golang.org/grpc/status"

"github.com/arduino/arduino-cli/cli/errorcodes"
"github.com/arduino/arduino-cli/cli/instance"
Expand Down Expand Up @@ -197,7 +198,7 @@ func run(cmd *cobra.Command, args []string) {
ImportDir: buildPath,
Programmer: programmer,
}
var err error
var err *status.Status
if output.OutputFormat == "json" {
// TODO: do not print upload output in json mode
uploadOut := new(bytes.Buffer)
Expand Down
1 change: 1 addition & 0 deletions client_example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
dbg "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/debug/v1"
"github.com/arduino/arduino-cli/rpc/cc/arduino/cli/settings/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/status"
)

var (
Expand Down
6 changes: 3 additions & 3 deletions commands/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ func (s *ArduinoCoreServerImpl) Upload(req *rpc.UploadRequest, stream rpc.Arduin
utils.FeedStreamTo(func(data []byte) { stream.Send(&rpc.UploadResponse{ErrStream: data}) }),
)
if err != nil {
return err
return err.Err()
}
return stream.Send(resp)
}
Expand All @@ -301,7 +301,7 @@ func (s *ArduinoCoreServerImpl) UploadUsingProgrammer(req *rpc.UploadUsingProgra
utils.FeedStreamTo(func(data []byte) { stream.Send(&rpc.UploadUsingProgrammerResponse{ErrStream: data}) }),
)
if err != nil {
return err
return err.Err()
}
return stream.Send(resp)
}
Expand All @@ -314,7 +314,7 @@ func (s *ArduinoCoreServerImpl) BurnBootloader(req *rpc.BurnBootloaderRequest, s
utils.FeedStreamTo(func(data []byte) { stream.Send(&rpc.BurnBootloaderResponse{ErrStream: data}) }),
)
if err != nil {
return err
return err.Err()
}
return stream.Send(resp)
}
Expand Down
3 changes: 2 additions & 1 deletion commands/upload/burnbootloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ import (
"github.com/arduino/arduino-cli/commands"
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
"github.com/sirupsen/logrus"
"google.golang.org/grpc/status"
)

// BurnBootloader FIXMEDOC
func BurnBootloader(ctx context.Context, req *rpc.BurnBootloaderRequest, outStream io.Writer, errStream io.Writer) (*rpc.BurnBootloaderResponse, error) {
func BurnBootloader(ctx context.Context, req *rpc.BurnBootloaderRequest, outStream io.Writer, errStream io.Writer) (*rpc.BurnBootloaderResponse, *status.Status) {
logrus.
WithField("fqbn", req.GetFqbn()).
WithField("port", req.GetPort()).
Expand Down
53 changes: 27 additions & 26 deletions commands/upload/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,25 @@ import (
properties "github.com/arduino/go-properties-orderedmap"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

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

// TODO: make a generic function to extract sketch from request
// and remove duplication in commands/compile.go
sketchPath := paths.New(req.GetSketchPath())
sketch, err := sketches.NewSketchFromPath(sketchPath)
if err != nil && req.GetImportDir() == "" && req.GetImportFile() == "" {
return nil, fmt.Errorf("opening sketch: %s", err)
return nil, status.Newf(codes.InvalidArgument, "Sketch not found: %s", err)
}

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

err = runProgramAction(
return &rpc.UploadResponse{}, runProgramAction(
pm,
sketch,
req.GetImportFile(),
Expand All @@ -66,18 +68,14 @@ func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, er
outStream,
errStream,
)
if err != nil {
return nil, err
}
return &rpc.UploadResponse{}, nil
}

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

if req.GetProgrammer() == "" {
return nil, errors.New("programmer not specified")
return nil, status.New(codes.InvalidArgument, "Programmer not specified")
}
_, err := Upload(ctx, &rpc.UploadRequest{
Instance: req.GetInstance(),
Expand All @@ -98,17 +96,17 @@ func runProgramAction(pm *packagemanager.PackageManager,
importFile, importDir, fqbnIn, port string,
programmerID string,
verbose, verify, burnBootloader bool,
outStream, errStream io.Writer) error {
outStream, errStream io.Writer) *status.Status {

if burnBootloader && programmerID == "" {
return fmt.Errorf("no programmer specified for burning bootloader")
return status.New(codes.InvalidArgument, "Programmer not specified")
}

// FIXME: make a specification on how a port is specified via command line
if port == "" && sketch != nil && sketch.Metadata != nil {
deviceURI, err := url.Parse(sketch.Metadata.CPU.Port)
if err != nil {
return fmt.Errorf("invalid Device URL format: %s", err)
return status.Newf(codes.InvalidArgument, "Invalid Device URL format: %s", err)
}
if deviceURI.Scheme == "serial" {
port = deviceURI.Host + deviceURI.Path
Expand All @@ -120,18 +118,18 @@ func runProgramAction(pm *packagemanager.PackageManager,
fqbnIn = sketch.Metadata.CPU.Fqbn
}
if fqbnIn == "" {
return fmt.Errorf("no Fully Qualified Board Name provided")
return status.New(codes.InvalidArgument, "No FQBN (Fully Qualified Board Name) provided")
}
fqbn, err := cores.ParseFQBN(fqbnIn)
if err != nil {
return fmt.Errorf("incorrect FQBN: %s", err)
return status.Newf(codes.InvalidArgument, fmt.Sprintf("Invalid FQBN: %s", err))
}
logrus.WithField("fqbn", fqbn).Tracef("Detected FQBN")

// Find target board and board properties
_, boardPlatform, board, boardProperties, buildPlatform, err := pm.ResolveFQBN(fqbn)
if err != nil {
return fmt.Errorf("incorrect FQBN: %s", err)
return status.Newf(codes.InvalidArgument, "Could not resolve FQBN: %s", err)
}
logrus.
WithField("boardPlatform", boardPlatform).
Expand All @@ -148,7 +146,7 @@ func runProgramAction(pm *packagemanager.PackageManager,
programmer = buildPlatform.Programmers[programmerID]
}
if programmer == nil {
return fmt.Errorf("programmer '%s' not available", programmerID)
return status.Newf(codes.InvalidArgument, "Programmer '%s' not available", programmerID)
}
}

Expand All @@ -173,7 +171,7 @@ func runProgramAction(pm *packagemanager.PackageManager,
if t, ok := props.GetOk(toolProperty); ok {
uploadToolID = t
} else {
return fmt.Errorf("cannot get programmer tool: undefined '%s' property", toolProperty)
return status.Newf(codes.FailedPrecondition, "Cannot get programmer tool: undefined '%s' property", toolProperty)
}
}

Expand All @@ -189,7 +187,7 @@ func runProgramAction(pm *packagemanager.PackageManager,
Trace("Upload tool")

if split := strings.Split(uploadToolID, ":"); len(split) > 2 {
return fmt.Errorf("invalid 'upload.tool' property: %s", uploadToolID)
return status.Newf(codes.FailedPrecondition, "Invalid 'upload.tool' property: %s", uploadToolID)
} else if len(split) == 2 {
uploadToolID = split[1]
uploadToolPlatform = pm.GetInstalledPlatformRelease(
Expand Down Expand Up @@ -228,7 +226,10 @@ func runProgramAction(pm *packagemanager.PackageManager,
}

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

// Set properties for verbose upload
Expand Down Expand Up @@ -276,13 +277,13 @@ func runProgramAction(pm *packagemanager.PackageManager,
if !burnBootloader {
importPath, sketchName, err := determineBuildPathAndSketchName(importFile, importDir, sketch, fqbn)
if err != nil {
return errors.Errorf("retrieving build artifacts: %s", err)
return status.Newf(codes.Internal, "Error finding build artifacts: %s", err)
}
if !importPath.Exist() {
return fmt.Errorf("compiled sketch not found in %s", importPath)
return status.Newf(codes.Internal, "Compiled sketch not found in %s", importPath)
}
if !importPath.IsDir() {
return fmt.Errorf("expected compiled sketch in directory %s, but is a file instead", importPath)
return status.Newf(codes.Internal, "Expected compiled sketch in directory %s, but is a file instead", importPath)
}
uploadProperties.SetPath("build.path", importPath)
uploadProperties.Set("build.project_name", sketchName)
Expand Down Expand Up @@ -359,18 +360,18 @@ func runProgramAction(pm *packagemanager.PackageManager,
// Run recipes for upload
if burnBootloader {
if err := runTool("erase.pattern", uploadProperties, outStream, errStream, verbose); err != nil {
return fmt.Errorf("chip erase error: %s", err)
return status.Newf(codes.Internal, "Failed chip erase: %s", err)
}
if err := runTool("bootloader.pattern", uploadProperties, outStream, errStream, verbose); err != nil {
return fmt.Errorf("burn bootloader error: %s", err)
return status.Newf(codes.Internal, "Failed to burn bootloader: %s", err)
}
} else if programmer != nil {
if err := runTool("program.pattern", uploadProperties, outStream, errStream, verbose); err != nil {
return fmt.Errorf("programming error: %s", err)
return status.Newf(codes.Internal, "Failed programming: %s", err)
}
} else {
if err := runTool("upload.pattern", uploadProperties, outStream, errStream, verbose); err != nil {
return fmt.Errorf("uploading error: %s", err)
return status.Newf(codes.Internal, "Failed uploading: %s", err)
}
}

Expand Down
6 changes: 3 additions & 3 deletions commands/upload/upload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func TestUploadPropertiesComposition(t *testing.T) {
testRunner := func(t *testing.T, test test, verboseVerify bool) {
outStream := &bytes.Buffer{}
errStream := &bytes.Buffer{}
err := runProgramAction(
status := runProgramAction(
pm,
nil, // sketch
"", // importFile
Expand All @@ -197,9 +197,9 @@ func TestUploadPropertiesComposition(t *testing.T) {
verboseVerifyOutput = "quiet noverify"
}
if test.expectedOutput == "FAIL" {
require.Error(t, err)
require.NotNil(t, status)
} else {
require.NoError(t, err)
require.Nil(t, status)
outFiltered := strings.ReplaceAll(outStream.String(), "\r", "")
outFiltered = strings.ReplaceAll(outFiltered, "\\", "/")
require.Contains(t, outFiltered, strings.ReplaceAll(test.expectedOutput, "$$VERBOSE-VERIFY$$", verboseVerifyOutput))
Expand Down
Loading

0 comments on commit 3a17f7f

Please sign in to comment.