Skip to content

Commit b0fd012

Browse files
committed
Added support for ota v2
1 parent 88d6d00 commit b0fd012

File tree

17 files changed

+832
-53
lines changed

17 files changed

+832
-53
lines changed

cli/cli.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ package cli
1919

2020
import (
2121
"fmt"
22-
"io/ioutil"
22+
"io"
2323
"os"
2424
"strings"
2525

@@ -83,7 +83,7 @@ func parseFormatString(arg string) (feedback.OutputFormat, bool) {
8383
}
8484

8585
func preRun(flags *cliFlags) error {
86-
logrus.SetOutput(ioutil.Discard)
86+
logrus.SetOutput(io.Discard)
8787
// enable log only if verbose flag is passed
8888
if flags.verbose {
8989
logrus.SetLevel(logrus.InfoLevel)

cli/device/list.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ import (
3333
)
3434

3535
type listFlags struct {
36-
tags map[string]string
36+
tags map[string]string
37+
deviceIds string
3738
}
3839

3940
func initListCommand() *cobra.Command {
@@ -56,6 +57,7 @@ func initListCommand() *cobra.Command {
5657
"Comma-separated list of tags with format <key>=<value>.\n"+
5758
"List only devices that match the provided tags.",
5859
)
60+
listCommand.Flags().StringVarP(&flags.deviceIds, "device-ids", "d", "", "Comma separated list of Device IDs")
5961
return listCommand
6062
}
6163

@@ -67,7 +69,7 @@ func runListCommand(flags *listFlags) error {
6769
return fmt.Errorf("retrieving credentials: %w", err)
6870
}
6971

70-
params := &device.ListParams{Tags: flags.tags}
72+
params := &device.ListParams{Tags: flags.tags, DeviceIds: flags.deviceIds}
7173
devs, err := device.List(context.TODO(), params, cred)
7274
if err != nil {
7375
return err

cli/device/tag/create.go

+28-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"context"
2222
"fmt"
2323
"os"
24+
"strings"
2425

2526
"github.com/arduino/arduino-cli/cli/errorcodes"
2627
"github.com/arduino/arduino-cli/cli/feedback"
@@ -32,6 +33,7 @@ import (
3233

3334
type createTagsFlags struct {
3435
id string
36+
ids string
3537
tags map[string]string
3638
}
3739

@@ -49,23 +51,45 @@ func InitCreateTagsCommand() *cobra.Command {
4951
},
5052
}
5153
createTagsCommand.Flags().StringVarP(&flags.id, "id", "i", "", "Device ID")
54+
createTagsCommand.Flags().StringVarP(&flags.ids, "ids", "", "", "Comma-separated list of Device IDs")
5255
createTagsCommand.Flags().StringToStringVar(
5356
&flags.tags,
5457
"tags",
5558
nil,
5659
"Comma-separated list of tags with format <key>=<value>.",
5760
)
58-
createTagsCommand.MarkFlagRequired("id")
5961
createTagsCommand.MarkFlagRequired("tags")
6062
return createTagsCommand
6163
}
6264

6365
func runCreateTagsCommand(flags *createTagsFlags) error {
64-
logrus.Infof("Creating tags on device %s", flags.id)
66+
if flags.id == "" && flags.ids == "" {
67+
return fmt.Errorf("missing required flag(s) \"id\" or \"ids\"")
68+
}
69+
70+
if flags.id != "" {
71+
if err := creteTag(flags.id, flags.tags); err != nil {
72+
return err
73+
}
74+
}
75+
if flags.ids != "" {
76+
idsArray := strings.Split(flags.ids, ",")
77+
for _, id := range idsArray {
78+
id = strings.TrimSpace(id)
79+
if err := creteTag(id, flags.tags); err != nil {
80+
return err
81+
}
82+
}
83+
}
84+
return nil
85+
}
86+
87+
func creteTag(id string, tags map[string]string) error {
88+
logrus.Infof("Creating tags on device %s", id)
6589

6690
params := &tag.CreateTagsParams{
67-
ID: flags.id,
68-
Tags: flags.tags,
91+
ID: id,
92+
Tags: tags,
6993
Resource: tag.Device,
7094
}
7195

cli/device/tag/delete.go

+31-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"context"
2222
"fmt"
2323
"os"
24+
"strings"
2425

2526
"github.com/arduino/arduino-cli/cli/errorcodes"
2627
"github.com/arduino/arduino-cli/cli/feedback"
@@ -32,6 +33,7 @@ import (
3233

3334
type deleteTagsFlags struct {
3435
id string
36+
ids string
3537
keys []string
3638
}
3739

@@ -49,23 +51,48 @@ func InitDeleteTagsCommand() *cobra.Command {
4951
},
5052
}
5153
deleteTagsCommand.Flags().StringVarP(&flags.id, "id", "i", "", "Device ID")
54+
deleteTagsCommand.Flags().StringVarP(&flags.id, "ids", "", "", "Comma-separated list of Device IDs")
5255
deleteTagsCommand.Flags().StringSliceVarP(&flags.keys, "keys", "k", nil, "Comma-separated list of keys of tags to delete")
53-
deleteTagsCommand.MarkFlagRequired("id")
5456
deleteTagsCommand.MarkFlagRequired("keys")
5557
return deleteTagsCommand
5658
}
5759

5860
func runDeleteTagsCommand(flags *deleteTagsFlags) error {
59-
logrus.Infof("Deleting tags with keys %s", flags.keys)
61+
if flags.id == "" && flags.ids == "" {
62+
return fmt.Errorf("missing required flag(s) \"id\" or \"ids\"")
63+
}
64+
65+
if flags.id != "" {
66+
err := deleteTags(flags.id, flags.keys)
67+
if err != nil {
68+
return err
69+
}
70+
}
71+
if flags.ids != "" {
72+
ids := strings.Split(flags.ids, ",")
73+
for _, id := range ids {
74+
id = strings.TrimSpace(id)
75+
err := deleteTags(id, flags.keys)
76+
if err != nil {
77+
return err
78+
}
79+
}
80+
}
81+
82+
return nil
83+
}
84+
85+
func deleteTags(id string, keys []string) error {
86+
logrus.Infof("Deleting tags with keys %s", keys)
6087

6188
cred, err := config.RetrieveCredentials()
6289
if err != nil {
6390
return fmt.Errorf("retrieving credentials: %w", err)
6491
}
6592

6693
params := &tag.DeleteTagsParams{
67-
ID: flags.id,
68-
Keys: flags.keys,
94+
ID: id,
95+
Keys: keys,
6996
Resource: tag.Device,
7097
}
7198

cli/ota/massupload.go

+23-22
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"fmt"
2323
"os"
2424
"sort"
25-
"strings"
2625

2726
"github.com/arduino/arduino-cli/cli/errorcodes"
2827
"github.com/arduino/arduino-cli/cli/feedback"
@@ -96,22 +95,6 @@ func runMassUploadCommand(flags *massUploadFlags) error {
9695
})
9796

9897
feedback.PrintResult(massUploadResult{resp})
99-
100-
var failed []string
101-
for _, r := range resp {
102-
if r.Err != nil {
103-
failed = append(failed, r.ID)
104-
}
105-
}
106-
if len(failed) == 0 {
107-
return nil
108-
}
109-
failDevs := strings.Join(failed, ",")
110-
feedback.Printf(
111-
"You can try to perform the OTA again on the failed devices using the following command:\n"+
112-
"$ arduino-cloud-cli ota mass-upload --file %s --fqbn %s -d %s",
113-
params.File, params.FQBN, failDevs,
114-
)
11598
return nil
11699
}
117100

@@ -128,17 +111,35 @@ func (r massUploadResult) String() string {
128111
return "No OTA done."
129112
}
130113
t := table.New()
131-
t.SetHeader("ID", "Result")
114+
hasErrorReason := false
115+
for _, r := range r.res {
116+
if r.OtaStatus.ErrorReason != "" {
117+
hasErrorReason = true
118+
break
119+
}
120+
}
121+
122+
if hasErrorReason {
123+
t.SetHeader("Device ID", "Ota ID", "Result", "Error Reason")
124+
} else {
125+
t.SetHeader("Device ID", "Ota ID", "Result")
126+
}
127+
128+
// Now print the table
132129
for _, r := range r.res {
133130
outcome := "Success"
134131
if r.Err != nil {
135132
outcome = fmt.Sprintf("Fail: %s", r.Err.Error())
136133
}
134+
if r.OtaStatus.Status != "" {
135+
outcome = r.OtaStatus.MapStatus()
136+
}
137137

138-
t.AddRow(
139-
r.ID,
140-
outcome,
141-
)
138+
line := []interface{}{r.ID, r.OtaStatus.ID, outcome}
139+
if hasErrorReason {
140+
line = append(line, r.OtaStatus.ErrorReason)
141+
}
142+
t.AddRow(line...)
142143
}
143144
return t.Render()
144145
}

cli/ota/ota.go

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ func NewCommand() *cobra.Command {
3030

3131
otaCommand.AddCommand(initUploadCommand())
3232
otaCommand.AddCommand(initMassUploadCommand())
33+
otaCommand.AddCommand(initOtaStatusCommand())
3334

3435
return otaCommand
3536
}

cli/ota/status.go

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// This file is part of arduino-cloud-cli.
2+
//
3+
// Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU Affero General Public License as published
7+
// by the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU Affero General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Affero General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
18+
package ota
19+
20+
import (
21+
"fmt"
22+
"os"
23+
24+
"github.com/arduino/arduino-cli/cli/errorcodes"
25+
"github.com/arduino/arduino-cli/cli/feedback"
26+
"github.com/arduino/arduino-cloud-cli/command/ota"
27+
"github.com/arduino/arduino-cloud-cli/config"
28+
"github.com/spf13/cobra"
29+
)
30+
31+
type statusFlags struct {
32+
otaID string
33+
otaIDs string
34+
deviceId string
35+
limit int16
36+
sort string
37+
}
38+
39+
func initOtaStatusCommand() *cobra.Command {
40+
flags := &statusFlags{}
41+
uploadCommand := &cobra.Command{
42+
Use: "status",
43+
Short: "OTA status",
44+
Long: "Get OTA status by OTA or device ID",
45+
Run: func(cmd *cobra.Command, args []string) {
46+
if err := runOtaStatusCommand(flags); err != nil {
47+
feedback.Errorf("Error during ota get status: %v", err)
48+
os.Exit(errorcodes.ErrGeneric)
49+
}
50+
},
51+
}
52+
uploadCommand.Flags().StringVarP(&flags.otaID, "ota-id", "o", "", "OTA ID")
53+
uploadCommand.Flags().StringVarP(&flags.otaIDs, "ota-ids", "", "", "OTA IDs (comma separated)")
54+
uploadCommand.Flags().StringVarP(&flags.deviceId, "device-id", "d", "", "Device ID")
55+
uploadCommand.Flags().Int16VarP(&flags.limit, "limit", "l", 10, "Output limit (default: 10)")
56+
uploadCommand.Flags().StringVarP(&flags.sort, "sort", "s", "desc", "Sorting (default: desc)")
57+
58+
return uploadCommand
59+
}
60+
61+
func runOtaStatusCommand(flags *statusFlags) error {
62+
if flags.otaID == "" && flags.deviceId == "" && flags.otaIDs == "" {
63+
return fmt.Errorf("required flag(s) \"ota-id\" or \"device-id\" or \"ota-ids\" not set")
64+
}
65+
66+
cred, err := config.RetrieveCredentials()
67+
if err != nil {
68+
return fmt.Errorf("retrieving credentials: %w", err)
69+
}
70+
71+
return ota.PrintOtaStatus(flags.otaID, flags.otaIDs, flags.deviceId, cred, int(flags.limit), flags.sort)
72+
}

command/device/list.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ package device
2020
import (
2121
"context"
2222
"fmt"
23+
"slices"
24+
"strings"
2325

2426
"github.com/arduino/arduino-cloud-cli/config"
2527
"github.com/arduino/arduino-cloud-cli/internal/iot"
@@ -28,7 +30,8 @@ import (
2830
// ListParams contains the optional parameters needed
2931
// to filter the devices to be listed.
3032
type ListParams struct {
31-
Tags map[string]string // If tags are provided, only devices that have all these tags are listed.
33+
Tags map[string]string // If tags are provided, only devices that have all these tags are listed.
34+
DeviceIds string // If ids are provided, only devices with these ids are listed.
3235
}
3336

3437
// List command is used to list
@@ -43,9 +46,19 @@ func List(ctx context.Context, params *ListParams, cred *config.Credentials) ([]
4346
if err != nil {
4447
return nil, err
4548
}
49+
var deviceIdFilter []string
50+
if params.DeviceIds != "" {
51+
deviceIdFilter = strings.Split(params.DeviceIds, ",")
52+
for i := range deviceIdFilter {
53+
deviceIdFilter[i] = strings.TrimSpace(deviceIdFilter[i])
54+
}
55+
}
4656

4757
var devices []DeviceInfo
4858
for _, foundDev := range foundDevices {
59+
if len(deviceIdFilter) > 0 && !slices.Contains(deviceIdFilter, foundDev.Id) {
60+
continue
61+
}
4962
dev, err := getDeviceInfo(&foundDev)
5063
if err != nil {
5164
return nil, fmt.Errorf("parsing device %s from cloud: %w", foundDev.Id, err)

0 commit comments

Comments
 (0)