Skip to content

Commit

Permalink
Add gRPC interface to CLI settings (#521)
Browse files Browse the repository at this point in the history
* define proto messages and service

* run protoc on settings interface, regenerate code

* add service implementation and tests

* remove test leftovers
  • Loading branch information
Massimiliano Pippi authored and Roberto Sora committed Dec 19, 2019
1 parent d474176 commit 596f4e4
Show file tree
Hide file tree
Showing 17 changed files with 1,015 additions and 307 deletions.
1 change: 1 addition & 0 deletions Taskfile.yml
Expand Up @@ -6,6 +6,7 @@ tasks:
cmds:
- '{{ default "protoc" .PROTOC_BINARY }} --proto_path=rpc --go_out=plugins=grpc,paths=source_relative:rpc ./rpc/commands/*.proto'
- '{{ default "protoc" .PROTOC_BINARY }} --proto_path=rpc --go_out=plugins=grpc,paths=source_relative:rpc ./rpc/monitor/*.proto'
- '{{ default "protoc" .PROTOC_BINARY }} --proto_path=rpc --go_out=plugins=grpc,paths=source_relative:rpc ./rpc/settings/*.proto'

build:
desc: Build the project
Expand Down
1 change: 1 addition & 0 deletions arduino/builder/sketch_test.go
Expand Up @@ -115,6 +115,7 @@ func TestLoadSketchFolderSymlink(t *testing.T) {
symlinkSketchPath := filepath.Join("testdata", t.Name())
srcSketchPath := t.Name() + "Src"
os.Symlink(srcSketchPath, symlinkSketchPath)
defer os.Remove(symlinkSketchPath)
mainFilePath := filepath.Join(symlinkSketchPath, t.Name()+".ino")
s, err := builder.SketchLoad(symlinkSketchPath, "")
require.Nil(t, err)
Expand Down
4 changes: 4 additions & 0 deletions cli/daemon/daemon.go
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/arduino/arduino-cli/commands/daemon"
srv_commands "github.com/arduino/arduino-cli/rpc/commands"
srv_monitor "github.com/arduino/arduino-cli/rpc/monitor"
srv_settings "github.com/arduino/arduino-cli/rpc/settings"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand Down Expand Up @@ -73,6 +74,9 @@ func runDaemonCommand(cmd *cobra.Command, args []string) {
// register the monitors service
srv_monitor.RegisterMonitorServer(s, &daemon.MonitorService{})

// register the settings service
srv_settings.RegisterSettingsServer(s, &daemon.SettingsService{})

if !daemonize {
// When parent process ends terminate also the daemon
go func() {
Expand Down
91 changes: 91 additions & 0 deletions commands/daemon/settings.go
@@ -0,0 +1,91 @@
// This file is part of arduino-cli.
//
// Copyright 2019 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.

package daemon

import (
"context"
"encoding/json"
"errors"
"fmt"

rpc "github.com/arduino/arduino-cli/rpc/settings"
"github.com/spf13/viper"
)

// SettingsService implements the `Settings` service
type SettingsService struct{}

// GetAll returns a message with a string field containing all the settings
// currently in use, marshalled in JSON format.
func (s *SettingsService) GetAll(ctx context.Context, req *rpc.GetAllRequest) (*rpc.RawData, error) {
b, err := json.Marshal(viper.AllSettings())
if err == nil {
return &rpc.RawData{
JsonData: string(b),
}, nil
}

return nil, err
}

// Merge applies multiple settings values at once.
func (s *SettingsService) Merge(ctx context.Context, req *rpc.RawData) (*rpc.MergeResponse, error) {
var toMerge map[string]interface{}
if err := json.Unmarshal([]byte(req.GetJsonData()), &toMerge); err != nil {
return nil, err
}

if err := viper.MergeConfigMap(toMerge); err != nil {
return nil, err
}

return &rpc.MergeResponse{}, nil
}

// GetValue returns a settings value given its key. If the key is not present
// an error will be returned, so that we distinguish empty settings from missing
// ones.
func (s *SettingsService) GetValue(ctx context.Context, req *rpc.GetValueRequest) (*rpc.Value, error) {
key := req.GetKey()
value := &rpc.Value{}

fmt.Println(viper.AllKeys())

if !viper.InConfig(key) {
return nil, errors.New("key not found in settings")
}

b, err := json.Marshal(viper.Get(key))
if err == nil {
value.Key = key
value.JsonData = string(b)
}

return value, err
}

// SetValue updates or set a value for a certain key.
func (s *SettingsService) SetValue(ctx context.Context, val *rpc.Value) (*rpc.SetValueResponse, error) {
key := val.GetKey()
var value interface{}

err := json.Unmarshal([]byte(val.GetJsonData()), &value)
if err == nil {
viper.Set(key, value)
}

return &rpc.SetValueResponse{}, err
}
86 changes: 86 additions & 0 deletions commands/daemon/settings_test.go
@@ -0,0 +1,86 @@
// This file is part of arduino-cli.
//
// Copyright 2019 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.

package daemon

import (
"context"
"encoding/json"
"fmt"
"testing"

"github.com/spf13/viper"

"github.com/arduino/arduino-cli/configuration"
rpc "github.com/arduino/arduino-cli/rpc/settings"
"github.com/stretchr/testify/require"
)

var svc = SettingsService{}

func init() {
configuration.Init("testdata")
}

func reset() {
viper.Reset()
configuration.Init("testdata")
}

func TestGetAll(t *testing.T) {
resp, err := svc.GetAll(context.Background(), &rpc.GetAllRequest{})
require.Nil(t, err)

content, err := json.Marshal(viper.AllSettings())
require.Nil(t, err)

require.Equal(t, string(content), resp.GetJsonData())
}

func TestMerge(t *testing.T) {
bulkSettings := `{"foo": "bar", "daemon":{"port":"420"}}`
_, err := svc.Merge(context.Background(), &rpc.RawData{JsonData: bulkSettings})
require.Nil(t, err)

fmt.Println(viper.AllSettings())
require.Equal(t, "420", viper.GetString("daemon.port"))
require.Equal(t, "bar", viper.GetString("foo"))

reset()
}

func TestGetValue(t *testing.T) {
key := &rpc.GetValueRequest{Key: "daemon"}
resp, err := svc.GetValue(context.Background(), key)
require.Nil(t, err)
require.Equal(t, `{"port":"50051"}`, resp.GetJsonData())
}

func TestGetValueNotFound(t *testing.T) {
key := &rpc.GetValueRequest{Key: "DOESNTEXIST"}
_, err := svc.GetValue(context.Background(), key)
require.NotNil(t, err)
require.Equal(t, `key not found in settings`, err.Error())
}

func TestSetValue(t *testing.T) {
val := &rpc.Value{
Key: "foo",
JsonData: `"bar"`,
}
_, err := svc.SetValue(context.Background(), val)
require.Nil(t, err)
require.Equal(t, "bar", viper.GetString("foo"))
}
16 changes: 16 additions & 0 deletions commands/daemon/testdata/arduino-cli.yml
@@ -0,0 +1,16 @@
board_manager:
additional_urls:
- http://foobar.com
- http://example.com

daemon:
port: "50051"

directories:
data: /home/massi/.arduino15
downloads: /home/massi/.arduino15/staging

logging:
file: ""
format: text
level: info
1 change: 1 addition & 0 deletions go.sum
Expand Up @@ -124,6 +124,7 @@ github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
Expand Down
84 changes: 42 additions & 42 deletions rpc/commands/board.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 596f4e4

Please sign in to comment.