Skip to content

Commit 2731c71

Browse files
authored
Merge pull request #934 from n4ss/refactor-trust-inspect
Refactor trust view command into a --pretty flag on trust inspect
2 parents 240c03a + 8c3d0b9 commit 2731c71

11 files changed

Lines changed: 124 additions & 87 deletions

cli/command/trust/cmd.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ func NewTrustCommand(dockerCli command.Cli) *cobra.Command {
1616
Annotations: map[string]string{"experimentalCLI": ""},
1717
}
1818
cmd.AddCommand(
19-
newViewCommand(dockerCli),
2019
newRevokeCommand(dockerCli),
2120
newSignCommand(dockerCli),
2221
newTrustKeyCommand(dockerCli),

cli/command/trust/inspect.go

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package trust
22

33
import (
44
"encoding/json"
5+
"fmt"
56
"sort"
67

78
"github.com/docker/cli/cli"
@@ -11,24 +12,55 @@ import (
1112
"github.com/theupdateframework/notary/tuf/data"
1213
)
1314

15+
type inspectOptions struct {
16+
remotes []string
17+
// FIXME(n4ss): this is consistent with `docker service inspect` but we should provide
18+
// a `--format` flag too. (format and pretty-print should be exclusive)
19+
prettyPrint bool
20+
}
21+
1422
func newInspectCommand(dockerCli command.Cli) *cobra.Command {
23+
options := inspectOptions{}
1524
cmd := &cobra.Command{
1625
Use: "inspect IMAGE[:TAG] [IMAGE[:TAG]...]",
1726
Short: "Return low-level information about keys and signatures",
1827
Args: cli.RequiresMinArgs(1),
1928
RunE: func(cmd *cobra.Command, args []string) error {
20-
return runInspect(dockerCli, args)
29+
options.remotes = args
30+
31+
return runInspect(dockerCli, options)
2132
},
2233
}
34+
35+
flags := cmd.Flags()
36+
flags.BoolVar(&options.prettyPrint, "pretty", false, "Print the information in a human friendly format")
37+
2338
return cmd
2439
}
2540

26-
func runInspect(dockerCli command.Cli, remotes []string) error {
41+
func runInspect(dockerCli command.Cli, opts inspectOptions) error {
42+
if opts.prettyPrint {
43+
var err error
44+
45+
for index, remote := range opts.remotes {
46+
if err = prettyPrintTrustInfo(dockerCli, remote); err != nil {
47+
return err
48+
}
49+
50+
// Additional separator between the inspection output of each image
51+
if index < len(opts.remotes)-1 {
52+
fmt.Fprint(dockerCli.Out(), "\n\n")
53+
}
54+
}
55+
56+
return err
57+
}
58+
2759
getRefFunc := func(ref string) (interface{}, []byte, error) {
2860
i, err := getRepoTrustInfo(dockerCli, ref)
2961
return nil, i, err
3062
}
31-
return inspect.Inspect(dockerCli.Out(), remotes, "", getRefFunc)
63+
return inspect.Inspect(dockerCli.Out(), opts.remotes, "", getRefFunc)
3264
}
3365

3466
func getRepoTrustInfo(cli command.Cli, remote string) ([]byte, error) {
Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,21 @@ import (
44
"fmt"
55
"io"
66
"sort"
7-
"strings"
87

9-
"github.com/docker/cli/cli"
108
"github.com/docker/cli/cli/command"
119
"github.com/docker/cli/cli/command/formatter"
12-
"github.com/spf13/cobra"
1310
"github.com/theupdateframework/notary/client"
1411
)
1512

16-
func newViewCommand(dockerCli command.Cli) *cobra.Command {
17-
cmd := &cobra.Command{
18-
Use: "view IMAGE[:TAG]",
19-
Short: "Display detailed information about keys and signatures",
20-
Args: cli.ExactArgs(1),
21-
RunE: func(cmd *cobra.Command, args []string) error {
22-
return viewTrustInfo(dockerCli, args[0])
23-
},
24-
}
25-
return cmd
26-
}
27-
28-
func viewTrustInfo(cli command.Cli, remote string) error {
13+
func prettyPrintTrustInfo(cli command.Cli, remote string) error {
2914
signatureRows, adminRolesWithSigs, delegationRoles, err := lookupTrustInfo(cli, remote)
3015
if err != nil {
3116
return err
3217
}
3318

3419
if len(signatureRows) > 0 {
20+
fmt.Fprintf(cli.Out(), "\nSignatures for %s\n\n", remote)
21+
3522
if err := printSignatures(cli.Out(), signatureRows); err != nil {
3623
return err
3724
}
@@ -42,22 +29,24 @@ func viewTrustInfo(cli command.Cli, remote string) error {
4229

4330
// If we do not have additional signers, do not display
4431
if len(signerRoleToKeyIDs) > 0 {
45-
fmt.Fprintf(cli.Out(), "\nList of signers and their keys for %s:\n\n", strings.Split(remote, ":")[0])
32+
fmt.Fprintf(cli.Out(), "\nList of signers and their keys for %s\n\n", remote)
4633
if err := printSignerInfo(cli.Out(), signerRoleToKeyIDs); err != nil {
4734
return err
4835
}
4936
}
5037

5138
// This will always have the root and targets information
52-
fmt.Fprintf(cli.Out(), "\nAdministrative keys for %s:\n", strings.Split(remote, ":")[0])
39+
fmt.Fprintf(cli.Out(), "\nAdministrative keys for %s\n\n", remote)
5340
printSortedAdminKeys(cli.Out(), adminRolesWithSigs)
5441
return nil
5542
}
5643

5744
func printSortedAdminKeys(out io.Writer, adminRoles []client.RoleWithSignatures) {
5845
sort.Slice(adminRoles, func(i, j int) bool { return adminRoles[i].Name > adminRoles[j].Name })
5946
for _, adminRole := range adminRoles {
60-
fmt.Fprintf(out, "%s", formatAdminRole(adminRole))
47+
if formattedAdminRole := formatAdminRole(adminRole); formattedAdminRole != "" {
48+
fmt.Fprintf(out, " %s", formattedAdminRole)
49+
}
6150
}
6251
}
6352

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,21 @@ import (
1717
"github.com/theupdateframework/notary/tuf/data"
1818
)
1919

20+
// TODO(n4ss): remove common tests with the regular inspect command
21+
2022
type fakeClient struct {
2123
dockerClient.Client
2224
}
2325

24-
func TestTrustViewCommandErrors(t *testing.T) {
26+
func TestTrustInspectPrettyCommandErrors(t *testing.T) {
2527
testCases := []struct {
2628
name string
2729
args []string
2830
expectedError string
2931
}{
3032
{
3133
name: "not-enough-args",
32-
expectedError: "requires exactly 1 argument",
33-
},
34-
{
35-
name: "too-many-args",
36-
args: []string{"remote1", "remote2"},
37-
expectedError: "requires exactly 1 argument",
34+
expectedError: "requires at least 1 argument",
3835
},
3936
{
4037
name: "sha-reference",
@@ -48,104 +45,115 @@ func TestTrustViewCommandErrors(t *testing.T) {
4845
},
4946
}
5047
for _, tc := range testCases {
51-
cmd := newViewCommand(
48+
cmd := newInspectCommand(
5249
test.NewFakeCli(&fakeClient{}))
5350
cmd.SetArgs(tc.args)
5451
cmd.SetOutput(ioutil.Discard)
52+
cmd.Flags().Set("pretty", "true")
5553
assert.ErrorContains(t, cmd.Execute(), tc.expectedError)
5654
}
5755
}
5856

59-
func TestTrustViewCommandOfflineErrors(t *testing.T) {
57+
func TestTrustInspectPrettyCommandOfflineErrors(t *testing.T) {
6058
cli := test.NewFakeCli(&fakeClient{})
6159
cli.SetNotaryClient(notaryfake.GetOfflineNotaryRepository)
62-
cmd := newViewCommand(cli)
60+
cmd := newInspectCommand(cli)
61+
cmd.Flags().Set("pretty", "true")
6362
cmd.SetArgs([]string{"nonexistent-reg-name.io/image"})
6463
cmd.SetOutput(ioutil.Discard)
6564
assert.ErrorContains(t, cmd.Execute(), "No signatures or cannot access nonexistent-reg-name.io/image")
6665

6766
cli = test.NewFakeCli(&fakeClient{})
6867
cli.SetNotaryClient(notaryfake.GetOfflineNotaryRepository)
69-
cmd = newViewCommand(cli)
68+
cmd = newInspectCommand(cli)
69+
cmd.Flags().Set("pretty", "true")
7070
cmd.SetArgs([]string{"nonexistent-reg-name.io/image:tag"})
7171
cmd.SetOutput(ioutil.Discard)
7272
assert.ErrorContains(t, cmd.Execute(), "No signatures or cannot access nonexistent-reg-name.io/image")
7373
}
7474

75-
func TestTrustViewCommandUninitializedErrors(t *testing.T) {
75+
func TestTrustInspectPrettyCommandUninitializedErrors(t *testing.T) {
7676
cli := test.NewFakeCli(&fakeClient{})
7777
cli.SetNotaryClient(notaryfake.GetUninitializedNotaryRepository)
78-
cmd := newViewCommand(cli)
78+
cmd := newInspectCommand(cli)
79+
cmd.Flags().Set("pretty", "true")
7980
cmd.SetArgs([]string{"reg/unsigned-img"})
8081
cmd.SetOutput(ioutil.Discard)
8182
assert.ErrorContains(t, cmd.Execute(), "No signatures or cannot access reg/unsigned-img")
8283

8384
cli = test.NewFakeCli(&fakeClient{})
8485
cli.SetNotaryClient(notaryfake.GetUninitializedNotaryRepository)
85-
cmd = newViewCommand(cli)
86+
cmd = newInspectCommand(cli)
87+
cmd.Flags().Set("pretty", "true")
8688
cmd.SetArgs([]string{"reg/unsigned-img:tag"})
8789
cmd.SetOutput(ioutil.Discard)
8890
assert.ErrorContains(t, cmd.Execute(), "No signatures or cannot access reg/unsigned-img:tag")
8991
}
9092

91-
func TestTrustViewCommandEmptyNotaryRepoErrors(t *testing.T) {
93+
func TestTrustInspectPrettyCommandEmptyNotaryRepoErrors(t *testing.T) {
9294
cli := test.NewFakeCli(&fakeClient{})
9395
cli.SetNotaryClient(notaryfake.GetEmptyTargetsNotaryRepository)
94-
cmd := newViewCommand(cli)
96+
cmd := newInspectCommand(cli)
97+
cmd.Flags().Set("pretty", "true")
9598
cmd.SetArgs([]string{"reg/img:unsigned-tag"})
9699
cmd.SetOutput(ioutil.Discard)
97100
assert.NilError(t, cmd.Execute())
98101
assert.Check(t, is.Contains(cli.OutBuffer().String(), "No signatures for reg/img:unsigned-tag"))
99-
assert.Check(t, is.Contains(cli.OutBuffer().String(), "Administrative keys for reg/img:"))
102+
assert.Check(t, is.Contains(cli.OutBuffer().String(), "Administrative keys for reg/img"))
100103

101104
cli = test.NewFakeCli(&fakeClient{})
102105
cli.SetNotaryClient(notaryfake.GetEmptyTargetsNotaryRepository)
103-
cmd = newViewCommand(cli)
106+
cmd = newInspectCommand(cli)
107+
cmd.Flags().Set("pretty", "true")
104108
cmd.SetArgs([]string{"reg/img"})
105109
cmd.SetOutput(ioutil.Discard)
106110
assert.NilError(t, cmd.Execute())
107111
assert.Check(t, is.Contains(cli.OutBuffer().String(), "No signatures for reg/img"))
108-
assert.Check(t, is.Contains(cli.OutBuffer().String(), "Administrative keys for reg/img:"))
112+
assert.Check(t, is.Contains(cli.OutBuffer().String(), "Administrative keys for reg/img"))
109113
}
110114

111-
func TestTrustViewCommandFullRepoWithoutSigners(t *testing.T) {
115+
func TestTrustInspectPrettyCommandFullRepoWithoutSigners(t *testing.T) {
112116
cli := test.NewFakeCli(&fakeClient{})
113117
cli.SetNotaryClient(notaryfake.GetLoadedWithNoSignersNotaryRepository)
114-
cmd := newViewCommand(cli)
118+
cmd := newInspectCommand(cli)
119+
cmd.Flags().Set("pretty", "true")
115120
cmd.SetArgs([]string{"signed-repo"})
116121
assert.NilError(t, cmd.Execute())
117122

118-
golden.Assert(t, cli.OutBuffer().String(), "trust-view-full-repo-no-signers.golden")
123+
golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-pretty-full-repo-no-signers.golden")
119124
}
120125

121-
func TestTrustViewCommandOneTagWithoutSigners(t *testing.T) {
126+
func TestTrustInspectPrettyCommandOneTagWithoutSigners(t *testing.T) {
122127
cli := test.NewFakeCli(&fakeClient{})
123128
cli.SetNotaryClient(notaryfake.GetLoadedWithNoSignersNotaryRepository)
124-
cmd := newViewCommand(cli)
129+
cmd := newInspectCommand(cli)
130+
cmd.Flags().Set("pretty", "true")
125131
cmd.SetArgs([]string{"signed-repo:green"})
126132
assert.NilError(t, cmd.Execute())
127133

128-
golden.Assert(t, cli.OutBuffer().String(), "trust-view-one-tag-no-signers.golden")
134+
golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-pretty-one-tag-no-signers.golden")
129135
}
130136

131-
func TestTrustViewCommandFullRepoWithSigners(t *testing.T) {
137+
func TestTrustInspectPrettyCommandFullRepoWithSigners(t *testing.T) {
132138
cli := test.NewFakeCli(&fakeClient{})
133139
cli.SetNotaryClient(notaryfake.GetLoadedNotaryRepository)
134-
cmd := newViewCommand(cli)
140+
cmd := newInspectCommand(cli)
141+
cmd.Flags().Set("pretty", "true")
135142
cmd.SetArgs([]string{"signed-repo"})
136143
assert.NilError(t, cmd.Execute())
137144

138-
golden.Assert(t, cli.OutBuffer().String(), "trust-view-full-repo-with-signers.golden")
145+
golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-pretty-full-repo-with-signers.golden")
139146
}
140147

141-
func TestTrustViewCommandUnsignedTagInSignedRepo(t *testing.T) {
148+
func TestTrustInspectPrettyCommandUnsignedTagInSignedRepo(t *testing.T) {
142149
cli := test.NewFakeCli(&fakeClient{})
143150
cli.SetNotaryClient(notaryfake.GetLoadedNotaryRepository)
144-
cmd := newViewCommand(cli)
151+
cmd := newInspectCommand(cli)
152+
cmd.Flags().Set("pretty", "true")
145153
cmd.SetArgs([]string{"signed-repo:unsigned"})
146154
assert.NilError(t, cmd.Execute())
147155

148-
golden.Assert(t, cli.OutBuffer().String(), "trust-view-unsigned-tag-with-signers.golden")
156+
golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-pretty-unsigned-tag-with-signers.golden")
149157
}
150158

151159
func TestNotaryRoleToSigner(t *testing.T) {

cli/command/trust/inspect_test.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,7 @@ func TestTrustInspectCommandErrors(t *testing.T) {
1818
}{
1919
{
2020
name: "not-enough-args",
21-
expectedError: "requires exactly 1 argument",
22-
},
23-
{
24-
name: "too-many-args",
25-
args: []string{"remote1", "remote2"},
26-
expectedError: "requires exactly 1 argument",
21+
expectedError: "requires at least 1 argument",
2722
},
2823
{
2924
name: "sha-reference",
@@ -37,8 +32,9 @@ func TestTrustInspectCommandErrors(t *testing.T) {
3732
},
3833
}
3934
for _, tc := range testCases {
40-
cmd := newViewCommand(
35+
cmd := newInspectCommand(
4136
test.NewFakeCli(&fakeClient{}))
37+
cmd.Flags().Set("pretty", "true")
4238
cmd.SetArgs(tc.args)
4339
cmd.SetOutput(ioutil.Discard)
4440
assert.ErrorContains(t, cmd.Execute(), tc.expectedError)

cli/command/trust/testdata/trust-view-full-repo-no-signers.golden renamed to cli/command/trust/testdata/trust-inspect-pretty-full-repo-no-signers.golden

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
2+
Signatures for signed-repo
3+
14
SIGNED TAG DIGEST SIGNERS
25
green 677265656e2d646967657374 (Repo Admin)
36

4-
Administrative keys for signed-repo:
5-
Repository Key: targetsID
6-
Root Key: rootID
7+
Administrative keys for signed-repo
8+
9+
Repository Key: targetsID
10+
Root Key: rootID

cli/command/trust/testdata/trust-view-full-repo-with-signers.golden renamed to cli/command/trust/testdata/trust-inspect-pretty-full-repo-with-signers.golden

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1+
2+
Signatures for signed-repo
3+
14
SIGNED TAG DIGEST SIGNERS
25
blue 626c75652d646967657374 alice
36
green 677265656e2d646967657374 (Repo Admin)
47
red 7265642d646967657374 alice, bob
58

6-
List of signers and their keys for signed-repo:
9+
List of signers and their keys for signed-repo
710

811
SIGNER KEYS
912
alice A
1013
bob B
1114

12-
Administrative keys for signed-repo:
13-
Repository Key: targetsID
14-
Root Key: rootID
15+
Administrative keys for signed-repo
16+
17+
Repository Key: targetsID
18+
Root Key: rootID
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
Signatures for signed-repo:green
3+
4+
SIGNED TAG DIGEST SIGNERS
5+
green 677265656e2d646967657374 (Repo Admin)
6+
7+
Administrative keys for signed-repo:green
8+
9+
Repository Key: targetsID
10+
Root Key: rootID
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
No signatures for signed-repo:unsigned
3+
4+
5+
List of signers and their keys for signed-repo:unsigned
6+
7+
SIGNER KEYS
8+
alice A
9+
bob B
10+
11+
Administrative keys for signed-repo:unsigned
12+
13+
Repository Key: targetsID
14+
Root Key: rootID

0 commit comments

Comments
 (0)