瀏覽代碼

implement secret management

Signed-off-by: aiordache <[email protected]>
Signed-off-by: Nicolas De Loof <[email protected]>
aiordache 5 年之前
父節點
當前提交
41aaf802e3
共有 9 個文件被更改,包括 417 次插入285 次删除
  1. 1 1
      ecs/Makefile
  2. 117 0
      ecs/cmd/commands/compose.go
  3. 147 0
      ecs/cmd/commands/secret.go
  4. 58 0
      ecs/cmd/main.go
  5. 0 261
      ecs/cmd/main/main.go
  6. 59 11
      ecs/pkg/amazon/sdk.go
  7. 10 8
      ecs/pkg/amazon/secrets.go
  8. 5 4
      ecs/pkg/compose/api.go
  9. 20 0
      ecs/pkg/docker/secret.go

+ 1 - 1
ecs/Makefile

@@ -2,7 +2,7 @@ clean:
 	rm -rf dist/
 
 build:
-	go build -v -o dist/docker-ecs cmd/main/main.go
+	go build -v -o dist/docker-ecs cmd/main.go
 
 test: ## Run tests
 	go test ./... -v

+ 117 - 0
ecs/cmd/commands/compose.go

@@ -0,0 +1,117 @@
+package commands
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/docker/ecs-plugin/pkg/amazon"
+	"github.com/docker/ecs-plugin/pkg/compose"
+	"github.com/spf13/cobra"
+)
+
+type ClusterOptions struct {
+	Profile string
+	Region  string
+	Cluster string
+}
+
+func ComposeCommand(clusteropts *ClusterOptions) *cobra.Command {
+	cmd := &cobra.Command{
+		Use: "compose",
+	}
+	opts := &compose.ProjectOptions{}
+	opts.AddFlags(cmd.Flags())
+
+	cmd.AddCommand(
+		ConvertCommand(clusteropts, opts),
+		UpCommand(clusteropts, opts),
+		DownCommand(clusteropts, opts),
+	)
+	return cmd
+}
+
+type upOptions struct {
+	loadBalancerArn string
+}
+
+func (o upOptions) LoadBalancerArn() *string {
+	if o.loadBalancerArn == "" {
+		return nil
+	}
+	return &o.loadBalancerArn
+}
+
+func ConvertCommand(clusteropts *ClusterOptions, projectOpts *compose.ProjectOptions) *cobra.Command {
+	cmd := &cobra.Command{
+		Use: "convert",
+		RunE: compose.WithProject(projectOpts, func(project *compose.Project, args []string) error {
+			client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
+			if err != nil {
+				return err
+			}
+			template, err := client.Convert(context.Background(), project)
+			if err != nil {
+				return err
+			}
+
+			j, err := template.JSON()
+			if err != nil {
+				fmt.Printf("Failed to generate JSON: %s\n", err)
+			} else {
+				fmt.Printf("%s\n", string(j))
+			}
+			return nil
+		}),
+	}
+	return cmd
+}
+
+func UpCommand(clusteropts *ClusterOptions, projectOpts *compose.ProjectOptions) *cobra.Command {
+	opts := upOptions{}
+	cmd := &cobra.Command{
+		Use: "up",
+		RunE: compose.WithProject(projectOpts, func(project *compose.Project, args []string) error {
+			client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
+			if err != nil {
+				return err
+			}
+			return client.ComposeUp(context.Background(), project)
+		}),
+	}
+	cmd.Flags().StringVar(&opts.loadBalancerArn, "load-balancer", "", "")
+	return cmd
+}
+
+type downOptions struct {
+	DeleteCluster bool
+}
+
+func DownCommand(clusteropts *ClusterOptions, projectOpts *compose.ProjectOptions) *cobra.Command {
+	opts := downOptions{}
+	cmd := &cobra.Command{
+		Use: "down",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
+			if err != nil {
+				return err
+			}
+			if len(args) == 0 {
+				project, err := compose.ProjectFromOptions(projectOpts)
+				if err != nil {
+					return err
+				}
+				return client.ComposeDown(context.Background(), project.Name, opts.DeleteCluster)
+			}
+			// project names passed as parameters
+			for _, name := range args {
+				err := client.ComposeDown(context.Background(), name, opts.DeleteCluster)
+				if err != nil {
+					return err
+				}
+			}
+			return nil
+		},
+	}
+	cmd.Flags().BoolVar(&opts.DeleteCluster, "delete-cluster", false, "Delete cluster")
+	return cmd
+}

