|
|
@@ -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 {
|