This fix tries to address the enhancement discussed in 28735 to add
`--format` for the output of `docker plugin ls`.
This fix
1. Add `--format` and `--quiet` flags to `docker plugin ls`
2. Convert the current implementation to use `formatter`, consistent with
other docker list commands.
3. Add `pluginsFormat` for config.json.
Related docs has been updated.
Several unit tests have been added to cover the changes.
This fix is related to 28708 and 28735.
Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
| 1 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,87 @@ |
| 0 |
+package formatter |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "strings" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/docker/docker/api/types" |
|
| 6 |
+ "github.com/docker/docker/pkg/stringid" |
|
| 7 |
+ "github.com/docker/docker/pkg/stringutils" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+const ( |
|
| 11 |
+ defaultPluginTableFormat = "table {{.ID}}\t{{.Name}}\t{{.Description}}\t{{.Enabled}}"
|
|
| 12 |
+ |
|
| 13 |
+ pluginIDHeader = "ID" |
|
| 14 |
+ descriptionHeader = "DESCRIPTION" |
|
| 15 |
+ enabledHeader = "ENABLED" |
|
| 16 |
+) |
|
| 17 |
+ |
|
| 18 |
+// NewPluginFormat returns a Format for rendering using a plugin Context |
|
| 19 |
+func NewPluginFormat(source string, quiet bool) Format {
|
|
| 20 |
+ switch source {
|
|
| 21 |
+ case TableFormatKey: |
|
| 22 |
+ if quiet {
|
|
| 23 |
+ return defaultQuietFormat |
|
| 24 |
+ } |
|
| 25 |
+ return defaultPluginTableFormat |
|
| 26 |
+ case RawFormatKey: |
|
| 27 |
+ if quiet {
|
|
| 28 |
+ return `plugin_id: {{.ID}}`
|
|
| 29 |
+ } |
|
| 30 |
+ return `plugin_id: {{.ID}}\nname: {{.Name}}\ndescription: {{.Description}}\nenabled: {{.Enabled}}\n`
|
|
| 31 |
+ } |
|
| 32 |
+ return Format(source) |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+// PluginWrite writes the context |
|
| 36 |
+func PluginWrite(ctx Context, plugins []*types.Plugin) error {
|
|
| 37 |
+ render := func(format func(subContext subContext) error) error {
|
|
| 38 |
+ for _, plugin := range plugins {
|
|
| 39 |
+ pluginCtx := &pluginContext{trunc: ctx.Trunc, p: *plugin}
|
|
| 40 |
+ if err := format(pluginCtx); err != nil {
|
|
| 41 |
+ return err |
|
| 42 |
+ } |
|
| 43 |
+ } |
|
| 44 |
+ return nil |
|
| 45 |
+ } |
|
| 46 |
+ return ctx.Write(&pluginContext{}, render)
|
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+type pluginContext struct {
|
|
| 50 |
+ HeaderContext |
|
| 51 |
+ trunc bool |
|
| 52 |
+ p types.Plugin |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 55 |
+func (c *pluginContext) MarshalJSON() ([]byte, error) {
|
|
| 56 |
+ return marshalJSON(c) |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+func (c *pluginContext) ID() string {
|
|
| 60 |
+ c.AddHeader(pluginIDHeader) |
|
| 61 |
+ if c.trunc {
|
|
| 62 |
+ return stringid.TruncateID(c.p.ID) |
|
| 63 |
+ } |
|
| 64 |
+ return c.p.ID |
|
| 65 |
+} |
|
| 66 |
+ |
|
| 67 |
+func (c *pluginContext) Name() string {
|
|
| 68 |
+ c.AddHeader(nameHeader) |
|
| 69 |
+ return c.p.Name |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+func (c *pluginContext) Description() string {
|
|
| 73 |
+ c.AddHeader(descriptionHeader) |
|
| 74 |
+ desc := strings.Replace(c.p.Config.Description, "\n", "", -1) |
|
| 75 |
+ desc = strings.Replace(desc, "\r", "", -1) |
|
| 76 |
+ if c.trunc {
|
|
| 77 |
+ desc = stringutils.Ellipsis(desc, 45) |
|
| 78 |
+ } |
|
| 79 |
+ |
|
| 80 |
+ return desc |
|
| 81 |
+} |
|
| 82 |
+ |
|
| 83 |
+func (c *pluginContext) Enabled() bool {
|
|
| 84 |
+ c.AddHeader(enabledHeader) |
|
| 85 |
+ return c.p.Enabled |
|
| 86 |
+} |
| 0 | 87 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,188 @@ |
| 0 |
+package formatter |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "encoding/json" |
|
| 5 |
+ "strings" |
|
| 6 |
+ "testing" |
|
| 7 |
+ |
|
| 8 |
+ "github.com/docker/docker/api/types" |
|
| 9 |
+ "github.com/docker/docker/pkg/stringid" |
|
| 10 |
+ "github.com/docker/docker/pkg/testutil/assert" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+func TestPluginContext(t *testing.T) {
|
|
| 14 |
+ pluginID := stringid.GenerateRandomID() |
|
| 15 |
+ |
|
| 16 |
+ var ctx pluginContext |
|
| 17 |
+ cases := []struct {
|
|
| 18 |
+ pluginCtx pluginContext |
|
| 19 |
+ expValue string |
|
| 20 |
+ expHeader string |
|
| 21 |
+ call func() string |
|
| 22 |
+ }{
|
|
| 23 |
+ {pluginContext{
|
|
| 24 |
+ p: types.Plugin{ID: pluginID},
|
|
| 25 |
+ trunc: false, |
|
| 26 |
+ }, pluginID, pluginIDHeader, ctx.ID}, |
|
| 27 |
+ {pluginContext{
|
|
| 28 |
+ p: types.Plugin{ID: pluginID},
|
|
| 29 |
+ trunc: true, |
|
| 30 |
+ }, stringid.TruncateID(pluginID), pluginIDHeader, ctx.ID}, |
|
| 31 |
+ {pluginContext{
|
|
| 32 |
+ p: types.Plugin{Name: "plugin_name"},
|
|
| 33 |
+ }, "plugin_name", nameHeader, ctx.Name}, |
|
| 34 |
+ {pluginContext{
|
|
| 35 |
+ p: types.Plugin{Config: types.PluginConfig{Description: "plugin_description"}},
|
|
| 36 |
+ }, "plugin_description", descriptionHeader, ctx.Description}, |
|
| 37 |
+ } |
|
| 38 |
+ |
|
| 39 |
+ for _, c := range cases {
|
|
| 40 |
+ ctx = c.pluginCtx |
|
| 41 |
+ v := c.call() |
|
| 42 |
+ if strings.Contains(v, ",") {
|
|
| 43 |
+ compareMultipleValues(t, v, c.expValue) |
|
| 44 |
+ } else if v != c.expValue {
|
|
| 45 |
+ t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
|
| 46 |
+ } |
|
| 47 |
+ |
|
| 48 |
+ h := ctx.FullHeader() |
|
| 49 |
+ if h != c.expHeader {
|
|
| 50 |
+ t.Fatalf("Expected %s, was %s\n", c.expHeader, h)
|
|
| 51 |
+ } |
|
| 52 |
+ } |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 55 |
+func TestPluginContextWrite(t *testing.T) {
|
|
| 56 |
+ cases := []struct {
|
|
| 57 |
+ context Context |
|
| 58 |
+ expected string |
|
| 59 |
+ }{
|
|
| 60 |
+ |
|
| 61 |
+ // Errors |
|
| 62 |
+ {
|
|
| 63 |
+ Context{Format: "{{InvalidFunction}}"},
|
|
| 64 |
+ `Template parsing error: template: :1: function "InvalidFunction" not defined |
|
| 65 |
+`, |
|
| 66 |
+ }, |
|
| 67 |
+ {
|
|
| 68 |
+ Context{Format: "{{nil}}"},
|
|
| 69 |
+ `Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command |
|
| 70 |
+`, |
|
| 71 |
+ }, |
|
| 72 |
+ // Table format |
|
| 73 |
+ {
|
|
| 74 |
+ Context{Format: NewPluginFormat("table", false)},
|
|
| 75 |
+ `ID NAME DESCRIPTION ENABLED |
|
| 76 |
+pluginID1 foobar_baz description 1 true |
|
| 77 |
+pluginID2 foobar_bar description 2 false |
|
| 78 |
+`, |
|
| 79 |
+ }, |
|
| 80 |
+ {
|
|
| 81 |
+ Context{Format: NewPluginFormat("table", true)},
|
|
| 82 |
+ `pluginID1 |
|
| 83 |
+pluginID2 |
|
| 84 |
+`, |
|
| 85 |
+ }, |
|
| 86 |
+ {
|
|
| 87 |
+ Context{Format: NewPluginFormat("table {{.Name}}", false)},
|
|
| 88 |
+ `NAME |
|
| 89 |
+foobar_baz |
|
| 90 |
+foobar_bar |
|
| 91 |
+`, |
|
| 92 |
+ }, |
|
| 93 |
+ {
|
|
| 94 |
+ Context{Format: NewPluginFormat("table {{.Name}}", true)},
|
|
| 95 |
+ `NAME |
|
| 96 |
+foobar_baz |
|
| 97 |
+foobar_bar |
|
| 98 |
+`, |
|
| 99 |
+ }, |
|
| 100 |
+ // Raw Format |
|
| 101 |
+ {
|
|
| 102 |
+ Context{Format: NewPluginFormat("raw", false)},
|
|
| 103 |
+ `plugin_id: pluginID1 |
|
| 104 |
+name: foobar_baz |
|
| 105 |
+description: description 1 |
|
| 106 |
+enabled: true |
|
| 107 |
+ |
|
| 108 |
+plugin_id: pluginID2 |
|
| 109 |
+name: foobar_bar |
|
| 110 |
+description: description 2 |
|
| 111 |
+enabled: false |
|
| 112 |
+ |
|
| 113 |
+`, |
|
| 114 |
+ }, |
|
| 115 |
+ {
|
|
| 116 |
+ Context{Format: NewPluginFormat("raw", true)},
|
|
| 117 |
+ `plugin_id: pluginID1 |
|
| 118 |
+plugin_id: pluginID2 |
|
| 119 |
+`, |
|
| 120 |
+ }, |
|
| 121 |
+ // Custom Format |
|
| 122 |
+ {
|
|
| 123 |
+ Context{Format: NewPluginFormat("{{.Name}}", false)},
|
|
| 124 |
+ `foobar_baz |
|
| 125 |
+foobar_bar |
|
| 126 |
+`, |
|
| 127 |
+ }, |
|
| 128 |
+ } |
|
| 129 |
+ |
|
| 130 |
+ for _, testcase := range cases {
|
|
| 131 |
+ plugins := []*types.Plugin{
|
|
| 132 |
+ {ID: "pluginID1", Name: "foobar_baz", Config: types.PluginConfig{Description: "description 1"}, Enabled: true},
|
|
| 133 |
+ {ID: "pluginID2", Name: "foobar_bar", Config: types.PluginConfig{Description: "description 2"}, Enabled: false},
|
|
| 134 |
+ } |
|
| 135 |
+ out := bytes.NewBufferString("")
|
|
| 136 |
+ testcase.context.Output = out |
|
| 137 |
+ err := PluginWrite(testcase.context, plugins) |
|
| 138 |
+ if err != nil {
|
|
| 139 |
+ assert.Error(t, err, testcase.expected) |
|
| 140 |
+ } else {
|
|
| 141 |
+ assert.Equal(t, out.String(), testcase.expected) |
|
| 142 |
+ } |
|
| 143 |
+ } |
|
| 144 |
+} |
|
| 145 |
+ |
|
| 146 |
+func TestPluginContextWriteJSON(t *testing.T) {
|
|
| 147 |
+ plugins := []*types.Plugin{
|
|
| 148 |
+ {ID: "pluginID1", Name: "foobar_baz"},
|
|
| 149 |
+ {ID: "pluginID2", Name: "foobar_bar"},
|
|
| 150 |
+ } |
|
| 151 |
+ expectedJSONs := []map[string]interface{}{
|
|
| 152 |
+ {"Description": "", "Enabled": false, "ID": "pluginID1", "Name": "foobar_baz"},
|
|
| 153 |
+ {"Description": "", "Enabled": false, "ID": "pluginID2", "Name": "foobar_bar"},
|
|
| 154 |
+ } |
|
| 155 |
+ |
|
| 156 |
+ out := bytes.NewBufferString("")
|
|
| 157 |
+ err := PluginWrite(Context{Format: "{{json .}}", Output: out}, plugins)
|
|
| 158 |
+ if err != nil {
|
|
| 159 |
+ t.Fatal(err) |
|
| 160 |
+ } |
|
| 161 |
+ for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") {
|
|
| 162 |
+ var m map[string]interface{}
|
|
| 163 |
+ if err := json.Unmarshal([]byte(line), &m); err != nil {
|
|
| 164 |
+ t.Fatal(err) |
|
| 165 |
+ } |
|
| 166 |
+ assert.DeepEqual(t, m, expectedJSONs[i]) |
|
| 167 |
+ } |
|
| 168 |
+} |
|
| 169 |
+ |
|
| 170 |
+func TestPluginContextWriteJSONField(t *testing.T) {
|
|
| 171 |
+ plugins := []*types.Plugin{
|
|
| 172 |
+ {ID: "pluginID1", Name: "foobar_baz"},
|
|
| 173 |
+ {ID: "pluginID2", Name: "foobar_bar"},
|
|
| 174 |
+ } |
|
| 175 |
+ out := bytes.NewBufferString("")
|
|
| 176 |
+ err := PluginWrite(Context{Format: "{{json .ID}}", Output: out}, plugins)
|
|
| 177 |
+ if err != nil {
|
|
| 178 |
+ t.Fatal(err) |
|
| 179 |
+ } |
|
| 180 |
+ for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") {
|
|
| 181 |
+ var s string |
|
| 182 |
+ if err := json.Unmarshal([]byte(line), &s); err != nil {
|
|
| 183 |
+ t.Fatal(err) |
|
| 184 |
+ } |
|
| 185 |
+ assert.Equal(t, s, plugins[i].ID) |
|
| 186 |
+ } |
|
| 187 |
+} |
| ... | ... |
@@ -1,20 +1,17 @@ |
| 1 | 1 |
package plugin |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "fmt" |
|
| 5 |
- "strings" |
|
| 6 |
- "text/tabwriter" |
|
| 7 |
- |
|
| 8 | 4 |
"github.com/docker/docker/cli" |
| 9 | 5 |
"github.com/docker/docker/cli/command" |
| 10 |
- "github.com/docker/docker/pkg/stringid" |
|
| 11 |
- "github.com/docker/docker/pkg/stringutils" |
|
| 6 |
+ "github.com/docker/docker/cli/command/formatter" |
|
| 12 | 7 |
"github.com/spf13/cobra" |
| 13 | 8 |
"golang.org/x/net/context" |
| 14 | 9 |
) |
| 15 | 10 |
|
| 16 | 11 |
type listOptions struct {
|
| 12 |
+ quiet bool |
|
| 17 | 13 |
noTrunc bool |
| 14 |
+ format string |
|
| 18 | 15 |
} |
| 19 | 16 |
|
| 20 | 17 |
func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
|
| ... | ... |
@@ -32,7 +29,9 @@ func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
|
| 32 | 32 |
|
| 33 | 33 |
flags := cmd.Flags() |
| 34 | 34 |
|
| 35 |
+ flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display plugin IDs") |
|
| 35 | 36 |
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Don't truncate output") |
| 37 |
+ flags.StringVar(&opts.format, "format", "", "Pretty-print plugins using a Go template") |
|
| 36 | 38 |
|
| 37 | 39 |
return cmd |
| 38 | 40 |
} |
| ... | ... |
@@ -43,21 +42,19 @@ func runList(dockerCli *command.DockerCli, opts listOptions) error {
|
| 43 | 43 |
return err |
| 44 | 44 |
} |
| 45 | 45 |
|
| 46 |
- w := tabwriter.NewWriter(dockerCli.Out(), 20, 1, 3, ' ', 0) |
|
| 47 |
- fmt.Fprintf(w, "ID \tNAME \tDESCRIPTION\tENABLED") |
|
| 48 |
- fmt.Fprintf(w, "\n") |
|
| 49 |
- |
|
| 50 |
- for _, p := range plugins {
|
|
| 51 |
- id := p.ID |
|
| 52 |
- desc := strings.Replace(p.Config.Description, "\n", " ", -1) |
|
| 53 |
- desc = strings.Replace(desc, "\r", " ", -1) |
|
| 54 |
- if !opts.noTrunc {
|
|
| 55 |
- id = stringid.TruncateID(p.ID) |
|
| 56 |
- desc = stringutils.Ellipsis(desc, 45) |
|
| 46 |
+ format := opts.format |
|
| 47 |
+ if len(format) == 0 {
|
|
| 48 |
+ if len(dockerCli.ConfigFile().PluginsFormat) > 0 && !opts.quiet {
|
|
| 49 |
+ format = dockerCli.ConfigFile().PluginsFormat |
|
| 50 |
+ } else {
|
|
| 51 |
+ format = formatter.TableFormatKey |
|
| 57 | 52 |
} |
| 53 |
+ } |
|
| 58 | 54 |
|
| 59 |
- fmt.Fprintf(w, "%s\t%s\t%s\t%v\n", id, p.Name, desc, p.Enabled) |
|
| 55 |
+ pluginsCtx := formatter.Context{
|
|
| 56 |
+ Output: dockerCli.Out(), |
|
| 57 |
+ Format: formatter.NewPluginFormat(format, opts.quiet), |
|
| 58 |
+ Trunc: !opts.noTrunc, |
|
| 60 | 59 |
} |
| 61 |
- w.Flush() |
|
| 62 |
- return nil |
|
| 60 |
+ return formatter.PluginWrite(pluginsCtx, plugins) |
|
| 63 | 61 |
} |
| ... | ... |
@@ -27,6 +27,7 @@ type ConfigFile struct {
|
| 27 | 27 |
PsFormat string `json:"psFormat,omitempty"` |
| 28 | 28 |
ImagesFormat string `json:"imagesFormat,omitempty"` |
| 29 | 29 |
NetworksFormat string `json:"networksFormat,omitempty"` |
| 30 |
+ PluginsFormat string `json:"pluginsFormat,omitempty"` |
|
| 30 | 31 |
VolumesFormat string `json:"volumesFormat,omitempty"` |
| 31 | 32 |
StatsFormat string `json:"statsFormat,omitempty"` |
| 32 | 33 |
DetachKeys string `json:"detachKeys,omitempty"` |
| ... | ... |
@@ -131,6 +131,12 @@ Docker's client uses this property. If this property is not set, the client |
| 131 | 131 |
falls back to the default table format. For a list of supported formatting |
| 132 | 132 |
directives, see the [**Formatting** section in the `docker images` documentation](images.md) |
| 133 | 133 |
|
| 134 |
+The property `pluginsFormat` specifies the default format for `docker plugin ls` output. |
|
| 135 |
+When the `--format` flag is not provided with the `docker plugin ls` command, |
|
| 136 |
+Docker's client uses this property. If this property is not set, the client |
|
| 137 |
+falls back to the default table format. For a list of supported formatting |
|
| 138 |
+directives, see the [**Formatting** section in the `docker plugin ls` documentation](plugin_ls.md) |
|
| 139 |
+ |
|
| 134 | 140 |
The property `serviceInspectFormat` specifies the default format for `docker |
| 135 | 141 |
service inspect` output. When the `--format` flag is not provided with the |
| 136 | 142 |
`docker service inspect` command, Docker's client uses this property. If this |
| ... | ... |
@@ -186,6 +192,7 @@ Following is a sample `config.json` file: |
| 186 | 186 |
}, |
| 187 | 187 |
"psFormat": "table {{.ID}}\\t{{.Image}}\\t{{.Command}}\\t{{.Labels}}",
|
| 188 | 188 |
"imagesFormat": "table {{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.CreatedAt}}",
|
| 189 |
+ "pluginsFormat": "table {{.ID}}\t{{.Name}}\t{{.Enabled}}",
|
|
| 189 | 190 |
"statsFormat": "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}",
|
| 190 | 191 |
"serviceInspectFormat": "pretty", |
| 191 | 192 |
"detachKeys": "ctrl-e,e", |
| ... | ... |
@@ -24,8 +24,10 @@ Aliases: |
| 24 | 24 |
ls, list |
| 25 | 25 |
|
| 26 | 26 |
Options: |
| 27 |
- --help Print usage |
|
| 28 |
- --no-trunc Don't truncate output |
|
| 27 |
+ --format string Pretty-print plugins using a Go template |
|
| 28 |
+ --help Print usage |
|
| 29 |
+ --no-trunc Don't truncate output |
|
| 30 |
+ -q, --quiet Only display plugin IDs |
|
| 29 | 31 |
``` |
| 30 | 32 |
|
| 31 | 33 |
Lists all the plugins that are currently installed. You can install plugins |
| ... | ... |
@@ -40,6 +42,32 @@ ID NAME TAG DESCRIP |
| 40 | 40 |
69553ca1d123 tiborvass/sample-volume-plugin latest A test plugin for Docker true |
| 41 | 41 |
``` |
| 42 | 42 |
|
| 43 |
+## Formatting |
|
| 44 |
+ |
|
| 45 |
+The formatting options (`--format`) pretty-prints plugins output |
|
| 46 |
+using a Go template. |
|
| 47 |
+ |
|
| 48 |
+Valid placeholders for the Go template are listed below: |
|
| 49 |
+ |
|
| 50 |
+Placeholder | Description |
|
| 51 |
+---------------|------------------------------------------------------------------------------------------ |
|
| 52 |
+`.ID` | Plugin ID |
|
| 53 |
+`.Name` | Plugin name |
|
| 54 |
+`.Description` | Plugin description |
|
| 55 |
+`.Enabled` | Whether plugin is enabled or not |
|
| 56 |
+ |
|
| 57 |
+When using the `--format` option, the `plugin ls` command will either |
|
| 58 |
+output the data exactly as the template declares or, when using the |
|
| 59 |
+`table` directive, includes column headers as well. |
|
| 60 |
+ |
|
| 61 |
+The following example uses a template without headers and outputs the |
|
| 62 |
+`ID` and `Name` entries separated by a colon for all plugins: |
|
| 63 |
+ |
|
| 64 |
+```bash |
|
| 65 |
+$ docker plugin ls --format "{{.ID}}: {{.Name}}"
|
|
| 66 |
+4be01827a72e: tiborvass/no-remove |
|
| 67 |
+``` |
|
| 68 |
+ |
|
| 43 | 69 |
## Related information |
| 44 | 70 |
|
| 45 | 71 |
* [plugin create](plugin_create.md) |
| ... | ... |
@@ -401,3 +401,29 @@ func (s *DockerSuite) TestPluginIDPrefix(c *check.C) {
|
| 401 | 401 |
c.Assert(out, checker.Not(checker.Contains), pName) |
| 402 | 402 |
c.Assert(out, checker.Not(checker.Contains), pTag) |
| 403 | 403 |
} |
| 404 |
+ |
|
| 405 |
+func (s *DockerSuite) TestPluginListDefaultFormat(c *check.C) {
|
|
| 406 |
+ testRequires(c, DaemonIsLinux, Network, IsAmd64) |
|
| 407 |
+ |
|
| 408 |
+ config, err := ioutil.TempDir("", "config-file-")
|
|
| 409 |
+ c.Assert(err, check.IsNil) |
|
| 410 |
+ defer os.RemoveAll(config) |
|
| 411 |
+ |
|
| 412 |
+ err = ioutil.WriteFile(filepath.Join(config, "config.json"), []byte(`{"pluginsFormat": "raw"}`), 0644)
|
|
| 413 |
+ c.Assert(err, check.IsNil) |
|
| 414 |
+ |
|
| 415 |
+ out, _ := dockerCmd(c, "plugin", "install", "--grant-all-permissions", pName) |
|
| 416 |
+ c.Assert(strings.TrimSpace(out), checker.Contains, pName) |
|
| 417 |
+ |
|
| 418 |
+ out, _ = dockerCmd(c, "plugin", "inspect", "--format", "{{.ID}}", pNameWithTag)
|
|
| 419 |
+ id := strings.TrimSpace(out) |
|
| 420 |
+ |
|
| 421 |
+ // We expect the format to be in `raw + --no-trunc` |
|
| 422 |
+ expectedOutput := fmt.Sprintf(`plugin_id: %s |
|
| 423 |
+name: %s |
|
| 424 |
+description: A sample volume plugin for Docker |
|
| 425 |
+enabled: true`, id, pNameWithTag) |
|
| 426 |
+ |
|
| 427 |
+ out, _ = dockerCmd(c, "--config", config, "plugin", "ls", "--no-trunc") |
|
| 428 |
+ c.Assert(strings.TrimSpace(out), checker.Contains, expectedOutput) |
|
| 429 |
+} |