+ 147 - 0
ecs/cmd/commands/secret.go

@@ -0,0 +1,147 @@
+package commands
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"io"
+	"os"
+	"strings"
+	"text/tabwriter"
+
+	"github.com/docker/ecs-plugin/pkg/amazon"
+	"github.com/docker/ecs-plugin/pkg/docker"
+	"github.com/spf13/cobra"
+)
+
+type createSecretOptions struct {
+	Label string
+}
+
+type deleteSecretOptions struct {
+	recover bool
+}
+
+func SecretCommand(clusteropts *ClusterOptions) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "secret",
+		Short: "Manages secrets",
+	}
+
+	cmd.AddCommand(
+		CreateSecret(clusteropts),
+		InspectSecret(clusteropts),
+		ListSecrets(clusteropts),
+		DeleteSecret(clusteropts),
+	)
+	return cmd
+}
+
+func CreateSecret(clusteropts *ClusterOptions) *cobra.Command {
+	//opts := createSecretOptions{}
+	cmd := &cobra.Command{
+		Use:   "create NAME SECRET",
+		Short: "Creates a secret.",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
+			if err != nil {
+				return err
+			}
+			if len(args) == 0 {
+				return errors.New("Missing mandatory parameter: NAME")
+			}
+			name := args[0]
+			secret := args[1]
+			id, err := client.CreateSecret(context.Background(), name, secret)
+			fmt.Println(id)
+			return err
+		},
+	}
+	return cmd
+}
+
+func InspectSecret(clusteropts *ClusterOptions) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "inspect ID",
+		Short: "Displays secret details",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
+			if err != nil {
+				return err
+			}
+			if len(args) == 0 {
+				return errors.New("Missing mandatory parameter: ID")
+			}
+			id := args[0]
+			secret, err := client.InspectSecret(context.Background(), id)
+			if err != nil {
+				return err
+			}
+			out, err := secret.ToJSON()
+			if err != nil {
+				return err
+			}
+			fmt.Println(out)
+			return nil
+		},
+	}
+	return cmd
+}
+
+func ListSecrets(clusteropts *ClusterOptions) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:     "list",
+		Aliases: []string{"ls"},
+		Short:   "List secrets stored for the existing account.",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
+			if err != nil {
+				return err
+			}
+			secrets, err := client.ListSecrets(context.Background())
+			if err != nil {
+				return err
+			}
+
+			printList(os.Stdout, secrets)
+			return nil
+		},
+	}
+	return cmd
+}
+
+func DeleteSecret(clusteropts *ClusterOptions) *cobra.Command {
+	opts := deleteSecretOptions{}
+	cmd := &cobra.Command{
+		Use:     "delete NAME",
+		Aliases: []string{"rm", "remove"},
+		Short:   "Removes a secret.",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
+			if err != nil {
+				return err
+			}
+			if len(args) == 0 {
+				return errors.New("Missing mandatory parameter: [NAME]")
+			}
+			return client.DeleteSecret(context.Background(), args[0], opts.recover)
+		},
+	}
+	cmd.Flags().BoolVar(&opts.recover, "recover", false, "Enable recovery.")
+	return cmd
+}
+
+func printList(out io.Writer, secrets []docker.Secret) {
+	printSection(out, len(secrets), func(w io.Writer) {
+		for _, secret := range secrets {
+			fmt.Fprintf(w, "%s\t%s\t%s\n", secret.ID, secret.Name, secret.Description)
+		}
+	}, "ID", "NAME", "DESCRIPTION")
+}
+
+func printSection(out io.Writer, len int, printer func(io.Writer), headers ...string) {
+	w := tabwriter.NewWriter(out, 20, 1, 3, ' ', 0)
+	fmt.Fprintln(w, strings.Join(headers, "\t"))
+	printer(w)
+	w.Flush()
+}

+ 58 - 0
ecs/cmd/main.go

