Skip to content

Commit 27be505

Browse files
committed
Extract command for secrets
Signed-off-by: David Gageot <[email protected]>
1 parent 22a6f49 commit 27be505

File tree

7 files changed

+141
-147
lines changed

7 files changed

+141
-147
lines changed

cmd/docker-mcp/commands/root.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/docker"
1515
"github.com/docker/mcp-gateway/cmd/docker-mcp/oauth"
1616
"github.com/docker/mcp-gateway/cmd/docker-mcp/secret-management/policy"
17-
"github.com/docker/mcp-gateway/cmd/docker-mcp/secret-management/secret"
1817
"github.com/docker/mcp-gateway/cmd/docker-mcp/version"
1918
)
2019

@@ -74,7 +73,7 @@ func Root(ctx context.Context, cwd string, dockerCli command.Cli) *cobra.Command
7473

7574
dockerClient := docker.NewClient(dockerCli)
7675

77-
cmd.AddCommand(secret.NewSecretsCmd(dockerClient))
76+
cmd.AddCommand(secretCommand(dockerClient))
7877
cmd.AddCommand(policy.NewPolicyCmd())
7978
cmd.AddCommand(oauth.NewOAuthCmd())
8079
cmd.AddCommand(client.NewClientCmd(cwd))

cmd/docker-mcp/commands/secret.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package commands
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/spf13/cobra"
9+
10+
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/docker"
11+
"github.com/docker/mcp-gateway/cmd/docker-mcp/secret-management/secret"
12+
)
13+
14+
const setExample = `
15+
# Using secrets for postgres password with default policy:
16+
docker mcp secret set POSTGRES_PASSWORD=my-secret-password
17+
docker run -d -l x-secret:POSTGRES_PASSWORD=/pwd.txt -e POSTGRES_PASSWORD_FILE=/pwd.txt -p 5432 postgres
18+
19+
# Or pass the secret via STDIN:
20+
echo my-secret-password > pwd.txt
21+
cat pwd.txt | docker mcp secret set POSTGRES_PASSWORD
22+
`
23+
24+
func secretCommand(docker docker.Client) *cobra.Command {
25+
cmd := &cobra.Command{
26+
Use: "secret",
27+
Short: "Manage secrets",
28+
Example: strings.Trim(setExample, "\n"),
29+
}
30+
cmd.AddCommand(rmCommand())
31+
cmd.AddCommand(listCommand())
32+
cmd.AddCommand(setCommand())
33+
cmd.AddCommand(exportCommand(docker))
34+
return cmd
35+
}
36+
37+
func rmCommand() *cobra.Command {
38+
var opts secret.RmOpts
39+
cmd := &cobra.Command{
40+
Use: "rm name1 name2 ...",
41+
Short: "Remove secrets from Docker Desktop's secret store",
42+
RunE: func(cmd *cobra.Command, args []string) error {
43+
if err := validateRmArgs(args, opts); err != nil {
44+
return err
45+
}
46+
return secret.Remove(cmd.Context(), args, opts)
47+
},
48+
}
49+
flags := cmd.Flags()
50+
flags.BoolVar(&opts.All, "all", false, "Remove all secrets")
51+
return cmd
52+
}
53+
54+
func validateRmArgs(args []string, opts secret.RmOpts) error {
55+
if len(args) == 0 && !opts.All {
56+
return errors.New("either provide a secret name or use --all to remove all secrets")
57+
}
58+
return nil
59+
}
60+
61+
func listCommand() *cobra.Command {
62+
var opts secret.ListOptions
63+
cmd := &cobra.Command{
64+
Use: "ls",
65+
Short: "List all secret names in Docker Desktop's secret store",
66+
Args: cobra.NoArgs,
67+
RunE: func(cmd *cobra.Command, _ []string) error {
68+
return secret.List(cmd.Context(), opts)
69+
},
70+
}
71+
flags := cmd.Flags()
72+
flags.BoolVar(&opts.JSON, "json", false, "Print as JSON.")
73+
return cmd
74+
}
75+
76+
func setCommand() *cobra.Command {
77+
opts := &secret.SetOpts{}
78+
cmd := &cobra.Command{
79+
Use: "set key[=value]",
80+
Short: "Set a secret in Docker Desktop's secret store",
81+
Example: strings.Trim(setExample, "\n"),
82+
Args: cobra.ExactArgs(1),
83+
RunE: func(cmd *cobra.Command, args []string) error {
84+
if !secret.IsValidProvider(opts.Provider) {
85+
return fmt.Errorf("invalid provider: %s", opts.Provider)
86+
}
87+
var s secret.Secret
88+
if isNotImplicitReadFromStdinSyntax(args, *opts) {
89+
va, err := secret.ParseArg(args[0], *opts)
90+
if err != nil {
91+
return err
92+
}
93+
s = *va
94+
} else {
95+
val, err := secret.MappingFromSTDIN(cmd.Context(), args[0])
96+
if err != nil {
97+
return err
98+
}
99+
s = *val
100+
}
101+
return secret.Set(cmd.Context(), s, *opts)
102+
},
103+
}
104+
flags := cmd.Flags()
105+
flags.StringVar(&opts.Provider, "provider", "", "Supported: credstore, oauth/<provider>")
106+
return cmd
107+
}
108+
109+
func isNotImplicitReadFromStdinSyntax(args []string, opts secret.SetOpts) bool {
110+
return strings.Contains(args[0], "=") || len(args) > 1 || opts.Provider != ""
111+
}
112+
113+
func exportCommand(docker docker.Client) *cobra.Command {
114+
return &cobra.Command{
115+
Use: "export [server1] [server2] ...",
116+
Short: "Export secrets for the specified servers",
117+
Hidden: true,
118+
Args: cobra.MinimumNArgs(1),
119+
RunE: func(cmd *cobra.Command, args []string) error {
120+
secrets, err := secret.Export(cmd.Context(), docker, args)
121+
if err != nil {
122+
return err
123+
}
124+
125+
for name, secret := range secrets {
126+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s=%s\n", name, secret)
127+
}
128+
129+
return nil
130+
},
131+
}
132+
}

cmd/docker-mcp/secret-management/secret/export.go

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,10 @@ import (
55
"fmt"
66
"sort"
77

8-
"github.com/spf13/cobra"
9-
108
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/catalog"
119
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/docker"
1210
)
1311

14-
func exportCommand(docker docker.Client) *cobra.Command {
15-
return &cobra.Command{
16-
Use: "export [server1] [server2] ...",
17-
Short: "Export secrets for the specified servers",
18-
Hidden: true,
19-
Args: cobra.MinimumNArgs(1),
20-
RunE: func(cmd *cobra.Command, args []string) error {
21-
secrets, err := Export(cmd.Context(), docker, args)
22-
if err != nil {
23-
return err
24-
}
25-
26-
for name, secret := range secrets {
27-
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s=%s\n", name, secret)
28-
}
29-
30-
return nil
31-
},
32-
}
33-
}
34-
3512
func Export(ctx context.Context, docker docker.Client, serverNames []string) (map[string]string, error) {
3613
catalog, err := catalog.Get(ctx)
3714
if err != nil {

cmd/docker-mcp/secret-management/secret/list.go

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import (
55
"encoding/json"
66
"fmt"
77

8-
"github.com/spf13/cobra"
9-
108
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/desktop"
119
"github.com/docker/mcp-gateway/cmd/docker-mcp/secret-management/formatting"
1210
)
@@ -15,21 +13,6 @@ type ListOptions struct {
1513
JSON bool
1614
}
1715

18-
func listCommand() *cobra.Command {
19-
var opts ListOptions
20-
cmd := &cobra.Command{
21-
Use: "ls",
22-
Short: "List all secret names in Docker Desktop's secret store",
23-
Args: cobra.NoArgs,
24-
RunE: func(cmd *cobra.Command, _ []string) error {
25-
return List(cmd.Context(), opts)
26-
},
27-
}
28-
flags := cmd.Flags()
29-
flags.BoolVar(&opts.JSON, "json", false, "Print as JSON.")
30-
return cmd
31-
}
32-
3316
func List(ctx context.Context, opts ListOptions) error {
3417
l, err := desktop.NewSecretsClient().ListJfsSecrets(ctx)
3518
if err != nil {

cmd/docker-mcp/secret-management/secret/rm.go

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,13 @@ import (
55
"errors"
66
"fmt"
77

8-
"github.com/spf13/cobra"
9-
108
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/desktop"
119
)
1210

1311
type RmOpts struct {
1412
All bool
1513
}
1614

17-
func rmCommand() *cobra.Command {
18-
var opts RmOpts
19-
cmd := &cobra.Command{
20-
Use: "rm name1 name2 ...",
21-
Short: "Remove secrets from Docker Desktop's secret store",
22-
RunE: func(cmd *cobra.Command, args []string) error {
23-
if err := validateArgs(args, opts); err != nil {
24-
return err
25-
}
26-
return Remove(cmd.Context(), args, opts)
27-
},
28-
}
29-
flags := cmd.Flags()
30-
flags.BoolVar(&opts.All, "all", false, "Remove all secrets")
31-
return cmd
32-
}
33-
34-
func validateArgs(args []string, opts RmOpts) error {
35-
if len(args) == 0 && !opts.All {
36-
return errors.New("either provide a secret name or use --all to remove all secrets")
37-
}
38-
return nil
39-
}
40-
4115
func Remove(ctx context.Context, names []string, opts RmOpts) error {
4216
c := desktop.NewSecretsClient()
4317
if opts.All && len(names) == 0 {

cmd/docker-mcp/secret-management/secret/root.go

Lines changed: 0 additions & 22 deletions
This file was deleted.

cmd/docker-mcp/secret-management/secret/set.go

Lines changed: 8 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,10 @@ import (
66
"os"
77
"strings"
88

9-
"github.com/spf13/cobra"
10-
119
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/desktop"
1210
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/tui"
1311
)
1412

15-
const setExample = `
16-
# Using secrets for postgres password with default policy:
17-
docker mcp secret set POSTGRES_PASSWORD=my-secret-password
18-
docker run -d -l x-secret:POSTGRES_PASSWORD=/pwd.txt -e POSTGRES_PASSWORD_FILE=/pwd.txt -p 5432 postgres
19-
20-
# Or pass the secret via STDIN:
21-
echo my-secret-password > pwd.txt
22-
cat pwd.txt | docker mcp secret set POSTGRES_PASSWORD
23-
`
24-
2513
const (
2614
Credstore = "credstore"
2715
)
@@ -30,79 +18,42 @@ type SetOpts struct {
3018
Provider string
3119
}
3220

33-
func setCommand() *cobra.Command {
34-
opts := &SetOpts{}
35-
cmd := &cobra.Command{
36-
Use: "set key[=value]",
37-
Short: "Set a secret in Docker Desktop's secret store",
38-
Example: strings.Trim(setExample, "\n"),
39-
Args: cobra.ExactArgs(1),
40-
RunE: func(cmd *cobra.Command, args []string) error {
41-
if !isValidProvider(opts.Provider) {
42-
return fmt.Errorf("invalid provider: %s", opts.Provider)
43-
}
44-
var s secret
45-
if isNotImplicitReadFromStdinSyntax(args, *opts) {
46-
va, err := parseArg(args[0], *opts)
47-
if err != nil {
48-
return err
49-
}
50-
s = *va
51-
} else {
52-
val, err := secretMappingFromSTDIN(cmd.Context(), args[0])
53-
if err != nil {
54-
return err
55-
}
56-
s = *val
57-
}
58-
return Set(cmd.Context(), s, *opts)
59-
},
60-
}
61-
flags := cmd.Flags()
62-
flags.StringVar(&opts.Provider, "provider", "", "Supported: credstore, oauth/<provider>")
63-
return cmd
64-
}
65-
66-
func isNotImplicitReadFromStdinSyntax(args []string, opts SetOpts) bool {
67-
return strings.Contains(args[0], "=") || len(args) > 1 || opts.Provider != ""
68-
}
69-
70-
func secretMappingFromSTDIN(ctx context.Context, key string) (*secret, error) {
21+
func MappingFromSTDIN(ctx context.Context, key string) (*Secret, error) {
7122
data, err := tui.ReadAllWithContext(ctx, os.Stdin)
7223
if err != nil {
7324
return nil, err
7425
}
7526

76-
return &secret{
27+
return &Secret{
7728
key: key,
7829
val: string(data),
7930
}, nil
8031
}
8132

82-
type secret struct {
33+
type Secret struct {
8334
key string
8435
val string
8536
}
8637

87-
func parseArg(arg string, opts SetOpts) (*secret, error) {
38+
func ParseArg(arg string, opts SetOpts) (*Secret, error) {
8839
if !isDirectValueProvider(opts.Provider) && strings.Contains(arg, "=") {
8940
return nil, fmt.Errorf("provider cannot be used with key=value pairs: %s", arg)
9041
}
9142
if !isDirectValueProvider(opts.Provider) {
92-
return &secret{key: arg, val: ""}, nil
43+
return &Secret{key: arg, val: ""}, nil
9344
}
9445
parts := strings.Split(arg, "=")
9546
if len(parts) != 2 {
9647
return nil, fmt.Errorf("no key=value pair: %s", arg)
9748
}
98-
return &secret{key: parts[0], val: parts[1]}, nil
49+
return &Secret{key: parts[0], val: parts[1]}, nil
9950
}
10051

10152
func isDirectValueProvider(provider string) bool {
10253
return provider == "" || provider == Credstore
10354
}
10455

105-
func Set(ctx context.Context, s secret, opts SetOpts) error {
56+
func Set(ctx context.Context, s Secret, opts SetOpts) error {
10657
if opts.Provider == Credstore {
10758
p := NewCredStoreProvider()
10859
if err := p.SetSecret(s.key, s.val); err != nil {
@@ -116,7 +67,7 @@ func Set(ctx context.Context, s secret, opts SetOpts) error {
11667
})
11768
}
11869

119-
func isValidProvider(provider string) bool {
70+
func IsValidProvider(provider string) bool {
12071
if provider == "" {
12172
return true
12273
}

0 commit comments

Comments
 (0)