From aa8587095f4b4e23d494b4b3f7997e0528eb8e64 Mon Sep 17 00:00:00 2001 From: Guillaume Lours Date: Mon, 4 May 2020 18:35:23 +0200 Subject: [PATCH] Add setup command to define a docker context for ecs-plugin Signed-off-by: Guillaume Lours Signed-off-by: Nicolas De Loof --- ecs/cmd/commands/compose.go | 15 +++----- ecs/cmd/commands/secret.go | 10 +++--- ecs/cmd/commands/setup.go | 33 ++++++++++++++++++ ecs/cmd/main/main.go | 21 ++++++++---- ecs/go.mod | 1 + ecs/pkg/docker/contextStore.go | 63 ++++++++++++++++++++++++++++++++++ 6 files changed, 121 insertions(+), 22 deletions(-) create mode 100644 ecs/cmd/commands/setup.go create mode 100644 ecs/pkg/docker/contextStore.go diff --git a/ecs/cmd/commands/compose.go b/ecs/cmd/commands/compose.go index 972477b3a..459a48813 100644 --- a/ecs/cmd/commands/compose.go +++ b/ecs/cmd/commands/compose.go @@ -6,16 +6,11 @@ import ( "github.com/docker/ecs-plugin/pkg/amazon" "github.com/docker/ecs-plugin/pkg/compose" + "github.com/docker/ecs-plugin/pkg/docker" "github.com/spf13/cobra" ) -type ClusterOptions struct { - Profile string - Region string - Cluster string -} - -func ComposeCommand(clusteropts *ClusterOptions) *cobra.Command { +func ComposeCommand(clusteropts *docker.AwsContext) *cobra.Command { cmd := &cobra.Command{ Use: "compose", } @@ -41,7 +36,7 @@ func (o upOptions) LoadBalancerArn() *string { return &o.loadBalancerArn } -func ConvertCommand(clusteropts *ClusterOptions, projectOpts *compose.ProjectOptions) *cobra.Command { +func ConvertCommand(clusteropts *docker.AwsContext, projectOpts *compose.ProjectOptions) *cobra.Command { cmd := &cobra.Command{ Use: "convert", RunE: compose.WithProject(projectOpts, func(project *compose.Project, args []string) error { @@ -66,7 +61,7 @@ func ConvertCommand(clusteropts *ClusterOptions, projectOpts *compose.ProjectOpt return cmd } -func UpCommand(clusteropts *ClusterOptions, projectOpts *compose.ProjectOptions) *cobra.Command { +func UpCommand(clusteropts *docker.AwsContext, projectOpts *compose.ProjectOptions) *cobra.Command { opts := upOptions{} cmd := &cobra.Command{ Use: "up", @@ -86,7 +81,7 @@ type downOptions struct { DeleteCluster bool } -func DownCommand(clusteropts *ClusterOptions, projectOpts *compose.ProjectOptions) *cobra.Command { +func DownCommand(clusteropts *docker.AwsContext, projectOpts *compose.ProjectOptions) *cobra.Command { opts := downOptions{} cmd := &cobra.Command{ Use: "down", diff --git a/ecs/cmd/commands/secret.go b/ecs/cmd/commands/secret.go index 0c228c54b..b46eeeb3d 100644 --- a/ecs/cmd/commands/secret.go +++ b/ecs/cmd/commands/secret.go @@ -22,7 +22,7 @@ type deleteSecretOptions struct { recover bool } -func SecretCommand(clusteropts *ClusterOptions) *cobra.Command { +func SecretCommand(clusteropts *docker.AwsContext) *cobra.Command { cmd := &cobra.Command{ Use: "secret", Short: "Manages secrets", @@ -37,7 +37,7 @@ func SecretCommand(clusteropts *ClusterOptions) *cobra.Command { return cmd } -func CreateSecret(clusteropts *ClusterOptions) *cobra.Command { +func CreateSecret(clusteropts *docker.AwsContext) *cobra.Command { //opts := createSecretOptions{} cmd := &cobra.Command{ Use: "create NAME SECRET", @@ -60,7 +60,7 @@ func CreateSecret(clusteropts *ClusterOptions) *cobra.Command { return cmd } -func InspectSecret(clusteropts *ClusterOptions) *cobra.Command { +func InspectSecret(clusteropts *docker.AwsContext) *cobra.Command { cmd := &cobra.Command{ Use: "inspect ID", Short: "Displays secret details", @@ -88,7 +88,7 @@ func InspectSecret(clusteropts *ClusterOptions) *cobra.Command { return cmd } -func ListSecrets(clusteropts *ClusterOptions) *cobra.Command { +func ListSecrets(clusteropts *docker.AwsContext) *cobra.Command { cmd := &cobra.Command{ Use: "list", Aliases: []string{"ls"}, @@ -110,7 +110,7 @@ func ListSecrets(clusteropts *ClusterOptions) *cobra.Command { return cmd } -func DeleteSecret(clusteropts *ClusterOptions) *cobra.Command { +func DeleteSecret(clusteropts *docker.AwsContext) *cobra.Command { opts := deleteSecretOptions{} cmd := &cobra.Command{ Use: "delete NAME", diff --git a/ecs/cmd/commands/setup.go b/ecs/cmd/commands/setup.go new file mode 100644 index 000000000..a927605d1 --- /dev/null +++ b/ecs/cmd/commands/setup.go @@ -0,0 +1,33 @@ +package commands + +import ( + "github.com/docker/cli/cli-plugins/plugin" + contextStore "github.com/docker/ecs-plugin/pkg/docker" + "github.com/spf13/cobra" +) + +func SetupCommand() *cobra.Command { + var opts contextStore.AwsContext + var name string + cmd := &cobra.Command{ + Use: "setup", + Short: "", + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + //Override the root command PersistentPreRun + //We just need to initialize the top parent command + return plugin.PersistentPreRunE(cmd, args) + }, + RunE: func(cmd *cobra.Command, args []string) error { + return contextStore.NewContext(name, &opts) + }, + } + cmd.Flags().StringVarP(&name, "name", "n", "aws", "Context Name") + cmd.Flags().StringVarP(&opts.Profile, "profile", "p", "", "AWS Profile") + cmd.Flags().StringVarP(&opts.Cluster, "cluster", "c", "", "ECS cluster") + cmd.Flags().StringVarP(&opts.Region, "region", "r", "", "AWS region") + + cmd.MarkFlagRequired("profile") + cmd.MarkFlagRequired("cluster") + cmd.MarkFlagRequired("region") + return cmd +} diff --git a/ecs/cmd/main/main.go b/ecs/cmd/main/main.go index 72d072a09..be8d21829 100644 --- a/ecs/cmd/main/main.go +++ b/ecs/cmd/main/main.go @@ -7,6 +7,7 @@ import ( "github.com/docker/cli/cli-plugins/plugin" "github.com/docker/cli/cli/command" commands "github.com/docker/ecs-plugin/cmd/commands" + "github.com/docker/ecs-plugin/pkg/docker" "github.com/spf13/cobra" ) @@ -26,23 +27,29 @@ func main() { // NewRootCmd returns the base root command. func NewRootCmd(name string, dockerCli command.Cli) *cobra.Command { - var opts commands.ClusterOptions + var opts *docker.AwsContext cmd := &cobra.Command{ Short: "Docker ECS", Long: `run multi-container applications on Amazon ECS.`, Use: name, Annotations: map[string]string{"experimentalCLI": "true"}, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + err := plugin.PersistentPreRunE(cmd, args) + if err != nil { + return err + } + contextName := dockerCli.CurrentContext() + opts, err = docker.CheckAwsContextExists(contextName) + return err + }, } cmd.AddCommand( VersionCommand(), - commands.ComposeCommand(&opts), - commands.SecretCommand(&opts), + commands.ComposeCommand(opts), + commands.SecretCommand(opts), + commands.SetupCommand(), ) - 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 } diff --git a/ecs/go.mod b/ecs/go.mod index 0ddf6c857..564890334 100644 --- a/ecs/go.mod +++ b/ecs/go.mod @@ -35,6 +35,7 @@ require ( github.com/lib/pq v1.3.0 // indirect github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/miekg/pkcs11 v1.0.3 // indirect + github.com/mitchellh/mapstructure v1.2.2 github.com/morikuni/aec v1.0.0 // indirect github.com/onsi/ginkgo v1.11.0 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect diff --git a/ecs/pkg/docker/contextStore.go b/ecs/pkg/docker/contextStore.go new file mode 100644 index 000000000..2d40b9dd0 --- /dev/null +++ b/ecs/pkg/docker/contextStore.go @@ -0,0 +1,63 @@ +package docker + +import ( + "fmt" + + cliconfig "github.com/docker/cli/cli/config" + "github.com/docker/cli/cli/context/store" + "github.com/mitchellh/mapstructure" +) + +const contextType = "aws" + +type TypeContext struct { + Type string +} + +func getter() interface{} { + return &TypeContext{} +} + +type AwsContext struct { + Profile string + Cluster string + Region string +} + +func NewContext(name string, awsContext *AwsContext) error { + contextStore := initContextStore() + endpoints := map[string]interface{}{ + "aws": awsContext, + "docker": awsContext, + } + + metadata := store.Metadata{ + Name: name, + Endpoints: endpoints, + Metadata: TypeContext{Type: contextType}, + } + return contextStore.CreateOrUpdate(metadata) +} + +func initContextStore() store.Store { + config := store.NewConfig(getter) + return store.New(cliconfig.ContextStoreDir(), config) +} + +func CheckAwsContextExists(contextName string) (*AwsContext, error) { + contextStore := initContextStore() + metadata, err := contextStore.GetMetadata(contextName) + if err != nil { + return nil, err + } + endpoint := metadata.Endpoints["aws"] + awsContext := AwsContext{} + err = mapstructure.Decode(endpoint, &awsContext) + if err != nil { + return nil, err + } + if awsContext == (AwsContext{}) { + return nil, fmt.Errorf("can't use \"%s\" with ECS command because it is not an AWS context", contextName) + } + return &awsContext, nil +}