@@ -0,0 +1,58 @@
+package main
+
+import (
+	"fmt"
+
+	"github.com/docker/cli/cli-plugins/manager"
+	"github.com/docker/cli/cli-plugins/plugin"
+	"github.com/docker/cli/cli/command"
+	commands "github.com/docker/ecs-plugin/cmd/commands"
+	"github.com/spf13/cobra"
+)
+
+const version = "0.0.1"
+
+func main() {
+	plugin.Run(func(dockerCli command.Cli) *cobra.Command {
+		cmd := NewRootCmd("ecs", dockerCli)
+		return cmd
+	}, manager.Metadata{
+		SchemaVersion: "0.1.0",
+		Vendor:        "Docker Inc.",
+		Version:       version,
+		Experimental:  true,
+	})
+}
+
+// NewRootCmd returns the base root command.
+func NewRootCmd(name string, dockerCli command.Cli) *cobra.Command {
+	var opts commands.ClusterOptions
+
+	cmd := &cobra.Command{
+		Short:       "Docker ECS",
+		Long:        `run multi-container applications on Amazon ECS.`,
+		Use:         name,
+		Annotations: map[string]string{"experimentalCLI": "true"},
+	}
+	cmd.AddCommand(
+		VersionCommand(),
+		commands.ComposeCommand(&opts),
+		commands.SecretCommand(&opts),
+	)
+	cmd.Flags().StringVarP(&opts.Profile, "profile", "p", "default", "AWS Profile")
+	cmd.Flags().StringVarP(&opts.Cluster, "cluster", "c", "default", "ECS cluster")
+	cmd.Flags().StringVarP(&opts.Region, "region", "r", "", "AWS region")
+
+	return cmd
+}
+
+func VersionCommand() *cobra.Command {
+	return &cobra.Command{
+		Use:   "version",
+		Short: "Show version.",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			fmt.Printf("Docker ECS plugin %s\n", version)
+			return nil
+		},
+	}
+}

+ 0 - 261
ecs/cmd/main/main.go

