mirror of
https://github.com/docker/compose.git
synced 2025-07-22 05:04:27 +02:00
implement -v, -p, --service-ports and --use-aliases on compose run
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
91b39d0772
commit
d08255c4ff
@ -151,20 +151,21 @@ type RemoveOptions struct {
|
|||||||
|
|
||||||
// RunOptions options to execute compose run
|
// RunOptions options to execute compose run
|
||||||
type RunOptions struct {
|
type RunOptions struct {
|
||||||
Name string
|
Name string
|
||||||
Service string
|
Service string
|
||||||
Command []string
|
Command []string
|
||||||
Entrypoint []string
|
Entrypoint []string
|
||||||
Detach bool
|
Detach bool
|
||||||
AutoRemove bool
|
AutoRemove bool
|
||||||
Writer io.Writer
|
Writer io.Writer
|
||||||
Reader io.Reader
|
Reader io.Reader
|
||||||
Tty bool
|
Tty bool
|
||||||
WorkingDir string
|
WorkingDir string
|
||||||
User string
|
User string
|
||||||
Environment []string
|
Environment []string
|
||||||
Labels types.Labels
|
Labels types.Labels
|
||||||
Privileged bool
|
Privileged bool
|
||||||
|
UseNetworkAliases bool
|
||||||
// used by exec
|
// used by exec
|
||||||
Index int
|
Index int
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/compose-spec/compose-go/loader"
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/mattn/go-shellwords"
|
"github.com/mattn/go-shellwords"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -34,18 +35,69 @@ import (
|
|||||||
|
|
||||||
type runOptions struct {
|
type runOptions struct {
|
||||||
*composeOptions
|
*composeOptions
|
||||||
Service string
|
Service string
|
||||||
Command []string
|
Command []string
|
||||||
environment []string
|
environment []string
|
||||||
Detach bool
|
Detach bool
|
||||||
Remove bool
|
Remove bool
|
||||||
noTty bool
|
noTty bool
|
||||||
user string
|
user string
|
||||||
workdir string
|
workdir string
|
||||||
entrypoint string
|
entrypoint string
|
||||||
labels []string
|
labels []string
|
||||||
name string
|
volumes []string
|
||||||
noDeps bool
|
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 {
|
func runCommand(p *projectOptions) *cobra.Command {
|
||||||
@ -63,6 +115,9 @@ func runCommand(p *projectOptions) *cobra.Command {
|
|||||||
opts.Command = args[1:]
|
opts.Command = args[1:]
|
||||||
}
|
}
|
||||||
opts.Service = args[0]
|
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)
|
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.StringVarP(&opts.workdir, "workdir", "w", "", "Working directory inside the container")
|
||||||
flags.StringVar(&opts.entrypoint, "entrypoint", "", "Override the entrypoint of the image")
|
flags.StringVar(&opts.entrypoint, "entrypoint", "", "Override the entrypoint of the image")
|
||||||
flags.BoolVar(&opts.noDeps, "no-deps", false, "Don't start linked services.")
|
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)
|
flags.SetInterspersed(false)
|
||||||
return cmd
|
return cmd
|
||||||
@ -88,17 +147,9 @@ func runRun(ctx context.Context, opts runOptions) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.noDeps {
|
err = opts.apply(project)
|
||||||
enabled, err := project.GetService(opts.Service)
|
if err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, s := range project.Services {
|
|
||||||
if s.Name != opts.Service {
|
|
||||||
project.DisabledServices = append(project.DisabledServices, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
project.Services = types.Services{enabled}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = progress.Run(ctx, func(ctx context.Context) (string, error) {
|
_, 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
|
// start container and attach to container streams
|
||||||
runOpts := compose.RunOptions{
|
runOpts := compose.RunOptions{
|
||||||
Name: opts.name,
|
Name: opts.name,
|
||||||
Service: opts.Service,
|
Service: opts.Service,
|
||||||
Command: opts.Command,
|
Command: opts.Command,
|
||||||
Detach: opts.Detach,
|
Detach: opts.Detach,
|
||||||
AutoRemove: opts.Remove,
|
AutoRemove: opts.Remove,
|
||||||
Writer: os.Stdout,
|
Writer: os.Stdout,
|
||||||
Reader: os.Stdin,
|
Reader: os.Stdin,
|
||||||
Tty: !opts.noTty,
|
Tty: !opts.noTty,
|
||||||
WorkingDir: opts.workdir,
|
WorkingDir: opts.workdir,
|
||||||
User: opts.user,
|
User: opts.user,
|
||||||
Environment: opts.environment,
|
Environment: opts.environment,
|
||||||
Entrypoint: entrypoint,
|
Entrypoint: entrypoint,
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
Index: 0,
|
UseNetworkAliases: opts.useAliases,
|
||||||
|
Index: 0,
|
||||||
}
|
}
|
||||||
exitCode, err := c.ComposeService().RunOneOffContainer(ctx, project, runOpts)
|
exitCode, err := c.ComposeService().RunOneOffContainer(ctx, project, runOpts)
|
||||||
if exitCode != 0 {
|
if exitCode != 0 {
|
||||||
|
@ -64,7 +64,7 @@ func (s *composeService) ensureScale(ctx context.Context, project *types.Project
|
|||||||
number := next + i
|
number := next + i
|
||||||
name := getContainerName(project.Name, service, number)
|
name := getContainerName(project.Name, service, number)
|
||||||
eg.Go(func() error {
|
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
|
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)
|
w := progress.ContextWriter(ctx)
|
||||||
eventName := "Container " + name
|
eventName := "Container " + name
|
||||||
w.Event(progress.CreatingEvent(eventName))
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -231,7 +231,7 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
|
|||||||
if inherit {
|
if inherit {
|
||||||
inherited = &container
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -268,8 +268,10 @@ func (s *composeService) restartContainer(ctx context.Context, container moby.Co
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) createMobyContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, name string, number int, inherit *moby.Container,
|
func (s *composeService) createMobyContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, name string, number int,
|
||||||
autoRemove bool) error {
|
inherit *moby.Container,
|
||||||
|
autoRemove bool,
|
||||||
|
useNetworkAliases bool) error {
|
||||||
cState, err := GetContextContainerState(ctx)
|
cState, err := GetContextContainerState(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -287,9 +289,16 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
|
|||||||
Labels: containerConfig.Labels,
|
Labels: containerConfig.Labels,
|
||||||
}
|
}
|
||||||
cState.Add(createdContainer)
|
cState.Add(createdContainer)
|
||||||
for netName := range service.Networks {
|
for netName, cfg := range service.Networks {
|
||||||
netwrk := project.Networks[netName]
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.
|
|||||||
if err := s.waitDependencies(ctx, project, service); err != nil {
|
if err := s.waitDependencies(ctx, project, service); err != nil {
|
||||||
return 0, err
|
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
|
return 0, err
|
||||||
}
|
}
|
||||||
containerID := service.ContainerName
|
containerID := service.ContainerName
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package e2e
|
package e2e
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -83,4 +84,20 @@ func TestLocalComposeRun(t *testing.T) {
|
|||||||
res := c.RunDockerCmd("ps", "--all")
|
res := c.RunDockerCmd("ps", "--all")
|
||||||
assert.Assert(t, !strings.Contains(res.Stdout(), "run-test"), res.Stdout())
|
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())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user