diff --git a/arduino/cli/commander.go b/arduino/cli/commander.go index bd772535..f7590ef5 100644 --- a/arduino/cli/commander.go +++ b/arduino/cli/commander.go @@ -3,7 +3,6 @@ package cli import ( "context" "fmt" - "io/ioutil" "os" "path/filepath" @@ -24,8 +23,8 @@ type commander struct { // programmatically call arduino-cli commands. // It directly imports the golang packages of the arduino-cli. func NewCommander() (arduino.Commander, error) { - // Discard arduino-cli log messages - logrus.SetOutput(ioutil.Discard) + // Discard arduino-cli log info messages + logrus.SetLevel(logrus.PanicLevel) // Initialize arduino-cli configuration configuration.Settings = configuration.Init(configuration.FindConfigFileInArgsOrWorkingDirectory(os.Args)) // Create arduino-cli instance, needed to execute arduino-cli commands @@ -35,6 +34,8 @@ func NewCommander() (arduino.Commander, error) { return nil, err } + // Re-enable info level log + logrus.SetLevel(logrus.InfoLevel) cmd := &commander{inst} return cmd, nil } @@ -62,10 +63,10 @@ func (c *commander) UploadBin(fqbn, bin, port string) error { Verbose: false, } - if _, err := upload.Upload(context.Background(), req, os.Stdout, os.Stderr); err != nil { + l := logrus.StandardLogger().WithField("source", "arduino-cli").Writer() + if _, err := upload.Upload(context.Background(), req, l, l); err != nil { err = fmt.Errorf("%s: %w", "uploading binary", err) return err } - return nil } diff --git a/cli/cli.go b/cli/cli.go new file mode 100644 index 00000000..afb1ddf5 --- /dev/null +++ b/cli/cli.go @@ -0,0 +1,70 @@ +package cli + +import ( + "fmt" + "io/ioutil" + "os" + "strings" + + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/arduino-cli/cli/feedback" + "github.com/arduino/iot-cloud-cli/cli/config" + "github.com/arduino/iot-cloud-cli/cli/device" + "github.com/arduino/iot-cloud-cli/cli/thing" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var cliFlags struct { + verbose bool + outputFormat string +} + +func Execute() { + cli := &cobra.Command{ + Use: "arduino-cloud-cli", + Short: "Arduino Cloud CLI.", + Long: "Arduino Cloud Command Line Interface (arduino-cloud-cli).", + PersistentPreRun: preRun, + } + + cli.AddCommand(config.NewCommand()) + cli.AddCommand(device.NewCommand()) + cli.AddCommand(thing.NewCommand()) + + cli.PersistentFlags().BoolVarP(&cliFlags.verbose, "verbose", "v", false, "Print the logs on the standard output.") + cli.PersistentFlags().StringVar(&cliFlags.outputFormat, "format", "text", "The output format, can be {text|json}.") + + if err := cli.Execute(); err != nil { + fmt.Fprintln(os.Stderr, err) + } +} + +func parseFormatString(arg string) (feedback.OutputFormat, bool) { + f, found := map[string]feedback.OutputFormat{ + "json": feedback.JSON, + "text": feedback.Text, + }[arg] + + return f, found +} + +func preRun(cmd *cobra.Command, args []string) { + logrus.SetOutput(ioutil.Discard) + // enable log only if verbose flag is passed + if cliFlags.verbose { + logrus.SetLevel(logrus.InfoLevel) + logrus.SetOutput(os.Stdout) + } + + // normalize the format strings + cliFlags.outputFormat = strings.ToLower(cliFlags.outputFormat) + // check the right output format was passed + format, found := parseFormatString(cliFlags.outputFormat) + if !found { + feedback.Error("Invalid output format: " + cliFlags.outputFormat) + os.Exit(errorcodes.ErrBadCall) + } + // use the output format to configure the Feedback + feedback.SetFormat(format) +} diff --git a/cli/config/config.go b/cli/config/config.go index ddd856f0..f2de8b18 100644 --- a/cli/config/config.go +++ b/cli/config/config.go @@ -1,12 +1,14 @@ package config import ( - "fmt" + "os" "strings" + "github.com/arduino/arduino-cli/cli/errorcodes" "github.com/arduino/arduino-cli/cli/feedback" paths "github.com/arduino/go-paths-helper" "github.com/arduino/iot-cloud-cli/command/config" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -22,7 +24,7 @@ func NewCommand() *cobra.Command { Use: "config", Short: "Set the configuration file", Long: "Set the configuration file to access Arduino IoT Cloud", - RunE: runConfigCommand, + Run: runConfigCommand, } configCommand.Flags().StringVarP(&configFlags.file, "file", "f", "", "Existing configuration yaml file") configCommand.Flags().StringVarP(&configFlags.client, "client", "c", "", "Client ID") @@ -30,9 +32,10 @@ func NewCommand() *cobra.Command { return configCommand } -func runConfigCommand(cmd *cobra.Command, args []string) error { +func runConfigCommand(cmd *cobra.Command, args []string) { if configFlags.file == "" && (configFlags.client == "" || configFlags.secret == "") { - return fmt.Errorf("%s", "Provide either a yaml file or credentials\n") + feedback.Error("Error during config: provide either a yaml file or credentials") + os.Exit(errorcodes.ErrGeneric) } conf := viper.New() @@ -45,8 +48,8 @@ func runConfigCommand(cmd *cobra.Command, args []string) error { conf.AddConfigPath(".") err := conf.ReadInConfig() if err != nil { - feedback.Errorf("Fatal error config file: %v", err) - return err + feedback.Errorf("Error during config: fatal error config file: %v", err) + os.Exit(errorcodes.ErrGeneric) } } else { @@ -56,10 +59,9 @@ func runConfigCommand(cmd *cobra.Command, args []string) error { err := config.Config(conf) if err != nil { - feedback.Errorf("Storing config file: %v", err) - return err + feedback.Errorf("Error during config: storing config file: %v", err) + os.Exit(errorcodes.ErrGeneric) } - fmt.Println("Configuration file updated") - return nil + logrus.Info("Configuration file updated") } diff --git a/cli/device/create.go b/cli/device/create.go index 49c2302e..c472206f 100644 --- a/cli/device/create.go +++ b/cli/device/create.go @@ -2,8 +2,12 @@ package device import ( "fmt" + "os" + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/arduino-cli/cli/feedback" "github.com/arduino/iot-cloud-cli/command/device" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -18,7 +22,7 @@ func initCreateCommand() *cobra.Command { Use: "create", Short: "Create a device", Long: "Create a device for Arduino IoT Cloud", - RunE: runCreateCommand, + Run: runCreateCommand, } createCommand.Flags().StringVarP(&createFlags.port, "port", "p", "", "Device port") createCommand.Flags().StringVarP(&createFlags.name, "name", "n", "", "Device name") @@ -27,8 +31,8 @@ func initCreateCommand() *cobra.Command { return createCommand } -func runCreateCommand(cmd *cobra.Command, args []string) error { - fmt.Printf("Creating device with name %s\n", createFlags.name) +func runCreateCommand(cmd *cobra.Command, args []string) { + logrus.Infof("Creating device with name %s\n", createFlags.name) params := &device.CreateParams{ Name: createFlags.name, @@ -40,11 +44,30 @@ func runCreateCommand(cmd *cobra.Command, args []string) error { params.Fqbn = &createFlags.fqbn } - devID, err := device.Create(params) + dev, err := device.Create(params) if err != nil { - return err + feedback.Errorf("Error during device create: %v", err) + os.Exit(errorcodes.ErrGeneric) } - fmt.Printf("IoT Cloud device created with ID: %s\n", devID) - return nil + feedback.PrintResult(createResult{dev}) +} + +type createResult struct { + device *device.DeviceInfo +} + +func (r createResult) Data() interface{} { + return r.device +} + +func (r createResult) String() string { + return fmt.Sprintf( + "name: %s\nid: %s\nboard: %s\nserial-number: %s\nfqbn: %s", + r.device.Name, + r.device.ID, + r.device.Board, + r.device.Serial, + r.device.FQBN, + ) } diff --git a/cli/device/delete.go b/cli/device/delete.go index f71f8e8e..96711106 100644 --- a/cli/device/delete.go +++ b/cli/device/delete.go @@ -1,9 +1,12 @@ package device import ( - "fmt" + "os" + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/arduino-cli/cli/feedback" "github.com/arduino/iot-cloud-cli/command/device" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -16,22 +19,22 @@ func initDeleteCommand() *cobra.Command { Use: "delete", Short: "Delete a device", Long: "Delete a device from Arduino IoT Cloud", - RunE: runDeleteCommand, + Run: runDeleteCommand, } deleteCommand.Flags().StringVarP(&deleteFlags.id, "id", "i", "", "Device ID") deleteCommand.MarkFlagRequired("id") return deleteCommand } -func runDeleteCommand(cmd *cobra.Command, args []string) error { - fmt.Printf("Deleting device %s\n", deleteFlags.id) +func runDeleteCommand(cmd *cobra.Command, args []string) { + logrus.Infof("Deleting device %s\n", deleteFlags.id) params := &device.DeleteParams{ID: deleteFlags.id} err := device.Delete(params) if err != nil { - return err + feedback.Errorf("Error during device delete: %v", err) + os.Exit(errorcodes.ErrGeneric) } - fmt.Println("Device successfully deleted") - return nil + logrus.Info("Device successfully deleted") } diff --git a/cli/device/list.go b/cli/device/list.go index e7f86540..6e959410 100644 --- a/cli/device/list.go +++ b/cli/device/list.go @@ -1,11 +1,13 @@ package device import ( - "fmt" + "os" + "github.com/arduino/arduino-cli/cli/errorcodes" "github.com/arduino/arduino-cli/cli/feedback" "github.com/arduino/arduino-cli/table" "github.com/arduino/iot-cloud-cli/command/device" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -14,33 +16,32 @@ func initListCommand() *cobra.Command { Use: "list", Short: "List devices", Long: "List devices on Arduino IoT Cloud", - RunE: runListCommand, + Run: runListCommand, } return listCommand } -func runListCommand(cmd *cobra.Command, args []string) error { - fmt.Println("Listing devices") +func runListCommand(cmd *cobra.Command, args []string) { + logrus.Info("Listing devices") devs, err := device.List() if err != nil { - return err + feedback.Errorf("Error during device list: %v", err) + os.Exit(errorcodes.ErrGeneric) } - feedback.PrintResult(result{devs}) - - return nil + feedback.PrintResult(listResult{devs}) } -type result struct { +type listResult struct { devices []device.DeviceInfo } -func (r result) Data() interface{} { +func (r listResult) Data() interface{} { return r.devices } -func (r result) String() string { +func (r listResult) String() string { if len(r.devices) == 0 { return "No devices found." } diff --git a/cli/root.go b/cli/root.go deleted file mode 100644 index ccc75821..00000000 --- a/cli/root.go +++ /dev/null @@ -1,22 +0,0 @@ -package cli - -import ( - "fmt" - "os" - - "github.com/arduino/iot-cloud-cli/cli/config" - "github.com/arduino/iot-cloud-cli/cli/device" - "github.com/arduino/iot-cloud-cli/cli/thing" - "github.com/spf13/cobra" -) - -func Execute() { - rootCmd := &cobra.Command{} - rootCmd.AddCommand(config.NewCommand()) - rootCmd.AddCommand(device.NewCommand()) - rootCmd.AddCommand(thing.NewCommand()) - - if err := rootCmd.Execute(); err != nil { - fmt.Fprintln(os.Stderr, err) - } -} diff --git a/cli/thing/bind.go b/cli/thing/bind.go index 5f940655..e3198329 100644 --- a/cli/thing/bind.go +++ b/cli/thing/bind.go @@ -1,9 +1,12 @@ package thing import ( - "fmt" + "os" + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/arduino-cli/cli/feedback" "github.com/arduino/iot-cloud-cli/command/thing" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -17,7 +20,7 @@ func initBindCommand() *cobra.Command { Use: "bind", Short: "Bind a thing to a device", Long: "Bind a thing to a device on Arduino IoT Cloud", - RunE: runBindCommand, + Run: runBindCommand, } bindCommand.Flags().StringVarP(&bindFlags.id, "id", "i", "", "Thing ID") bindCommand.Flags().StringVarP(&bindFlags.deviceID, "device-id", "d", "", "Device ID") @@ -26,8 +29,8 @@ func initBindCommand() *cobra.Command { return bindCommand } -func runBindCommand(cmd *cobra.Command, args []string) error { - fmt.Printf("Binding thing %s to device%s\n", bindFlags.id, bindFlags.deviceID) +func runBindCommand(cmd *cobra.Command, args []string) { + logrus.Infof("Binding thing %s to device %s\n", bindFlags.id, bindFlags.deviceID) params := &thing.BindParams{ ID: bindFlags.id, @@ -35,9 +38,9 @@ func runBindCommand(cmd *cobra.Command, args []string) error { } err := thing.Bind(params) if err != nil { - return err + feedback.Errorf("Error during thing bind: %v", err) + os.Exit(errorcodes.ErrGeneric) } - fmt.Println("Thing-Device bound successfully updated") - return nil + logrus.Info("Thing-Device bound successfully updated") } diff --git a/cli/thing/clone.go b/cli/thing/clone.go index c976f69b..4e9aed8c 100644 --- a/cli/thing/clone.go +++ b/cli/thing/clone.go @@ -2,8 +2,13 @@ package thing import ( "fmt" + "os" + "strings" + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/arduino-cli/cli/feedback" "github.com/arduino/iot-cloud-cli/command/thing" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -17,7 +22,7 @@ func initCloneCommand() *cobra.Command { Use: "clone", Short: "Clone a thing", Long: "Clone a thing for Arduino IoT Cloud", - RunE: runCloneCommand, + Run: runCloneCommand, } cloneCommand.Flags().StringVarP(&cloneFlags.name, "name", "n", "", "Thing name") cloneCommand.Flags().StringVarP(&cloneFlags.cloneID, "clone-id", "c", "", "ID of Thing to be cloned") @@ -26,19 +31,37 @@ func initCloneCommand() *cobra.Command { return cloneCommand } -func runCloneCommand(cmd *cobra.Command, args []string) error { - fmt.Printf("Cloning thing %s into a new thing called %s\n", cloneFlags.cloneID, cloneFlags.name) +func runCloneCommand(cmd *cobra.Command, args []string) { + logrus.Infof("Cloning thing %s into a new thing called %s\n", cloneFlags.cloneID, cloneFlags.name) params := &thing.CloneParams{ Name: cloneFlags.name, CloneID: cloneFlags.cloneID, } - thingID, err := thing.Clone(params) + thing, err := thing.Clone(params) if err != nil { - return err + feedback.Errorf("Error during thing clone: %v", err) + os.Exit(errorcodes.ErrGeneric) } - fmt.Printf("IoT Cloud thing created with ID: %s\n", thingID) - return nil + feedback.PrintResult(cloneResult{thing}) +} + +type cloneResult struct { + thing *thing.ThingInfo +} + +func (r cloneResult) Data() interface{} { + return r.thing +} + +func (r cloneResult) String() string { + return fmt.Sprintf( + "name: %s\nid: %s\ndevice-id: %s\nvariables: %s", + r.thing.Name, + r.thing.ID, + r.thing.DeviceID, + strings.Join(r.thing.Variables, ", "), + ) } diff --git a/cli/thing/create.go b/cli/thing/create.go index fd3545da..78bd20bd 100644 --- a/cli/thing/create.go +++ b/cli/thing/create.go @@ -2,8 +2,13 @@ package thing import ( "fmt" + "os" + "strings" + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/arduino-cli/cli/feedback" "github.com/arduino/iot-cloud-cli/command/thing" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -17,7 +22,7 @@ func initCreateCommand() *cobra.Command { Use: "create", Short: "Create a thing from a template", Long: "Create a thing from a template for Arduino IoT Cloud", - RunE: runCreateCommand, + Run: runCreateCommand, } createCommand.Flags().StringVarP(&createFlags.name, "name", "n", "", "Thing name") createCommand.Flags().StringVarP( @@ -31,8 +36,8 @@ func initCreateCommand() *cobra.Command { return createCommand } -func runCreateCommand(cmd *cobra.Command, args []string) error { - fmt.Printf("Creating thing from template %s\n", createFlags.template) +func runCreateCommand(cmd *cobra.Command, args []string) { + logrus.Infof("Creating thing from template %s\n", createFlags.template) params := &thing.CreateParams{ Template: createFlags.template, @@ -41,11 +46,29 @@ func runCreateCommand(cmd *cobra.Command, args []string) error { params.Name = &createFlags.name } - thingID, err := thing.Create(params) + thing, err := thing.Create(params) if err != nil { - return err + feedback.Errorf("Error during thing create: %v", err) + os.Exit(errorcodes.ErrGeneric) } - fmt.Printf("IoT Cloud thing created with ID: %s\n", thingID) - return nil + feedback.PrintResult(createResult{thing}) +} + +type createResult struct { + thing *thing.ThingInfo +} + +func (r createResult) Data() interface{} { + return r.thing +} + +func (r createResult) String() string { + return fmt.Sprintf( + "name: %s\nid: %s\ndevice-id: %s\nvariables: %s", + r.thing.Name, + r.thing.ID, + r.thing.DeviceID, + strings.Join(r.thing.Variables, ", "), + ) } diff --git a/cli/thing/delete.go b/cli/thing/delete.go index ef922edc..e6cdecf7 100644 --- a/cli/thing/delete.go +++ b/cli/thing/delete.go @@ -1,9 +1,12 @@ package thing import ( - "fmt" + "os" + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/arduino-cli/cli/feedback" "github.com/arduino/iot-cloud-cli/command/thing" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -16,22 +19,22 @@ func initDeleteCommand() *cobra.Command { Use: "delete", Short: "Delete a thing", Long: "Delete a thing from Arduino IoT Cloud", - RunE: runDeleteCommand, + Run: runDeleteCommand, } deleteCommand.Flags().StringVarP(&deleteFlags.id, "id", "i", "", "Thing ID") deleteCommand.MarkFlagRequired("id") return deleteCommand } -func runDeleteCommand(cmd *cobra.Command, args []string) error { - fmt.Printf("Deleting thing %s\n", deleteFlags.id) +func runDeleteCommand(cmd *cobra.Command, args []string) { + logrus.Infof("Deleting thing %s\n", deleteFlags.id) params := &thing.DeleteParams{ID: deleteFlags.id} err := thing.Delete(params) if err != nil { - return err + feedback.Errorf("Error during thing delete: %v", err) + os.Exit(errorcodes.ErrGeneric) } - fmt.Println("Thing successfully deleted") - return nil + logrus.Info("Thing successfully deleted") } diff --git a/cli/thing/extract.go b/cli/thing/extract.go index ba99971a..2dc0cf29 100644 --- a/cli/thing/extract.go +++ b/cli/thing/extract.go @@ -1,9 +1,12 @@ package thing import ( - "fmt" + "os" + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/arduino-cli/cli/feedback" "github.com/arduino/iot-cloud-cli/command/thing" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -18,7 +21,7 @@ func initExtractCommand() *cobra.Command { Use: "extract", Short: "Extract a template from a thing", Long: "Extract a template from a Arduino IoT Cloud thing and save it in a file", - RunE: runExtractCommand, + Run: runExtractCommand, } extractCommand.Flags().StringVarP(&extractFlags.id, "id", "i", "", "Thing ID") extractCommand.Flags().StringVarP(&extractFlags.outfile, "outfile", "o", "", "Template file destination path") @@ -33,8 +36,8 @@ func initExtractCommand() *cobra.Command { return extractCommand } -func runExtractCommand(cmd *cobra.Command, args []string) error { - fmt.Printf("Extracting template from thing %s\n", extractFlags.id) +func runExtractCommand(cmd *cobra.Command, args []string) { + logrus.Infof("Extracting template from thing %s\n", extractFlags.id) params := &thing.ExtractParams{ ID: extractFlags.id, @@ -46,9 +49,9 @@ func runExtractCommand(cmd *cobra.Command, args []string) error { err := thing.Extract(params) if err != nil { - return err + feedback.Errorf("Error during template extraction: %v", err) + os.Exit(errorcodes.ErrGeneric) } - fmt.Println("Template successfully extracted") - return nil + logrus.Info("Template successfully extracted") } diff --git a/cli/thing/list.go b/cli/thing/list.go index 961e1168..ee9a2137 100644 --- a/cli/thing/list.go +++ b/cli/thing/list.go @@ -1,12 +1,14 @@ package thing import ( - "fmt" + "os" "strings" + "github.com/arduino/arduino-cli/cli/errorcodes" "github.com/arduino/arduino-cli/cli/feedback" "github.com/arduino/arduino-cli/table" "github.com/arduino/iot-cloud-cli/command/thing" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -21,7 +23,7 @@ func initListCommand() *cobra.Command { Use: "list", Short: "List things", Long: "List things on Arduino IoT Cloud", - RunE: runListCommand, + Run: runListCommand, } // list only the things corresponding to the passed ids listCommand.Flags().StringSliceVarP(&listFlags.ids, "ids", "i", []string{}, "List of thing IDs to be retrieved") @@ -31,8 +33,8 @@ func initListCommand() *cobra.Command { return listCommand } -func runListCommand(cmd *cobra.Command, args []string) error { - fmt.Println("Listing things") +func runListCommand(cmd *cobra.Command, args []string) { + logrus.Info("Listing things") params := &thing.ListParams{ IDs: listFlags.ids, @@ -44,11 +46,11 @@ func runListCommand(cmd *cobra.Command, args []string) error { things, err := thing.List(params) if err != nil { - return err + feedback.Errorf("Error during thing list: %v", err) + os.Exit(errorcodes.ErrGeneric) } feedback.PrintResult(result{things}) - return nil } type result struct { diff --git a/command/device/create.go b/command/device/create.go index 7b3e5673..0a6dce21 100644 --- a/command/device/create.go +++ b/command/device/create.go @@ -9,6 +9,7 @@ import ( "github.com/arduino/iot-cloud-cli/arduino/cli" "github.com/arduino/iot-cloud-cli/internal/config" "github.com/arduino/iot-cloud-cli/internal/iot" + "github.com/sirupsen/logrus" ) // CreateParams contains the parameters needed @@ -22,7 +23,7 @@ type CreateParams struct { Fqbn *string } -type device struct { +type board struct { fqbn string serial string dType string @@ -31,66 +32,73 @@ type device struct { // Create command is used to provision a new arduino device // and to add it to Arduino IoT Cloud. -func Create(params *CreateParams) (string, error) { +func Create(params *CreateParams) (*DeviceInfo, error) { comm, err := cli.NewCommander() if err != nil { - return "", err + return nil, err } ports, err := comm.BoardList() if err != nil { - return "", err + return nil, err } - dev := deviceFromPorts(ports, params) - if dev == nil { - err = errors.New("no device found") - return "", err + board := boardFromPorts(ports, params) + if board == nil { + err = errors.New("no board found") + return nil, err } conf, err := config.Retrieve() if err != nil { - return "", err + return nil, err } iotClient, err := iot.NewClient(conf.Client, conf.Secret) if err != nil { - return "", err + return nil, err } - fmt.Println("Creating a new device on the cloud") - devID, err := iotClient.DeviceCreate(dev.fqbn, params.Name, dev.serial, dev.dType) + logrus.Info("Creating a new device on the cloud") + dev, err := iotClient.DeviceCreate(board.fqbn, params.Name, board.serial, board.dType) if err != nil { - return "", err + return nil, err } prov := &provision{ Commander: comm, Client: iotClient, - dev: dev, - id: devID} - err = prov.run() - if err != nil { + board: board, + id: dev.Id, + } + if err = prov.run(); err != nil { // TODO: retry to delete the device if it returns an error. // In alternative: encapsulate also this error. - iotClient.DeviceDelete(devID) + iotClient.DeviceDelete(dev.Id) err = fmt.Errorf("%s: %w", "cannot provision device", err) - return "", err + return nil, err } - return devID, nil + devInfo := &DeviceInfo{ + Name: dev.Name, + ID: dev.Id, + Board: dev.Type, + Serial: dev.Serial, + FQBN: dev.Fqbn, + } + return devInfo, nil } -// deviceFromPorts returns a board that matches all the criteria -// passed in. If no criteria are passed, it returns the first device found. -func deviceFromPorts(ports []*rpc.DetectedPort, params *CreateParams) *device { +// boardFromPorts returns a board that matches all the criteria +// passed in. If no criteria are passed, it returns the first board found. +func boardFromPorts(ports []*rpc.DetectedPort, params *CreateParams) *board { for _, port := range ports { if portFilter(port, params) { continue } - board := boardFilter(port.Boards, params) - if board != nil { - t := strings.Split(board.Fqbn, ":")[2] - dev := &device{board.Fqbn, port.SerialNumber, t, port.Address} - return dev + boardFound := boardFilter(port.Boards, params) + if boardFound != nil { + t := strings.Split(boardFound.Fqbn, ":")[2] + b := &board{boardFound.Fqbn, port.SerialNumber, t, port.Address} + return b } } diff --git a/command/device/create_test.go b/command/device/create_test.go index 4f6f5ae0..a52d61eb 100644 --- a/command/device/create_test.go +++ b/command/device/create_test.go @@ -39,26 +39,26 @@ func stringPointer(s string) *string { return &s } -func TestDeviceFromPorts(t *testing.T) { +func TestBoardFromPorts(t *testing.T) { tests := []struct { name string filter *CreateParams ports []*rpc.DetectedPort - want *device + want *board }{ { name: "port-filter", filter: &CreateParams{Fqbn: nil, Port: stringPointer("ACM1")}, ports: portsTwoBoards, - want: &device{fqbn: "arduino:avr:uno", port: "ACM1"}, + want: &board{fqbn: "arduino:avr:uno", port: "ACM1"}, }, { name: "fqbn-filter", filter: &CreateParams{Fqbn: stringPointer("arduino:avr:uno"), Port: nil}, ports: portsTwoBoards, - want: &device{fqbn: "arduino:avr:uno", port: "ACM1"}, + want: &board{fqbn: "arduino:avr:uno", port: "ACM1"}, }, { @@ -72,8 +72,8 @@ func TestDeviceFromPorts(t *testing.T) { name: "no-filter", filter: &CreateParams{Fqbn: nil, Port: nil}, ports: portsTwoBoards, - // first device found is selected - want: &device{fqbn: "arduino:samd:nano_33_iot", port: "ACM0"}, + // first board found is selected + want: &board{fqbn: "arduino:samd:nano_33_iot", port: "ACM0"}, }, { @@ -87,7 +87,7 @@ func TestDeviceFromPorts(t *testing.T) { name: "both-filter-found", filter: &CreateParams{Fqbn: stringPointer("arduino:avr:uno"), Port: stringPointer("ACM1")}, ports: portsTwoBoards, - want: &device{fqbn: "arduino:avr:uno", port: "ACM1"}, + want: &board{fqbn: "arduino:avr:uno", port: "ACM1"}, }, { @@ -100,19 +100,19 @@ func TestDeviceFromPorts(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := deviceFromPorts(tt.ports, tt.filter) + got := boardFromPorts(tt.ports, tt.filter) if got == nil && tt.want == nil { return } else if got != nil && tt.want == nil { - t.Errorf("Expected nil device, received not nil device with port %s and fqbn %s", got.port, got.fqbn) + t.Errorf("Expected nil board, received not nil board with port %s and fqbn %s", got.port, got.fqbn) } else if got == nil && tt.want != nil { - t.Errorf("Expected not nil device with port %s and fqbn %s, received a nil device", tt.want.port, tt.want.fqbn) + t.Errorf("Expected not nil board with port %s and fqbn %s, received a nil board", tt.want.port, tt.want.fqbn) } else if got.port != tt.want.port || got.fqbn != tt.want.fqbn { - t.Errorf("Expected device with port %s and fqbn %s, received device with port %s and fqbn %s", + t.Errorf("Expected board with port %s and fqbn %s, received board with port %s and fqbn %s", tt.want.port, tt.want.fqbn, got.port, got.fqbn) } }) diff --git a/command/device/list.go b/command/device/list.go index 82e8e438..d568c13f 100644 --- a/command/device/list.go +++ b/command/device/list.go @@ -8,11 +8,11 @@ import ( // DeviceInfo contains the most interesting // parameters of an Arduino IoT Cloud device. type DeviceInfo struct { - Name string - ID string - Board string - Serial string - FQBN string + Name string `json:"name"` + ID string `json:"id"` + Board string `json:"board"` + Serial string `json:"serial-number"` + FQBN string `json:"fqbn"` } // List command is used to list diff --git a/command/device/provision.go b/command/device/provision.go index c8fb3213..017b2d29 100644 --- a/command/device/provision.go +++ b/command/device/provision.go @@ -12,14 +12,15 @@ import ( "github.com/arduino/iot-cloud-cli/arduino" "github.com/arduino/iot-cloud-cli/internal/iot" "github.com/arduino/iot-cloud-cli/internal/serial" + "github.com/sirupsen/logrus" ) type provision struct { arduino.Commander iot.Client - ser *serial.Serial - dev *device - id string + ser *serial.Serial + board *board + id string } type binFile struct { @@ -31,49 +32,49 @@ type binFile struct { } func (p provision) run() error { - bin, err := downloadProvisioningFile(p.dev.fqbn) + bin, err := downloadProvisioningFile(p.board.fqbn) if err != nil { return err } - fmt.Printf("\n%s\n", "Uploading provisioning sketch on the device") + logrus.Infof("%s\n", "Uploading provisioning sketch on the board") time.Sleep(500 * time.Millisecond) // Try to upload the provisioning sketch - errMsg := "Error while uploading the provisioning sketch: " + errMsg := "Error while uploading the provisioning sketch" err = retry(5, time.Millisecond*1000, errMsg, func() error { //serialutils.Reset(dev.port, true, nil) - return p.UploadBin(p.dev.fqbn, bin, p.dev.port) + return p.UploadBin(p.board.fqbn, bin, p.board.port) }) if err != nil { return err } - fmt.Printf("\n%s\n", "Connecting to the device through serial port") - // Try to connect to device through the serial port + logrus.Infof("%s\n", "Connecting to the board through serial port") + // Try to connect to board through the serial port time.Sleep(1500 * time.Millisecond) p.ser = serial.NewSerial() - errMsg = "Error while connecting to the device: " + errMsg = "Error while connecting to the board" err = retry(5, time.Millisecond*1000, errMsg, func() error { - return p.ser.Connect(p.dev.port) + return p.ser.Connect(p.board.port) }) if err != nil { return err } defer p.ser.Close() - fmt.Printf("%s\n\n", "Connected to device") + logrus.Infof("%s\n\n", "Connected to board") - // Send configuration commands to the device - err = p.configDev() + // Send configuration commands to the board + err = p.configBoard() if err != nil { return err } - fmt.Printf("%s\n\n", "Device provisioning successful") + logrus.Infof("%s\n\n", "Device provisioning successful") return nil } -func (p provision) configDev() error { - fmt.Println("Receiving the certificate") +func (p provision) configBoard() error { + logrus.Info("Receiving the certificate") csr, err := p.ser.SendReceive(serial.CSR, []byte(p.id)) if err != nil { return err @@ -83,48 +84,48 @@ func (p provision) configDev() error { return err } - fmt.Println("Requesting begin storage") + logrus.Info("Requesting begin storage") err = p.ser.Send(serial.BeginStorage, nil) if err != nil { return err } s := strconv.Itoa(cert.NotBefore.Year()) - fmt.Println("Sending year: ", s) + logrus.Info("Sending year: ", s) err = p.ser.Send(serial.SetYear, []byte(s)) if err != nil { return err } s = fmt.Sprintf("%02d", int(cert.NotBefore.Month())) - fmt.Println("Sending month: ", s) + logrus.Info("Sending month: ", s) err = p.ser.Send(serial.SetMonth, []byte(s)) if err != nil { return err } s = fmt.Sprintf("%02d", cert.NotBefore.Day()) - fmt.Println("Sending day: ", s) + logrus.Info("Sending day: ", s) err = p.ser.Send(serial.SetDay, []byte(s)) if err != nil { return err } s = fmt.Sprintf("%02d", cert.NotBefore.Hour()) - fmt.Println("Sending hour: ", s) + logrus.Info("Sending hour: ", s) err = p.ser.Send(serial.SetHour, []byte(s)) if err != nil { return err } s = strconv.Itoa(31) - fmt.Println("Sending validity: ", s) + logrus.Info("Sending validity: ", s) err = p.ser.Send(serial.SetValidity, []byte(s)) if err != nil { return err } - fmt.Println("Sending certificate serial") + logrus.Info("Sending certificate serial") b, err := hex.DecodeString(cert.Serial) if err != nil { err = fmt.Errorf("%s: %w", "decoding certificate serial", err) @@ -135,7 +136,7 @@ func (p provision) configDev() error { return err } - fmt.Println("Sending certificate authority key") + logrus.Info("Sending certificate authority key") b, err = hex.DecodeString(cert.AuthorityKeyIdentifier) if err != nil { err = fmt.Errorf("%s: %w", "decoding certificate authority key id", err) @@ -146,7 +147,7 @@ func (p provision) configDev() error { return err } - fmt.Println("Sending certificate signature") + logrus.Info("Sending certificate signature") b, err = hex.DecodeString(cert.SignatureAsn1X + cert.SignatureAsn1Y) if err != nil { err = fmt.Errorf("%s: %w", "decoding certificate signature", err) @@ -158,14 +159,14 @@ func (p provision) configDev() error { } time.Sleep(time.Second) - fmt.Println("Requesting end storage") + logrus.Info("Requesting end storage") err = p.ser.Send(serial.EndStorage, nil) if err != nil { return err } time.Sleep(2 * time.Second) - fmt.Println("Requesting certificate reconstruction") + logrus.Info("Requesting certificate reconstruction") err = p.ser.Send(serial.ReconstructCert, nil) if err != nil { return err @@ -254,7 +255,7 @@ func retry(tries int, sleep time.Duration, errMsg string, fun func() error) erro if err == nil { break } - fmt.Println(errMsg, err.Error(), "\nTrying again...") + logrus.Warningf("%s: %s: %s", errMsg, err.Error(), "\nTrying again...") time.Sleep(sleep) } return err diff --git a/command/thing/clone.go b/command/thing/clone.go index cbf239f5..abac6046 100644 --- a/command/thing/clone.go +++ b/command/thing/clone.go @@ -17,29 +17,29 @@ type CloneParams struct { } // Clone allows to create a new thing from an already existing one -func Clone(params *CloneParams) (string, error) { +func Clone(params *CloneParams) (*ThingInfo, error) { conf, err := config.Retrieve() if err != nil { - return "", err + return nil, err } iotClient, err := iot.NewClient(conf.Client, conf.Secret) if err != nil { - return "", err + return nil, err } thing, err := retrieve(iotClient, params.CloneID) if err != nil { - return "", err + return nil, err } thing.Name = params.Name force := true - thingID, err := iotClient.ThingCreate(thing, force) + newThing, err := iotClient.ThingCreate(thing, force) if err != nil { - return "", err + return nil, err } - return thingID, nil + return getThingInfo(newThing), nil } func retrieve(client iot.Client, thingID string) (*iotclient.Thing, error) { diff --git a/command/thing/create.go b/command/thing/create.go index 22feb62c..abd9335a 100644 --- a/command/thing/create.go +++ b/command/thing/create.go @@ -22,19 +22,19 @@ type CreateParams struct { } // Create allows to create a new thing -func Create(params *CreateParams) (string, error) { +func Create(params *CreateParams) (*ThingInfo, error) { conf, err := config.Retrieve() if err != nil { - return "", err + return nil, err } iotClient, err := iot.NewClient(conf.Client, conf.Secret) if err != nil { - return "", err + return nil, err } thing, err := loadTemplate(params.Template) if err != nil { - return "", err + return nil, err } // Name passed as parameter has priority over name from template @@ -43,16 +43,16 @@ func Create(params *CreateParams) (string, error) { } // If name is not specified in the template, it should be passed as parameter if thing.Name == "" { - return "", errors.New("thing name not specified") + return nil, errors.New("thing name not specified") } force := true - thingID, err := iotClient.ThingCreate(thing, force) + newThing, err := iotClient.ThingCreate(thing, force) if err != nil { - return "", err + return nil, err } - return thingID, nil + return getThingInfo(newThing), nil } func loadTemplate(file string) (*iotclient.Thing, error) { diff --git a/command/thing/list.go b/command/thing/list.go index 2de5a255..c89639be 100644 --- a/command/thing/list.go +++ b/command/thing/list.go @@ -5,15 +5,6 @@ import ( "github.com/arduino/iot-cloud-cli/internal/iot" ) -// ThingInfo contains the main parameters of -// an Arduino IoT Cloud thing. -type ThingInfo struct { - Name string - ID string - DeviceID string - Variables []string -} - // ListParams contains the optional parameters needed // to filter the things to be listed. // If IDs is valid, only things belonging to that list are listed. @@ -44,17 +35,8 @@ func List(params *ListParams) ([]ThingInfo, error) { var things []ThingInfo for _, foundThing := range foundThings { - var vars []string - for _, p := range foundThing.Properties { - vars = append(vars, p.Name) - } - th := ThingInfo{ - Name: foundThing.Name, - ID: foundThing.Id, - DeviceID: foundThing.DeviceId, - Variables: vars, - } - things = append(things, th) + info := getThingInfo(&foundThing) + things = append(things, *info) } return things, nil diff --git a/command/thing/thing.go b/command/thing/thing.go new file mode 100644 index 00000000..df3552bb --- /dev/null +++ b/command/thing/thing.go @@ -0,0 +1,26 @@ +package thing + +import iotclient "github.com/arduino/iot-client-go" + +// ThingInfo contains the main parameters of +// an Arduino IoT Cloud thing. +type ThingInfo struct { + Name string `json:"name"` + ID string `json:"id"` + DeviceID string `json:"device-id"` + Variables []string `json:"variables"` +} + +func getThingInfo(thing *iotclient.ArduinoThing) *ThingInfo { + var vars []string + for _, p := range thing.Properties { + vars = append(vars, p.Name) + } + info := &ThingInfo{ + Name: thing.Name, + ID: thing.Id, + DeviceID: thing.DeviceId, + Variables: vars, + } + return info +} diff --git a/go.mod b/go.mod index 18c9a4c6..d272dbff 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/arduino/go-paths-helper v1.6.0 github.com/arduino/iot-client-go v1.3.4-0.20210902151346-1cd63fb0c784 github.com/howeyc/crc16 v0.0.0-20171223171357-2b2a61e366a6 - github.com/sirupsen/logrus v1.4.2 + github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.1.3 github.com/spf13/viper v1.7.0 github.com/stretchr/testify v1.6.1 diff --git a/go.sum b/go.sum index 76cd352d..ad9e7e37 100644 --- a/go.sum +++ b/go.sum @@ -255,7 +255,6 @@ github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -345,8 +344,9 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= @@ -531,6 +531,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/internal/iot/client.go b/internal/iot/client.go index 5560ca6a..8f52f07c 100644 --- a/internal/iot/client.go +++ b/internal/iot/client.go @@ -10,12 +10,12 @@ import ( // Client can be used to perform actions on Arduino IoT Cloud. type Client interface { - DeviceCreate(fqbn, name, serial, devType string) (string, error) + DeviceCreate(fqbn, name, serial, devType string) (*iotclient.ArduinoDevicev2, error) DeviceDelete(id string) error DeviceList() ([]iotclient.ArduinoDevicev2, error) DeviceShow(id string) (*iotclient.ArduinoDevicev2, error) CertificateCreate(id, csr string) (*iotclient.ArduinoCompressedv2, error) - ThingCreate(thing *iotclient.Thing, force bool) (string, error) + ThingCreate(thing *iotclient.Thing, force bool) (*iotclient.ArduinoThing, error) ThingUpdate(id string, thing *iotclient.Thing, force bool) error ThingDelete(id string) error ThingShow(id string) (*iotclient.ArduinoThing, error) @@ -40,8 +40,8 @@ func NewClient(clientID, secretID string) (Client, error) { } // DeviceCreate allows to create a new device on Arduino IoT Cloud. -// It returns the ID associated to the new device, and an error. -func (cl *client) DeviceCreate(fqbn, name, serial, dType string) (string, error) { +// It returns the newly created device, and an error. +func (cl *client) DeviceCreate(fqbn, name, serial, dType string) (*iotclient.ArduinoDevicev2, error) { payload := iotclient.CreateDevicesV2Payload{ Fqbn: fqbn, Name: name, @@ -51,9 +51,9 @@ func (cl *client) DeviceCreate(fqbn, name, serial, dType string) (string, error) dev, _, err := cl.api.DevicesV2Api.DevicesV2Create(cl.ctx, payload) if err != nil { err = fmt.Errorf("creating device, %w", errorDetail(err)) - return "", err + return nil, err } - return dev.Id, nil + return &dev, nil } // DeviceDelete deletes the device corresponding to the passed ID @@ -108,13 +108,13 @@ func (cl *client) CertificateCreate(id, csr string) (*iotclient.ArduinoCompresse } // ThingCreate adds a new thing on Arduino IoT Cloud. -func (cl *client) ThingCreate(thing *iotclient.Thing, force bool) (string, error) { +func (cl *client) ThingCreate(thing *iotclient.Thing, force bool) (*iotclient.ArduinoThing, error) { opt := &iotclient.ThingsV2CreateOpts{Force: optional.NewBool(force)} newThing, _, err := cl.api.ThingsV2Api.ThingsV2Create(cl.ctx, *thing, opt) if err != nil { - return "", fmt.Errorf("%s: %w", "adding new thing", errorDetail(err)) + return nil, fmt.Errorf("%s: %w", "adding new thing", errorDetail(err)) } - return newThing.Id, nil + return &newThing, nil } // ThingUpdate updates a thing on Arduino IoT Cloud.