Bladeren bron

Merge pull request #1392 from docker/run_opts

implement -v, -p, --service-ports and --use-aliases on compose run
Guillaume Tardif 4 jaren geleden
bovenliggende
commit
80822bde44
5 gewijzigde bestanden met toevoegingen van 139 en 60 verwijderingen
  1. 15 14
      api/compose/api.go
  2. 89 37
      cli/cmd/compose/run.go
  3. 17 8
      local/compose/convergence.go
  4. 1 1
      local/compose/run.go
  5. 17 0
      local/e2e/compose/compose_run_test.go

+ 15 - 14
api/compose/api.go

@@ -156,20 +156,21 @@ type RemoveOptions struct {
 
 // RunOptions options to execute compose run
 type RunOptions struct {
-	Name        string
-	Service     string
-	Command     []string
-	Entrypoint  []string
-	Detach      bool
-	AutoRemove  bool
-	Writer      io.Writer
-	Reader      io.Reader
-	Tty         bool
-	WorkingDir  string
-	User        string
-	Environment []string
-	Labels      types.Labels
-	Privileged  bool
+	Name              string
+	Service           string
+	Command           []string
+	Entrypoint        []string
+	Detach            bool
+	AutoRemove        bool
+	Writer            io.Writer
+	Reader            io.Reader
+	Tty               bool
+	WorkingDir        string
+	User              string
+	Environment       []string
+	Labels            types.Labels
+	Privileged        bool
+	UseNetworkAliases bool
 	// used by exec
 	Index int
 }

+ 89 - 37
cli/cmd/compose/run.go

@@ -22,6 +22,7 @@ import (
 	"os"
 	"strings"
 
+	"github.com/compose-spec/compose-go/loader"
 	"github.com/compose-spec/compose-go/types"
 	"github.com/mattn/go-shellwords"
 	"github.com/spf13/cobra"
@@ -34,18 +35,69 @@ import (
 
 type runOptions struct {
 	*composeOptions
-	Service     string
-	Command     []string
-	environment []string
-	Detach      bool
-	Remove      bool
-	noTty       bool
-	user        string
-	workdir     string
-	entrypoint  string
-	labels      []string
-	name        string
-	noDeps      bool
+	Service      string
+	Command      []string
+	environment  []string
+	Detach       bool
+	Remove       bool
+	noTty        bool
+	user         string
+	workdir      string
+	entrypoint   string
+	labels       []string
+	volumes      []string
+	publish      []string
+	useAliases   bool
+	servicePorts bool
+	name         string
+	noDeps       bool
+}
+
+func (opts runOptions) apply(project *types.Project) error {
+	target, err := project.GetService(opts.Service)
+	if err != nil {
+		return err
+	}
+	if !opts.servicePorts {
+		target.Ports = []types.ServicePortConfig{}
+	}
+	if len(opts.publish) > 0 {
+		target.Ports = []types.ServicePortConfig{}
+		for _, p := range opts.publish {
+			config, err := types.ParsePortConfig(p)
+			if err != nil {
+				return err
+			}
+			target.Ports = append(target.Ports, config...)
+		}
+	}
+	if len(opts.volumes) > 0 {
+		target.Volumes = []types.ServiceVolumeConfig{}
+		for _, v := range opts.volumes {
+			volume, err := loader.ParseVolume(v)
+			if err != nil {
+				return err
+			}
+			target.Volumes = append(target.Volumes, volume)
+		}
+	}
+
+	if opts.noDeps {
+		for _, s := range project.Services {
+			if s.Name != opts.Service {
+				project.DisabledServices = append(project.DisabledServices, s)
+			}
+		}
+		project.Services = types.Services{target}
+	}
+
+	for i, s := range project.Services {
+		if s.Name == opts.Service {
+			project.Services[i] = target
+			break
+		}
+	}
+	return nil
 }
 
 func runCommand(p *projectOptions) *cobra.Command {
@@ -63,6 +115,9 @@ func runCommand(p *projectOptions) *cobra.Command {
 				opts.Command = args[1:]
 			}
 			opts.Service = args[0]
+			if len(opts.publish) > 0 && opts.servicePorts {
+				return fmt.Errorf("--service-ports and --publish are incompatible")
+			}
 			return runRun(cmd.Context(), opts)
 		},
 	}
@@ -77,6 +132,10 @@ func runCommand(p *projectOptions) *cobra.Command {
 	flags.StringVarP(&opts.workdir, "workdir", "w", "", "Working directory inside the container")
 	flags.StringVar(&opts.entrypoint, "entrypoint", "", "Override the entrypoint of the image")
 	flags.BoolVar(&opts.noDeps, "no-deps", false, "Don't start linked services.")
+	flags.StringArrayVarP(&opts.volumes, "volumes", "v", []string{}, "Bind mount a volume.")
+	flags.StringArrayVarP(&opts.publish, "publish", "p", []string{}, "Publish a container's port(s) to the host.")
+	flags.BoolVar(&opts.useAliases, "use-aliases", false, "Use the service's network useAliases in the network(s) the container connects to.")
+	flags.BoolVar(&opts.servicePorts, "service-ports", false, "Run command with the service's ports enabled and mapped to the host.")
 
 	flags.SetInterspersed(false)
 	return cmd
@@ -88,17 +147,9 @@ func runRun(ctx context.Context, opts runOptions) error {
 		return err
 	}
 
-	if opts.noDeps {
-		enabled, err := project.GetService(opts.Service)
-		if err != nil {
-			return err
-		}
-		for _, s := range project.Services {
-			if s.Name != opts.Service {
-				project.DisabledServices = append(project.DisabledServices, s)
-			}
-		}
-		project.Services = types.Services{enabled}
+	err = opts.apply(project)
+	if err != nil {
+		return err
 	}
 
 	_, err = progress.Run(ctx, func(ctx context.Context) (string, error) {
@@ -127,20 +178,21 @@ func runRun(ctx context.Context, opts runOptions) error {
 
 	// start container and attach to container streams
 	runOpts := compose.RunOptions{
-		Name:        opts.name,
-		Service:     opts.Service,
-		Command:     opts.Command,
-		Detach:      opts.Detach,
-		AutoRemove:  opts.Remove,
-		Writer:      os.Stdout,
-		Reader:      os.Stdin,
-		Tty:         !opts.noTty,
-		WorkingDir:  opts.workdir,
-		User:        opts.user,
-		Environment: opts.environment,
-		Entrypoint:  entrypoint,
-		Labels:      labels,
-		Index:       0,
+		Name:              opts.name,
+		Service:           opts.Service,
+		Command:           opts.Command,
+		Detach:            opts.Detach,
+		AutoRemove:        opts.Remove,
+		Writer:            os.Stdout,
+		Reader:            os.Stdin,
+		Tty:               !opts.noTty,
+		WorkingDir:        opts.workdir,
+		User:              opts.user,
+		Environment:       opts.environment,
+		Entrypoint:        entrypoint,
+		Labels:            labels,
+		UseNetworkAliases: opts.useAliases,
+		Index:             0,
 	}
 	exitCode, err := c.ComposeService().RunOneOffContainer(ctx, project, runOpts)
 	if exitCode != 0 {

+ 17 - 8
local/compose/convergence.go

@@ -64,7 +64,7 @@ func (s *composeService) ensureScale(ctx context.Context, project *types.Project
 			number := next + i
 			name := getContainerName(project.Name, service, number)
 			eg.Go(func() error {
-				return s.createContainer(ctx, project, service, name, number, false)
+				return s.createContainer(ctx, project, service, name, number, false, true)
 			})
 		}
 	}
@@ -197,11 +197,11 @@ func getScale(config types.ServiceConfig) (int, error) {
 	return scale, err
 }
 
-func (s *composeService) createContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, name string, number int, autoRemove bool) error {
+func (s *composeService) createContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, name string, number int, autoRemove bool, useNetworkAliases bool) error {
 	w := progress.ContextWriter(ctx)
 	eventName := "Container " + name
 	w.Event(progress.CreatingEvent(eventName))
-	err := s.createMobyContainer(ctx, project, service, name, number, nil, autoRemove)
+	err := s.createMobyContainer(ctx, project, service, name, number, nil, autoRemove, useNetworkAliases)
 	if err != nil {
 		return err
 	}
@@ -231,7 +231,7 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
 	if inherit {
 		inherited = &container
 	}
-	err = s.createMobyContainer(ctx, project, service, name, number, inherited, false)
+	err = s.createMobyContainer(ctx, project, service, name, number, inherited, false, true)
 	if err != nil {
 		return err
 	}
@@ -268,8 +268,10 @@ func (s *composeService) restartContainer(ctx context.Context, container moby.Co
 	return nil
 }
 
-func (s *composeService) createMobyContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, name string, number int, inherit *moby.Container,
-	autoRemove bool) error {
+func (s *composeService) createMobyContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, name string, number int,
+	inherit *moby.Container,
+	autoRemove bool,
+	useNetworkAliases bool) error {
 	cState, err := GetContextContainerState(ctx)
 	if err != nil {
 		return err
@@ -287,9 +289,16 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
 		Labels: containerConfig.Labels,
 	}
 	cState.Add(createdContainer)
-	for netName := range service.Networks {
+	for netName, cfg := range service.Networks {
 		netwrk := project.Networks[netName]
-		err = s.connectContainerToNetwork(ctx, created.ID, netwrk.Name, service.Name, getContainerName(project.Name, service, number))
+		aliases := []string{getContainerName(project.Name, service, number)}
+		if useNetworkAliases {
+			aliases = append(aliases, service.Name)
+			if cfg != nil {
+				aliases = append(aliases, cfg.Aliases...)
+			}
+		}
+		err = s.connectContainerToNetwork(ctx, created.ID, netwrk.Name, aliases...)
 		if err != nil {
 			return err
 		}

+ 1 - 1
local/compose/run.go

@@ -62,7 +62,7 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.
 	if err := s.waitDependencies(ctx, project, service); err != nil {
 		return 0, err
 	}
-	if err := s.createContainer(ctx, project, service, service.ContainerName, 1, opts.AutoRemove); err != nil {
+	if err := s.createContainer(ctx, project, service, service.ContainerName, 1, opts.AutoRemove, opts.UseNetworkAliases); err != nil {
 		return 0, err
 	}
 	containerID := service.ContainerName

+ 17 - 0
local/e2e/compose/compose_run_test.go

@@ -17,6 +17,7 @@
 package e2e
 
 import (
+	"os"
 	"strings"
 	"testing"
 
@@ -83,4 +84,20 @@ func TestLocalComposeRun(t *testing.T) {
 		res := c.RunDockerCmd("ps", "--all")
 		assert.Assert(t, !strings.Contains(res.Stdout(), "run-test"), res.Stdout())
 	})
+
+	t.Run("compose run --volumes", func(t *testing.T) {
+		wd, err := os.Getwd()
+		assert.NilError(t, err)
+		res := c.RunDockerCmd("compose", "-f", "./fixtures/run-test/compose.yml", "run", "--volumes", wd+":/foo", "back", "/bin/sh", "-c", "ls /foo")
+		res.Assert(t, icmd.Expected{Out: "compose_run_test.go"})
+
+		res = c.RunDockerCmd("ps", "--all")
+		assert.Assert(t, strings.Contains(res.Stdout(), "run-test_back"), res.Stdout())
+	})
+
+	t.Run("compose run --publish", func(t *testing.T) {
+		c.RunDockerCmd("compose", "-f", "./fixtures/run-test/compose.yml", "run", "--rm", "--publish", "8080:80", "-d", "back", "/bin/sh", "-c", "sleep 10")
+		res := c.RunDockerCmd("ps")
+		assert.Assert(t, strings.Contains(res.Stdout(), "8080->80/tcp"), res.Stdout())
+	})
 }