@@ -1,261 +0,0 @@
-package main
-
-import (
-	"context"
-	"errors"
-	"fmt"
-
-	"github.com/docker/cli/cli-plugins/manager"
-	"github.com/docker/cli/cli-plugins/plugin"
-	"github.com/docker/cli/cli/command"
-	"github.com/docker/ecs-plugin/pkg/amazon"
-	"github.com/docker/ecs-plugin/pkg/compose"
-	"github.com/spf13/cobra"
-)
-
-const version = "0.0.1"
-
-func main() {
-	plugin.Run(func(dockerCli command.Cli) *cobra.Command {
-		cmd := NewRootCmd("ecs", dockerCli)
-		return cmd
-	}, manager.Metadata{
-		SchemaVersion: "0.1.0",
-		Vendor:        "Docker Inc.",
-		Version:       version,
-		Experimental:  true,
-	})
-}
-
-type clusterOptions struct {
-	profile string
-	region  string
-	cluster string
-}
-
-// NewRootCmd returns the base root command.
-func NewRootCmd(name string, dockerCli command.Cli) *cobra.Command {
-	var opts clusterOptions
-
-	cmd := &cobra.Command{
-		Short:       "Docker ECS",
-		Long:        `run multi-container applications on Amazon ECS.`,
-		Use:         name,
-		Annotations: map[string]string{"experimentalCLI": "true"},
-	}
-	cmd.AddCommand(
-		VersionCommand(),
-		ComposeCommand(&opts),
-		SecretCommand(&opts),
-	)
-	cmd.Flags().StringVarP(&opts.profile, "profile", "p", "default", "AWS Profile")
-	cmd.Flags().StringVarP(&opts.cluster, "cluster", "c", "default", "ECS cluster")
-	cmd.Flags().StringVarP(&opts.region, "region", "r", "", "AWS region")
-
-	return cmd
-}
-
-func VersionCommand() *cobra.Command {
-	return &cobra.Command{
-		Use:   "version",
-		Short: "Show version.",
-		RunE: func(cmd *cobra.Command, args []string) error {
-			fmt.Printf("Docker ECS plugin %s\n", version)
-			return nil
-		},
-	}
-}
-
-func ComposeCommand(clusteropts *clusterOptions) *cobra.Command {
-	cmd := &cobra.Command{
-		Use: "compose",
-	}
-	opts := &compose.ProjectOptions{}
-	opts.AddFlags(cmd.Flags())
-
-	cmd.AddCommand(
-		ConvertCommand(clusteropts, opts),
-		UpCommand(clusteropts, opts),
-		DownCommand(clusteropts, opts),
-	)
-	return cmd
-}
-
-type upOptions struct {
-	loadBalancerArn string
-}
-
-func (o upOptions) LoadBalancerArn() *string {
-	if o.loadBalancerArn == "" {
-		return nil
-	}
-	return &o.loadBalancerArn
-}
-
-func ConvertCommand(clusteropts *clusterOptions, projectOpts *compose.ProjectOptions) *cobra.Command {
-	cmd := &cobra.Command{
-		Use: "convert",
-		RunE: compose.WithProject(projectOpts, func(project *compose.Project, args []string) error {
-			client, err := amazon.NewClient(clusteropts.profile, clusteropts.cluster, clusteropts.region)
-			if err != nil {
-				return err
-			}
-			template, err := client.Convert(context.Background(), project)
-			if err != nil {
-				return err
-			}
-
-			j, err := template.JSON()
-			if err != nil {
-				fmt.Printf("Failed to generate JSON: %s\n", err)
-			} else {
-				fmt.Printf("%s\n", string(j))
-			}
-			return nil
-		}),
-	}
-	return cmd
-}
-
-func UpCommand(clusteropts *clusterOptions, projectOpts *compose.ProjectOptions) *cobra.Command {
-	opts := upOptions{}
-	cmd := &cobra.Command{
-		Use: "up",
-		RunE: compose.WithProject(projectOpts, func(project *compose.Project, args []string) error {
-			client, err := amazon.NewClient(clusteropts.profile, clusteropts.cluster, clusteropts.region)
-			if err != nil {
-				return err
-			}
-			return client.ComposeUp(context.Background(), project)
-		}),
-	}
-	cmd.Flags().StringVar(&opts.loadBalancerArn, "load-balancer", "", "")
-	return cmd
-}
-
-type downOptions struct {
-	DeleteCluster bool
-}
-
-func DownCommand(clusteropts *clusterOptions, projectOpts *compose.ProjectOptions) *cobra.Command {
-	opts := downOptions{}
-	cmd := &cobra.Command{
-		Use: "down",
-		RunE: func(cmd *cobra.Command, args []string) error {
-			client, err := amazon.NewClient(clusteropts.profile, clusteropts.cluster, clusteropts.region)
-			if err != nil {
-				return err
-			}
-			if len(args) == 0 {
-				project, err := compose.ProjectFromOptions(projectOpts)
-				if err != nil {
-					return err
-				}
-				return client.ComposeDown(context.Background(), project.Name, opts.DeleteCluster)
-			}
-			// project names passed as parameters
-			for _, name := range args {
-				err := client.ComposeDown(context.Background(), name, opts.DeleteCluster)
-				if err != nil {
-					return err
-				}
-			}
-			return nil
-		},
-	}
-	cmd.Flags().BoolVar(&opts.DeleteCluster, "delete-cluster", false, "Delete cluster")
-	return cmd
-}
-
-func SecretCommand(clusteropts *clusterOptions) *cobra.Command {
-	cmd := &cobra.Command{
-		Use: "secret",
-	}
-	opts := &compose.ProjectOptions{}
-	opts.AddFlags(cmd.Flags())
-
-	cmd.AddCommand(
-		CreateSecret(clusteropts),
-		InspectSecret(clusteropts),
-		ListSecrets(clusteropts),
-		DeleteSecret(clusteropts),
-	)
-	return cmd
-}
-
-type createSecretOptions struct {
-	Label string
-}
-
-func CreateSecret(clusteropts *clusterOptions) *cobra.Command {
-	//opts := createSecretOptions{}
-	cmd := &cobra.Command{
-		Use: "create [NAME]",
-		RunE: func(cmd *cobra.Command, args []string) error {
-			client, err := amazon.NewClient(clusteropts.profile, clusteropts.cluster, clusteropts.region)
-			if err != nil {
-				return err
-			}
-			if len(args) == 0 {
-				return errors.New("Missing mandatory parameter: [NAME]")
-			}
-			name := args[0]
-			content := "blabla"
-			id, err := client.CreateSecret(context.Background(), name, content)
-			fmt.Println(id)
-			return err
-		},
-	}
-	//cmd.Flags().BoolVar(&opts.Label, "label", false, "Secret label")
-	return cmd
-}
-
-func InspectSecret(clusteropts *clusterOptions) *cobra.Command {
-	cmd := &cobra.Command{
-		Use: "inspect [NAME]",
-		RunE: func(cmd *cobra.Command, args []string) error {
-			client, err := amazon.NewClient(clusteropts.profile, clusteropts.cluster, clusteropts.region)
-			if err != nil {
-				return err
-			}
-			if len(args) == 0 {
-				return errors.New("Missing mandatory parameter: [NAME]")
-			}
-			name := args[0]
-			return client.InspectSecret(context.Background(), name)
-		},
-	}
-	return cmd
-}
-
-func ListSecrets(clusteropts *clusterOptions) *cobra.Command {
-	cmd := &cobra.Command{
-		Use:     "list",
-		Aliases: []string{"ls"},
-		RunE: func(cmd *cobra.Command, args []string) error {
-			client, err := amazon.NewClient(clusteropts.profile, clusteropts.cluster, clusteropts.region)
-			if err != nil {
-				return err
-			}
-			return client.ListSecrets(context.Background())
-		},
-	}
-	return cmd
-}
-
-func DeleteSecret(clusteropts *clusterOptions) *cobra.Command {
-	cmd := &cobra.Command{
-		Use: "delete [NAME]",
-		RunE: func(cmd *cobra.Command, args []string) error {
-			client, err := amazon.NewClient(clusteropts.profile, clusteropts.cluster, clusteropts.region)
-			if err != nil {
-				return err
-			}
-			if len(args) == 0 {
-				return errors.New("Missing mandatory parameter: [NAME]")
-			}
-			return client.DeleteSecret(context.Background(), args[0])
-		},
-	}
-	return cmd
-}

+ 59 - 11
ecs/pkg/amazon/sdk.go

@@ -22,6 +22,8 @@ import (
 	"github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface"
 	cf "github.com/awslabs/goformation/v4/cloudformation"
 	"github.com/sirupsen/logrus"
+
+	"github.com/docker/ecs-plugin/pkg/docker"
 )
 
 type sdk struct {
@@ -198,22 +200,68 @@ func (s sdk) DeleteStack(ctx context.Context, name string) error {
 	return err
 }
 
-func (s sdk) CreateSecret(ctx context.Context, name string, content string) (string, error) {
+func (s sdk) CreateSecret(ctx context.Context, name string, secret string) (string, error) {
 	logrus.Debug("Create secret " + name)
-	return "test", nil
+	response, err := s.SM.CreateSecret(&secretsmanager.CreateSecretInput{Name: &name, SecretString: &secret})
+	if err != nil {
+		return "", err
+	}
+	return *response.ARN, nil
 }
 
-func (s sdk) InspectSecret(ctx context.Context, name string) error {
-	fmt.Printf("... done. \n")
-	return nil
+func (s sdk) InspectSecret(ctx context.Context, id string) (docker.Secret, error) {
+	logrus.Debug("Inspect secret " + id)
+	response, err := s.SM.DescribeSecret(&secretsmanager.DescribeSecretInput{SecretId: &id})
+	if err != nil {
+		return docker.Secret{}, err
+	}
+	labels := map[string]string{}
+	for _, tag := range response.Tags {
+		labels[*tag.Key] = *tag.Value
+	}
+	secret := docker.Secret{
+		ID:     *response.ARN,
+		Name:   *response.Name,
+		Labels: labels,
+	}
+	if response.Description != nil {
+		secret.Description = *response.Description
+	}
+	return secret, nil
 }
 
-func (s sdk) ListSecrets(ctx context.Context) error {
-	fmt.Printf("... done. \n")
-	return nil
+func (s sdk) ListSecrets(ctx context.Context) ([]docker.Secret, error) {
+
+	logrus.Debug("List secrets ...")
+	response, err := s.SM.ListSecrets(&secretsmanager.ListSecretsInput{})
+	if err != nil {
+		return []docker.Secret{}, err
+	}
+	var secrets []docker.Secret
+
+	for _, sec := range response.SecretList {
+
+		labels := map[string]string{}
+		for _, tag := range sec.Tags {
+			labels[*tag.Key] = *tag.Value
+		}
+		description := ""
+		if sec.Description != nil {
+			description = *sec.Description
+		}
+		secrets = append(secrets, docker.Secret{
+			ID:          *sec.ARN,
+			Name:        *sec.Name,
+			Labels:      labels,
+			Description: description,
+		})
+	}
+	return secrets, nil
 }
 
-func (s sdk) DeleteSecret(ctx context.Context, name string) error {
-	fmt.Printf("... done. \n")
-	return nil
+func (s sdk) DeleteSecret(ctx context.Context, id string, recover bool) error {
+	logrus.Debug("List secrets ...")
+	force := !recover
+	_, err := s.SM.DeleteSecret(&secretsmanager.DeleteSecretInput{SecretId: &id, ForceDeleteWithoutRecovery: &force})
+	return err
 }

+ 10 - 8
ecs/pkg/amazon/secrets.go

@@ -2,27 +2,29 @@ package amazon
 
 import (
 	"context"
+
+	"github.com/docker/ecs-plugin/pkg/docker"
 )
 
 type secretsAPI interface {
 	CreateSecret(ctx context.Context, name string, content string) (string, error)
-	InspectSecret(ctx context.Context, name string) error
-	ListSecrets(ctx context.Context) error
-	DeleteSecret(ctx context.Context, name string) error
+	InspectSecret(ctx context.Context, id string) (docker.Secret, error)
+	ListSecrets(ctx context.Context) ([]docker.Secret, error)
+	DeleteSecret(ctx context.Context, id string, recover bool) error
 }
 
 func (c client) CreateSecret(ctx context.Context, name string, content string) (string, error) {
 	return c.api.CreateSecret(ctx, name, content)
 }
 
-func (c client) InspectSecret(ctx context.Context, name string) error {
-	return c.api.InspectSecret(ctx, name)
+func (c client) InspectSecret(ctx context.Context, id string) (docker.Secret, error) {
+	return c.api.InspectSecret(ctx, id)
 }
 
-func (c client) ListSecrets(ctx context.Context) error {
+func (c client) ListSecrets(ctx context.Context) ([]docker.Secret, error) {
 	return c.api.ListSecrets(ctx)
 }
 
-func (c client) DeleteSecret(ctx context.Context, name string) error {
-	return c.api.DeleteSecret(ctx, name)
+func (c client) DeleteSecret(ctx context.Context, id string, recover bool) error {
+	return c.api.DeleteSecret(ctx, id, recover)
 }

+ 5 - 4
ecs/pkg/compose/api.go

@@ -4,6 +4,7 @@ import (
 	"context"
 
 	"github.com/awslabs/goformation/v4/cloudformation"
+	"github.com/docker/ecs-plugin/pkg/docker"
 )
 
 type API interface {
@@ -11,8 +12,8 @@ type API interface {
 	ComposeUp(ctx context.Context, project *Project) error
 	ComposeDown(ctx context.Context, projectName string, deleteCluster bool) error
 
-	CreateSecret(ctx context.Context, name string, content string) (string, error)
-	InspectSecret(ctx context.Context, name string) error
-	ListSecrets(ctx context.Context) error
-	DeleteSecret(ctx context.Context, name string) error
+	CreateSecret(ctx context.Context, name string, secret string) (string, error)
+	InspectSecret(ctx context.Context, id string) (docker.Secret, error)
+	ListSecrets(ctx context.Context) ([]docker.Secret, error)
+	DeleteSecret(ctx context.Context, id string, recover bool) error
 }

+ 20 - 0
ecs/pkg/docker/secret.go

@@ -0,0 +1,20 @@
+package docker
+
+import (
+	"encoding/json"
+)
+
+type Secret struct {
+	ID          string            `json:"ID"`
+	Name        string            `json:"Name"`
+	Labels      map[string]string `json:"Labels"`
+	Description string            `json:"Description"`
+}
+
+func (s Secret) ToJSON() (string, error) {
+	b, err := json.MarshalIndent(&s, "", "\t")
+	if err != nil {
+		return "", err
+	}
+	return string(b), nil
+}