Skip to content

Commit 272e8c1

Browse files
Suppor for OTA status retrieval (#150)
* Added support for ota v2 * Fixed go compatibility * Fixed output for ota status * refactor: fix typos * Tagging improvements * Tag in case of v1.x lib update * Fixed compile * Removed not used param * Increased timeout --------- Co-authored-by: Roberto Gazia <r.gazia@arduino.cc>
1 parent 4db92cc commit 272e8c1

File tree

16 files changed

+849
-47
lines changed

16 files changed

+849
-47
lines changed

cli/device/list.go

Lines changed: 4 additions & 2 deletions
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

Lines changed: 28 additions & 4 deletions
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

Lines changed: 31 additions & 4 deletions
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

Lines changed: 23 additions & 22 deletions
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"
@@ -99,22 +98,6 @@ func runMassUploadCommand(flags *massUploadFlags) error {
9998
})
10099

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

@@ -131,17 +114,35 @@ func (r massUploadResult) String() string {
131114
return "No OTA done."
132115
}
133116
t := table.New()
134-
t.SetHeader("ID", "Result")
117+
hasErrorReason := false
118+
for _, r := range r.res {
119+
if r.OtaStatus.ErrorReason != "" {
120+
hasErrorReason = true
121+
break
122+
}
123+
}
124+
125+
if hasErrorReason {
126+
t.SetHeader("Device ID", "Ota ID", "Result", "Error Reason")
127+
} else {
128+
t.SetHeader("Device ID", "Ota ID", "Result")
129+
}
130+
131+
// Now print the table
135132
for _, r := range r.res {
136133
outcome := "Success"
137134
if r.Err != nil {
138135
outcome = fmt.Sprintf("Fail: %s", r.Err.Error())
139136
}
137+
if r.OtaStatus.Status != "" {
138+
outcome = r.OtaStatus.MapStatus()
139+
}
140140

141-
t.AddRow(
142-
r.ID,
143-
outcome,
144-
)
141+
line := []interface{}{r.ID, r.OtaStatus.ID, outcome}
142+
if hasErrorReason {
143+
line = append(line, r.OtaStatus.ErrorReason)
144+
}
145+
t.AddRow(line...)
145146
}
146147
return t.Render()
147148
}

cli/ota/ota.go

Lines changed: 1 addition & 0 deletions
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
otaCommand.AddCommand(initEncodeBinaryCommand())
3435
otaCommand.AddCommand(initDecodeHeaderCommand())
3536

cli/ota/status.go

Lines changed: 72 additions & 0 deletions
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

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package device
2020
import (
2121
"context"
2222
"fmt"
23+
"strings"
2324

2425
"github.com/arduino/arduino-cloud-cli/config"
2526
"github.com/arduino/arduino-cloud-cli/internal/iot"
@@ -28,7 +29,8 @@ import (
2829
// ListParams contains the optional parameters needed
2930
// to filter the devices to be listed.
3031
type ListParams struct {
31-
Tags map[string]string // If tags are provided, only devices that have all these tags are listed.
32+
Tags map[string]string // If tags are provided, only devices that have all these tags are listed.
33+
DeviceIds string // If ids are provided, only devices with these ids are listed.
3234
}
3335

3436
// List command is used to list
@@ -43,9 +45,19 @@ func List(ctx context.Context, params *ListParams, cred *config.Credentials) ([]
4345
if err != nil {
4446
return nil, err
4547
}
48+
var deviceIdFilter []string
49+
if params.DeviceIds != "" {
50+
deviceIdFilter = strings.Split(params.DeviceIds, ",")
51+
for i := range deviceIdFilter {
52+
deviceIdFilter[i] = strings.TrimSpace(deviceIdFilter[i])
53+
}
54+
}
4655

4756
var devices []DeviceInfo
4857
for _, foundDev := range foundDevices {
58+
if len(deviceIdFilter) > 0 && !sliceContains(deviceIdFilter, foundDev.Id) {
59+
continue
60+
}
4961
dev, err := getDeviceInfo(&foundDev)
5062
if err != nil {
5163
return nil, fmt.Errorf("parsing device %s from cloud: %w", foundDev.Id, err)
@@ -55,3 +67,12 @@ func List(ctx context.Context, params *ListParams, cred *config.Credentials) ([]
5567

5668
return devices, nil
5769
}
70+
71+
func sliceContains(s []string, v string) bool {
72+
for i := range s {
73+
if v == s[i] {
74+
return true
75+
}
76+
}
77+
return false
78+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy