Skip to content

Commit 0fc3111

Browse files
authored
c/snap: add --format json to snap tasks (#17017)
* Revert "o/h/ctlcmd: remove incomplete async snapctl functionality prior to 2.76 (#17076)" This reverts commit 40008b5. * o/h/ctlcmd: fix is-ready return codes * t/main/snapctl-is-ready: remove spread test, test in snapctl async branch covers this feature * daemon: fix TestStateChange * daemon: fix TestStateChange unit test * c/snap: add --format json to snap tasks * t/main/snap-tasks: create spread test to ensure --format json produces valid JSON * cmd/snap, t/main/snap-tasks: fix formatting, specify --format options * fixup! cmd/snap, t/main/snap-tasks: fix formatting, specify --format options * c/snap, t/m/snap-tasks: fix spread test jq check, make tasks fail with invalid format * c/snap, t/main/snap-tasks: fix spread test, add formatMixin * t/main/snap-tasks: fix file name issue for task.yaml * c/snap, t/m/snap-tasks: fix spread test failure, fix formatting in cmd
1 parent da4df5b commit 0fc3111

4 files changed

Lines changed: 78 additions & 2 deletions

File tree

cmd/snap/cmd_changes.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"sort"
2626

2727
"github.com/jessevdk/go-flags"
28-
2928
"github.com/snapcore/snapd/client"
3029
"github.com/snapcore/snapd/i18n"
3130
)
@@ -51,14 +50,15 @@ type cmdChanges struct {
5150
type cmdTasks struct {
5251
timeMixin
5352
changeIDMixin
53+
formatMixin
5454
}
5555

5656
func init() {
5757
addCommand("changes", shortChangesHelp, longChangesHelp,
5858
func() flags.Commander { return &cmdChanges{} }, timeDescs, nil)
5959
addCommand("tasks", shortTasksHelp, longTasksHelp,
6060
func() flags.Commander { return &cmdTasks{} },
61-
changeIDMixinOptDesc.also(timeDescs),
61+
changeIDMixinOptDesc.also(timeDescs).also(formatArgsHelp),
6262
changeIDMixinArgDesc).alias = "change"
6363
}
6464

@@ -160,6 +160,11 @@ func (c *cmdTasks) showChange(chid string) error {
160160
return err
161161
}
162162

163+
if c.Format != "text" && c.Format != "" {
164+
err = c.formatNonText(chg)
165+
return err
166+
}
167+
163168
w := tabWriter()
164169

165170
fmt.Fprint(w, i18n.G("Status\tSpawn\tReady\tSummary\n"))

cmd/snap/cmd_changes_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@
2020
package main_test
2121

2222
import (
23+
"encoding/json"
2324
"fmt"
2425
"net/http"
2526
"strings"
2627

2728
"gopkg.in/check.v1"
2829

30+
"github.com/snapcore/snapd/client"
2931
snap "github.com/snapcore/snapd/cmd/snap"
3032
)
3133

@@ -232,6 +234,36 @@ Doing +2016-04-21T01:02:03Z +2016-04-21T01:02:04Z +some summary \(50.00%\)
232234
c.Check(s.Stderr(), check.Equals, "")
233235
}
234236

237+
func (s *SnapSuite) TestTasksJSON(c *check.C) {
238+
s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
239+
c.Check(r.Method, check.Equals, "GET")
240+
c.Check(r.URL.Path, check.Equals, "/v2/changes/42")
241+
fmt.Fprintln(w, mockChangeJSON)
242+
})
243+
244+
rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"tasks", "--format=json", "42"})
245+
c.Assert(err, check.IsNil)
246+
c.Assert(rest, check.DeepEquals, []string{})
247+
248+
var chg client.Change
249+
c.Assert(json.Unmarshal([]byte(s.Stdout()), &chg), check.IsNil)
250+
c.Check(chg.ID, check.Equals, "uno")
251+
c.Check(chg.Kind, check.Equals, "foo")
252+
c.Check(chg.Summary, check.Equals, "...")
253+
c.Check(chg.Status, check.Equals, "Do")
254+
c.Check(chg.Ready, check.Equals, false)
255+
c.Assert(chg.Tasks, check.HasLen, 1)
256+
c.Check(chg.Tasks[0].Kind, check.Equals, "bar")
257+
c.Check(chg.Tasks[0].Summary, check.Equals, "some summary")
258+
c.Check(chg.Tasks[0].Status, check.Equals, "Do")
259+
260+
// If format (which has defined values) gets passed an invalid value, the parser wraps it in `'.
261+
_, err = snap.Parser(snap.Client()).ParseArgs([]string{"tasks", "--format=", "42"})
262+
c.Assert(err, check.ErrorMatches, ".*Invalid value `' for option `--format'. Allowed values are: .* or json")
263+
_, err = snap.Parser(snap.Client()).ParseArgs([]string{"tasks", "--format=random", "42"})
264+
c.Assert(err, check.ErrorMatches, ".*Invalid value `random' for option `--format'. Allowed values are: .* or json")
265+
}
266+
235267
func (s *SnapSuite) TestNoChanges(c *check.C) {
236268
n := 0
237269
s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {

cmd/snap/main.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package main
2121

2222
import (
23+
"encoding/json"
2324
"errors"
2425
"fmt"
2526
"io"
@@ -215,6 +216,28 @@ func (ch *clientMixin) setClient(cli *client.Client) {
215216
ch.client = cli
216217
}
217218

219+
type formatMixin struct {
220+
//lint:ignore SA5008 "choice" tag is intentionally duplicated
221+
Format string `long:"format" default:"text" choice:"text" choice:"json"`
222+
}
223+
224+
var formatArgsHelp = map[string]string{
225+
"format": i18n.G("Output format"),
226+
}
227+
228+
func (mx formatMixin) formatNonText(result any) error {
229+
switch mx.Format {
230+
case "json":
231+
data, err := json.Marshal(result)
232+
if err != nil {
233+
return err
234+
}
235+
fmt.Fprintln(Stdout, string(data))
236+
return nil
237+
}
238+
panic(fmt.Sprintf("internal error: invalid format option %q", mx.Format))
239+
}
240+
218241
func firstNonOptionIsRun() bool {
219242
if len(os.Args) < 2 {
220243
return false

tests/main/snap-tasks/task.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
summary: Check that `snap tasks` works as expected.
2+
3+
details: |
4+
Verifies that the command 'snap tasks' properly lists the tasks
5+
associated with a change. Also check the error scenarios.
6+
7+
execute: |
8+
echo "Install snap to trigger a change"
9+
"$TESTSTOOLS"/snaps-state install-local test-snapd-sh-core24
10+
11+
echo "Get the change ID for the install operation"
12+
CHANGE_ID=$(snap changes | grep "Install \"test-snapd-sh-core24\" snap" | awk '{print $1}')
13+
14+
# Check that valid JSON output is produced for the change, and that it contains the expected task with the expected state.
15+
echo "Check that 'snap tasks' lists the task for the change (and accepts the --format option)"
16+
snap tasks --format json "$CHANGE_ID" | gojq -e '.tasks | all(.status == "Done" or .status == "done")'

0 commit comments

Comments
 (0)