зеркало из https://github.com/docker/compose-cli.git
move up logic from CLI into local backend
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Родитель
b86ff6b18f
Коммит
c135bd1d7c
|
@ -87,6 +87,12 @@ func (cs *aciComposeService) Copy(ctx context.Context, project *types.Project, o
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *aciComposeService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
|
func (cs *aciComposeService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
|
||||||
|
return progress.Run(ctx, func(ctx context.Context) error {
|
||||||
|
return cs.up(ctx, project)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *aciComposeService) up(ctx context.Context, project *types.Project) error {
|
||||||
logrus.Debugf("Up on project with name %q", project.Name)
|
logrus.Debugf("Up on project with name %q", project.Name)
|
||||||
|
|
||||||
if err := autocreateFileshares(ctx, project); err != nil {
|
if err := autocreateFileshares(ctx, project); err != nil {
|
||||||
|
|
|
@ -112,10 +112,14 @@ type CreateOptions struct {
|
||||||
|
|
||||||
// StartOptions group options of the Start API
|
// StartOptions group options of the Start API
|
||||||
type StartOptions struct {
|
type StartOptions struct {
|
||||||
// Attach will attach to service containers and send container logs and events
|
// Attach to container and forward logs if not nil
|
||||||
Attach ContainerEventListener
|
Attach LogConsumer
|
||||||
// Services passed in the command line to be started
|
// AttachTo set the services to attach to
|
||||||
Services []string
|
AttachTo []string
|
||||||
|
// CascadeStop stops the application when a container stops
|
||||||
|
CascadeStop bool
|
||||||
|
// ExitCodeFrom return exit code from specified service
|
||||||
|
ExitCodeFrom string
|
||||||
}
|
}
|
||||||
|
|
||||||
// RestartOptions group options of the Restart API
|
// RestartOptions group options of the Restart API
|
||||||
|
@ -136,10 +140,8 @@ type StopOptions struct {
|
||||||
|
|
||||||
// UpOptions group options of the Up API
|
// UpOptions group options of the Up API
|
||||||
type UpOptions struct {
|
type UpOptions struct {
|
||||||
// Detach will create services and return immediately
|
Create CreateOptions
|
||||||
Detach bool
|
Start StartOptions
|
||||||
// QuietPull makes the pulling process quiet
|
|
||||||
QuietPull bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DownOptions group options of the Down API
|
// DownOptions group options of the Down API
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
@ -90,14 +91,43 @@ type projectOptions struct {
|
||||||
// ProjectFunc does stuff within a types.Project
|
// ProjectFunc does stuff within a types.Project
|
||||||
type ProjectFunc func(ctx context.Context, project *types.Project) error
|
type ProjectFunc func(ctx context.Context, project *types.Project) error
|
||||||
|
|
||||||
|
// ProjectServicesFunc does stuff within a types.Project and a selection of services
|
||||||
|
type ProjectServicesFunc func(ctx context.Context, project *types.Project, services []string) error
|
||||||
|
|
||||||
// WithServices creates a cobra run command from a ProjectFunc based on configured project options and selected services
|
// WithServices creates a cobra run command from a ProjectFunc based on configured project options and selected services
|
||||||
func (o *projectOptions) WithServices(services []string, fn ProjectFunc) func(cmd *cobra.Command, args []string) error {
|
func (o *projectOptions) WithProject(fn ProjectFunc) func(cmd *cobra.Command, args []string) error {
|
||||||
return Adapt(func(ctx context.Context, strings []string) error {
|
return o.WithServices(func(ctx context.Context, project *types.Project, services []string) error {
|
||||||
project, err := o.toProject(services)
|
return fn(ctx, project)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithServices creates a cobra run command from a ProjectFunc based on configured project options and selected services
|
||||||
|
func (o *projectOptions) WithServices(fn ProjectServicesFunc) func(cmd *cobra.Command, args []string) error {
|
||||||
|
return Adapt(func(ctx context.Context, args []string) error {
|
||||||
|
project, err := o.toProject(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return fn(ctx, project)
|
|
||||||
|
if o.EnvFile != "" {
|
||||||
|
var services types.Services
|
||||||
|
for _, s := range project.Services {
|
||||||
|
ef := o.EnvFile
|
||||||
|
if ef != "" {
|
||||||
|
if !filepath.IsAbs(ef) {
|
||||||
|
ef = filepath.Join(project.WorkingDir, o.EnvFile)
|
||||||
|
}
|
||||||
|
if s.Labels == nil {
|
||||||
|
s.Labels = make(map[string]string)
|
||||||
|
}
|
||||||
|
s.Labels[compose.EnvironmentFileLabel] = ef
|
||||||
|
services = append(services, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
project.Services = services
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn(ctx, project, args)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +247,7 @@ func RootCommand(contextType string, backend compose.Service) *cobra.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
command.AddCommand(
|
command.AddCommand(
|
||||||
upCommand(&opts, contextType, backend),
|
upCommand(&opts, backend),
|
||||||
downCommand(&opts, backend),
|
downCommand(&opts, backend),
|
||||||
startCommand(&opts, backend),
|
startCommand(&opts, backend),
|
||||||
restartCommand(&opts, backend),
|
restartCommand(&opts, backend),
|
||||||
|
|
|
@ -19,22 +19,29 @@ package compose
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/docker/compose-cli/api/compose"
|
"github.com/docker/compose-cli/api/compose"
|
||||||
)
|
)
|
||||||
|
|
||||||
type createOptions struct {
|
type createOptions struct {
|
||||||
*composeOptions
|
Build bool
|
||||||
|
noBuild bool
|
||||||
|
removeOrphans bool
|
||||||
forceRecreate bool
|
forceRecreate bool
|
||||||
noRecreate bool
|
noRecreate bool
|
||||||
|
recreateDeps bool
|
||||||
|
noInherit bool
|
||||||
|
timeChanged bool
|
||||||
|
timeout int
|
||||||
|
quietPull bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func createCommand(p *projectOptions, backend compose.Service) *cobra.Command {
|
func createCommand(p *projectOptions, backend compose.Service) *cobra.Command {
|
||||||
opts := createOptions{
|
opts := createOptions{}
|
||||||
composeOptions: &composeOptions{},
|
|
||||||
}
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "create [SERVICE...]",
|
Use: "create [SERVICE...]",
|
||||||
Short: "Creates containers for a service.",
|
Short: "Creates containers for a service.",
|
||||||
|
@ -47,17 +54,15 @@ func createCommand(p *projectOptions, backend compose.Service) *cobra.Command {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}),
|
}),
|
||||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
RunE: p.WithProject(func(ctx context.Context, project *types.Project) error {
|
||||||
return runCreateStart(ctx, backend, upOptions{
|
return backend.Create(ctx, project, compose.CreateOptions{
|
||||||
composeOptions: &composeOptions{
|
RemoveOrphans: opts.removeOrphans,
|
||||||
projectOptions: p,
|
Recreate: opts.recreateStrategy(),
|
||||||
Build: opts.Build,
|
RecreateDependencies: opts.dependenciesRecreateStrategy(),
|
||||||
noBuild: opts.noBuild,
|
Inherit: !opts.noInherit,
|
||||||
},
|
Timeout: opts.GetTimeout(),
|
||||||
noStart: true,
|
QuietPull: false,
|
||||||
forceRecreate: opts.forceRecreate,
|
})
|
||||||
noRecreate: opts.noRecreate,
|
|
||||||
}, args)
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
|
@ -67,3 +72,46 @@ func createCommand(p *projectOptions, backend compose.Service) *cobra.Command {
|
||||||
flags.BoolVar(&opts.noRecreate, "no-recreate", false, "If containers already exist, don't recreate them. Incompatible with --force-recreate.")
|
flags.BoolVar(&opts.noRecreate, "no-recreate", false, "If containers already exist, don't recreate them. Incompatible with --force-recreate.")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (opts createOptions) recreateStrategy() string {
|
||||||
|
if opts.noRecreate {
|
||||||
|
return compose.RecreateNever
|
||||||
|
}
|
||||||
|
if opts.forceRecreate {
|
||||||
|
return compose.RecreateForce
|
||||||
|
}
|
||||||
|
return compose.RecreateDiverged
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts createOptions) dependenciesRecreateStrategy() string {
|
||||||
|
if opts.noRecreate {
|
||||||
|
return compose.RecreateNever
|
||||||
|
}
|
||||||
|
if opts.recreateDeps {
|
||||||
|
return compose.RecreateForce
|
||||||
|
}
|
||||||
|
return compose.RecreateDiverged
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts createOptions) GetTimeout() *time.Duration {
|
||||||
|
if opts.timeChanged {
|
||||||
|
t := time.Duration(opts.timeout) * time.Second
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts createOptions) Apply(project *types.Project) {
|
||||||
|
if opts.Build {
|
||||||
|
for i, service := range project.Services {
|
||||||
|
service.PullPolicy = types.PullPolicyBuild
|
||||||
|
project.Services[i] = service
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opts.noBuild {
|
||||||
|
for i, service := range project.Services {
|
||||||
|
service.Build = nil
|
||||||
|
project.Services[i] = service
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ package compose
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -31,7 +30,7 @@ func killCommand(p *projectOptions, backend compose.Service) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "kill [options] [SERVICE...]",
|
Use: "kill [options] [SERVICE...]",
|
||||||
Short: "Force stop service containers.",
|
Short: "Force stop service containers.",
|
||||||
RunE: p.WithServices(os.Args, func(ctx context.Context, project *types.Project) error {
|
RunE: p.WithProject(func(ctx context.Context, project *types.Project) error {
|
||||||
return backend.Kill(ctx, project, opts)
|
return backend.Kill(ctx, project, opts)
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,11 @@ func runCommand(p *projectOptions, backend compose.Service) *cobra.Command {
|
||||||
return nil
|
return nil
|
||||||
}),
|
}),
|
||||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||||
return runRun(ctx, backend, opts)
|
project, err := p.toProject([]string{opts.Service})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return runRun(ctx, backend, project, opts)
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
|
@ -143,13 +147,8 @@ func runCommand(p *projectOptions, backend compose.Service) *cobra.Command {
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRun(ctx context.Context, backend compose.Service, opts runOptions) error {
|
func runRun(ctx context.Context, backend compose.Service, project *types.Project, opts runOptions) error {
|
||||||
project, err := setup(*opts.composeOptions, []string{opts.Service})
|
err := opts.apply(project)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = opts.apply(project)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,40 +20,25 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/docker/cli/cli"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"golang.org/x/sync/errgroup"
|
|
||||||
|
|
||||||
"github.com/docker/compose-cli/api/compose"
|
"github.com/docker/compose-cli/api/compose"
|
||||||
"github.com/docker/compose-cli/api/context/store"
|
|
||||||
"github.com/docker/compose-cli/api/progress"
|
|
||||||
"github.com/docker/compose-cli/cli/formatter"
|
"github.com/docker/compose-cli/cli/formatter"
|
||||||
"github.com/docker/compose-cli/utils"
|
"github.com/docker/compose-cli/utils"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
// composeOptions hold options common to `up` and `run` to run compose project
|
// composeOptions hold options common to `up` and `run` to run compose project
|
||||||
type composeOptions struct {
|
type composeOptions struct {
|
||||||
*projectOptions
|
*projectOptions
|
||||||
Build bool
|
|
||||||
noBuild bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type upOptions struct {
|
type upOptions struct {
|
||||||
*composeOptions
|
*composeOptions
|
||||||
Detach bool
|
Detach bool
|
||||||
Environment []string
|
Environment []string
|
||||||
removeOrphans bool
|
|
||||||
forceRecreate bool
|
|
||||||
noRecreate bool
|
|
||||||
recreateDeps bool
|
|
||||||
noStart bool
|
noStart bool
|
||||||
noDeps bool
|
noDeps bool
|
||||||
cascadeStop bool
|
cascadeStop bool
|
||||||
|
@ -61,39 +46,7 @@ type upOptions struct {
|
||||||
scale []string
|
scale []string
|
||||||
noColor bool
|
noColor bool
|
||||||
noPrefix bool
|
noPrefix bool
|
||||||
timeChanged bool
|
|
||||||
timeout int
|
|
||||||
noInherit bool
|
|
||||||
attachDependencies bool
|
attachDependencies bool
|
||||||
quietPull bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (opts upOptions) recreateStrategy() string {
|
|
||||||
if opts.noRecreate {
|
|
||||||
return compose.RecreateNever
|
|
||||||
}
|
|
||||||
if opts.forceRecreate {
|
|
||||||
return compose.RecreateForce
|
|
||||||
}
|
|
||||||
return compose.RecreateDiverged
|
|
||||||
}
|
|
||||||
|
|
||||||
func (opts upOptions) dependenciesRecreateStrategy() string {
|
|
||||||
if opts.noRecreate {
|
|
||||||
return compose.RecreateNever
|
|
||||||
}
|
|
||||||
if opts.recreateDeps {
|
|
||||||
return compose.RecreateForce
|
|
||||||
}
|
|
||||||
return compose.RecreateDiverged
|
|
||||||
}
|
|
||||||
|
|
||||||
func (opts upOptions) GetTimeout() *time.Duration {
|
|
||||||
if opts.timeChanged {
|
|
||||||
t := time.Duration(opts.timeout) * time.Second
|
|
||||||
return &t
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opts upOptions) apply(project *types.Project, services []string) error {
|
func (opts upOptions) apply(project *types.Project, services []string) error {
|
||||||
|
@ -136,192 +89,105 @@ func (opts upOptions) apply(project *types.Project, services []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func upCommand(p *projectOptions, contextType string, backend compose.Service) *cobra.Command {
|
func upCommand(p *projectOptions, backend compose.Service) *cobra.Command {
|
||||||
opts := upOptions{
|
up := upOptions{}
|
||||||
composeOptions: &composeOptions{
|
create := createOptions{}
|
||||||
projectOptions: p,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
upCmd := &cobra.Command{
|
upCmd := &cobra.Command{
|
||||||
Use: "up [SERVICE...]",
|
Use: "up [SERVICE...]",
|
||||||
Short: "Create and start containers",
|
Short: "Create and start containers",
|
||||||
PreRun: func(cmd *cobra.Command, args []string) {
|
PreRun: func(cmd *cobra.Command, args []string) {
|
||||||
opts.timeChanged = cmd.Flags().Changed("timeout")
|
create.timeChanged = cmd.Flags().Changed("timeout")
|
||||||
},
|
},
|
||||||
PreRunE: Adapt(func(ctx context.Context, args []string) error {
|
PreRunE: Adapt(func(ctx context.Context, args []string) error {
|
||||||
if opts.exitCodeFrom != "" {
|
if up.exitCodeFrom != "" {
|
||||||
opts.cascadeStop = true
|
up.cascadeStop = true
|
||||||
}
|
}
|
||||||
if opts.Build && opts.noBuild {
|
if create.Build && create.noBuild {
|
||||||
return fmt.Errorf("--build and --no-build are incompatible")
|
return fmt.Errorf("--build and --no-build are incompatible")
|
||||||
}
|
}
|
||||||
if opts.Detach && (opts.attachDependencies || opts.cascadeStop) {
|
if up.Detach && (up.attachDependencies || up.cascadeStop) {
|
||||||
return fmt.Errorf("--detach cannot be combined with --abort-on-container-exit or --attach-dependencies")
|
return fmt.Errorf("--detach cannot be combined with --abort-on-container-exit or --attach-dependencies")
|
||||||
}
|
}
|
||||||
if opts.forceRecreate && opts.noRecreate {
|
if create.forceRecreate && create.noRecreate {
|
||||||
return fmt.Errorf("--force-recreate and --no-recreate are incompatible")
|
return fmt.Errorf("--force-recreate and --no-recreate are incompatible")
|
||||||
}
|
}
|
||||||
if opts.recreateDeps && opts.noRecreate {
|
if create.recreateDeps && create.noRecreate {
|
||||||
return fmt.Errorf("--always-recreate-deps and --no-recreate are incompatible")
|
return fmt.Errorf("--always-recreate-deps and --no-recreate are incompatible")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}),
|
}),
|
||||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
RunE: p.WithServices(func(ctx context.Context, project *types.Project, services []string) error {
|
||||||
switch contextType {
|
return runUp(ctx, backend, create, up, project, services)
|
||||||
case store.LocalContextType, store.DefaultContextType, store.EcsLocalSimulationContextType:
|
|
||||||
return runCreateStart(ctx, backend, opts, args)
|
|
||||||
default:
|
|
||||||
return runUp(ctx, backend, opts, args)
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
flags := upCmd.Flags()
|
flags := upCmd.Flags()
|
||||||
flags.StringArrayVarP(&opts.Environment, "environment", "e", []string{}, "Environment variables")
|
flags.StringArrayVarP(&up.Environment, "environment", "e", []string{}, "Environment variables")
|
||||||
flags.BoolVarP(&opts.Detach, "detach", "d", false, "Detached mode: Run containers in the background")
|
flags.BoolVarP(&up.Detach, "detach", "d", false, "Detached mode: Run containers in the background")
|
||||||
flags.BoolVar(&opts.Build, "build", false, "Build images before starting containers.")
|
flags.BoolVar(&create.Build, "build", false, "Build images before starting containers.")
|
||||||
flags.BoolVar(&opts.noBuild, "no-build", false, "Don't build an image, even if it's missing.")
|
flags.BoolVar(&create.noBuild, "no-build", false, "Don't build an image, even if it's missing.")
|
||||||
flags.BoolVar(&opts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.")
|
flags.BoolVar(&create.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.")
|
||||||
flags.StringArrayVar(&opts.scale, "scale", []string{}, "Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present.")
|
flags.StringArrayVar(&up.scale, "scale", []string{}, "Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present.")
|
||||||
flags.BoolVar(&opts.noColor, "no-color", false, "Produce monochrome output.")
|
flags.BoolVar(&up.noColor, "no-color", false, "Produce monochrome output.")
|
||||||
flags.BoolVar(&opts.noPrefix, "no-log-prefix", false, "Don't print prefix in logs.")
|
flags.BoolVar(&up.noPrefix, "no-log-prefix", false, "Don't print prefix in logs.")
|
||||||
|
flags.BoolVar(&create.forceRecreate, "force-recreate", false, "Recreate containers even if their configuration and image haven't changed.")
|
||||||
switch contextType {
|
flags.BoolVar(&create.noRecreate, "no-recreate", false, "If containers already exist, don't recreate them. Incompatible with --force-recreate.")
|
||||||
case store.LocalContextType, store.DefaultContextType, store.EcsLocalSimulationContextType:
|
flags.BoolVar(&up.noStart, "no-start", false, "Don't start the services after creating them.")
|
||||||
flags.BoolVar(&opts.forceRecreate, "force-recreate", false, "Recreate containers even if their configuration and image haven't changed.")
|
flags.BoolVar(&up.cascadeStop, "abort-on-container-exit", false, "Stops all containers if any container was stopped. Incompatible with -d")
|
||||||
flags.BoolVar(&opts.noRecreate, "no-recreate", false, "If containers already exist, don't recreate them. Incompatible with --force-recreate.")
|
flags.StringVar(&up.exitCodeFrom, "exit-code-from", "", "Return the exit code of the selected service container. Implies --abort-on-container-exit")
|
||||||
flags.BoolVar(&opts.noStart, "no-start", false, "Don't start the services after creating them.")
|
flags.IntVarP(&create.timeout, "timeout", "t", 10, "Use this timeout in seconds for container shutdown when attached or when containers are already running.")
|
||||||
flags.BoolVar(&opts.cascadeStop, "abort-on-container-exit", false, "Stops all containers if any container was stopped. Incompatible with -d")
|
flags.BoolVar(&up.noDeps, "no-deps", false, "Don't start linked services.")
|
||||||
flags.StringVar(&opts.exitCodeFrom, "exit-code-from", "", "Return the exit code of the selected service container. Implies --abort-on-container-exit")
|
flags.BoolVar(&create.recreateDeps, "always-recreate-deps", false, "Recreate dependent containers. Incompatible with --no-recreate.")
|
||||||
flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Use this timeout in seconds for container shutdown when attached or when containers are already running.")
|
flags.BoolVarP(&create.noInherit, "renew-anon-volumes", "V", false, "Recreate anonymous volumes instead of retrieving data from the previous containers.")
|
||||||
flags.BoolVar(&opts.noDeps, "no-deps", false, "Don't start linked services.")
|
flags.BoolVar(&up.attachDependencies, "attach-dependencies", false, "Attach to dependent containers.")
|
||||||
flags.BoolVar(&opts.recreateDeps, "always-recreate-deps", false, "Recreate dependent containers. Incompatible with --no-recreate.")
|
flags.BoolVar(&create.quietPull, "quiet-pull", false, "Pull without printing progress information.")
|
||||||
flags.BoolVarP(&opts.noInherit, "renew-anon-volumes", "V", false, "Recreate anonymous volumes instead of retrieving data from the previous containers.")
|
|
||||||
flags.BoolVar(&opts.attachDependencies, "attach-dependencies", false, "Attach to dependent containers.")
|
|
||||||
flags.BoolVar(&opts.quietPull, "quiet-pull", false, "Pull without printing progress information.")
|
|
||||||
}
|
|
||||||
|
|
||||||
return upCmd
|
return upCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func runUp(ctx context.Context, backend compose.Service, opts upOptions, services []string) error {
|
func runUp(ctx context.Context, backend compose.Service, createOptions createOptions, upOptions upOptions, project *types.Project, services []string) error {
|
||||||
project, err := setup(*opts.composeOptions, services)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = opts.apply(project, services)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return progress.Run(ctx, func(ctx context.Context) error {
|
|
||||||
return backend.Up(ctx, project, compose.UpOptions{
|
|
||||||
Detach: opts.Detach,
|
|
||||||
QuietPull: opts.quietPull,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func runCreateStart(ctx context.Context, backend compose.Service, opts upOptions, services []string) error {
|
|
||||||
project, err := setup(*opts.composeOptions, services)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = opts.apply(project, services)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(project.Services) == 0 {
|
if len(project.Services) == 0 {
|
||||||
return fmt.Errorf("no service selected")
|
return fmt.Errorf("no service selected")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = progress.Run(ctx, func(ctx context.Context) error {
|
createOptions.Apply(project)
|
||||||
err := backend.Create(ctx, project, compose.CreateOptions{
|
|
||||||
Services: services,
|
err := upOptions.apply(project, services)
|
||||||
RemoveOrphans: opts.removeOrphans,
|
|
||||||
Recreate: opts.recreateStrategy(),
|
|
||||||
RecreateDependencies: opts.dependenciesRecreateStrategy(),
|
|
||||||
Inherit: !opts.noInherit,
|
|
||||||
Timeout: opts.GetTimeout(),
|
|
||||||
QuietPull: opts.quietPull,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if opts.Detach {
|
|
||||||
err = backend.Start(ctx, project, compose.StartOptions{
|
|
||||||
Services: services,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.noStart {
|
var consumer compose.LogConsumer
|
||||||
return nil
|
if !upOptions.Detach {
|
||||||
|
consumer = formatter.NewLogConsumer(ctx, os.Stdout, !upOptions.noColor, !upOptions.noPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.attachDependencies {
|
attachTo := services
|
||||||
services = nil
|
if upOptions.attachDependencies {
|
||||||
|
attachTo = project.ServiceNames()
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Detach {
|
create := compose.CreateOptions{
|
||||||
return nil
|
RemoveOrphans: createOptions.removeOrphans,
|
||||||
|
Recreate: createOptions.recreateStrategy(),
|
||||||
|
RecreateDependencies: createOptions.dependenciesRecreateStrategy(),
|
||||||
|
Inherit: !createOptions.noInherit,
|
||||||
|
Timeout: createOptions.GetTimeout(),
|
||||||
|
QuietPull: createOptions.quietPull,
|
||||||
}
|
}
|
||||||
|
|
||||||
consumer := formatter.NewLogConsumer(ctx, os.Stdout, !opts.noColor, !opts.noPrefix)
|
if upOptions.noStart {
|
||||||
printer := compose.NewLogPrinter(consumer)
|
return backend.Create(ctx, project, create)
|
||||||
|
|
||||||
signalChan := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
|
|
||||||
|
|
||||||
stopFunc := func() error {
|
|
||||||
ctx := context.Background()
|
|
||||||
return progress.Run(ctx, func(ctx context.Context) error {
|
|
||||||
go func() {
|
|
||||||
<-signalChan
|
|
||||||
backend.Kill(ctx, project, compose.KillOptions{}) // nolint:errcheck
|
|
||||||
}()
|
|
||||||
|
|
||||||
return backend.Stop(ctx, project, compose.StopOptions{})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
go func() {
|
|
||||||
<-signalChan
|
|
||||||
printer.Cancel()
|
|
||||||
fmt.Println("Gracefully stopping... (press Ctrl+C again to force)")
|
|
||||||
stopFunc() // nolint:errcheck
|
|
||||||
}()
|
|
||||||
|
|
||||||
var exitCode int
|
return backend.Up(ctx, project, compose.UpOptions{
|
||||||
eg, ctx := errgroup.WithContext(ctx)
|
Create: create,
|
||||||
eg.Go(func() error {
|
Start: compose.StartOptions{
|
||||||
code, err := printer.Run(opts.cascadeStop, opts.exitCodeFrom, stopFunc)
|
Attach: consumer,
|
||||||
exitCode = code
|
AttachTo: attachTo,
|
||||||
return err
|
ExitCodeFrom: upOptions.exitCodeFrom,
|
||||||
|
CascadeStop: upOptions.cascadeStop,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
err = backend.Start(ctx, project, compose.StartOptions{
|
|
||||||
Attach: printer.HandleEvent,
|
|
||||||
Services: services,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = eg.Wait()
|
|
||||||
if exitCode != 0 {
|
|
||||||
errMsg := ""
|
|
||||||
if err != nil {
|
|
||||||
errMsg = err.Error()
|
|
||||||
}
|
|
||||||
return cli.StatusError{StatusCode: exitCode, Status: errMsg}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setServiceScale(project *types.Project, name string, replicas int) error {
|
func setServiceScale(project *types.Project, name string, replicas int) error {
|
||||||
|
@ -338,43 +204,3 @@ func setServiceScale(project *types.Project, name string, replicas int) error {
|
||||||
}
|
}
|
||||||
return fmt.Errorf("unknown service %q", name)
|
return fmt.Errorf("unknown service %q", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup(opts composeOptions, services []string) (*types.Project, error) {
|
|
||||||
project, err := opts.toProject(services)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Build {
|
|
||||||
for i, service := range project.Services {
|
|
||||||
service.PullPolicy = types.PullPolicyBuild
|
|
||||||
project.Services[i] = service
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if opts.noBuild {
|
|
||||||
for i, service := range project.Services {
|
|
||||||
service.Build = nil
|
|
||||||
project.Services[i] = service
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.EnvFile != "" {
|
|
||||||
var services types.Services
|
|
||||||
for _, s := range project.Services {
|
|
||||||
ef := opts.EnvFile
|
|
||||||
if ef != "" {
|
|
||||||
if !filepath.IsAbs(ef) {
|
|
||||||
ef = filepath.Join(project.WorkingDir, opts.EnvFile)
|
|
||||||
}
|
|
||||||
if s.Labels == nil {
|
|
||||||
s.Labels = make(map[string]string)
|
|
||||||
}
|
|
||||||
s.Labels[compose.EnvironmentFileLabel] = ef
|
|
||||||
services = append(services, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
project.Services = services
|
|
||||||
}
|
|
||||||
|
|
||||||
return project, nil
|
|
||||||
}
|
|
||||||
|
|
12
ecs/up.go
12
ecs/up.go
|
@ -23,12 +23,12 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/docker/compose-cli/api/compose"
|
"github.com/docker/compose-cli/api/compose"
|
||||||
"github.com/docker/compose-cli/api/errdefs"
|
"github.com/docker/compose-cli/api/errdefs"
|
||||||
|
"github.com/docker/compose-cli/api/progress"
|
||||||
"github.com/compose-spec/compose-go/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *ecsAPIService) Build(ctx context.Context, project *types.Project, options compose.BuildOptions) error {
|
func (b *ecsAPIService) Build(ctx context.Context, project *types.Project, options compose.BuildOptions) error {
|
||||||
|
@ -80,6 +80,12 @@ func (b *ecsAPIService) Copy(ctx context.Context, project *types.Project, option
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *ecsAPIService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
|
func (b *ecsAPIService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
|
||||||
|
return progress.Run(ctx, func(ctx context.Context) error {
|
||||||
|
return b.up(ctx, project, options)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ecsAPIService) up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
|
||||||
logrus.Debugf("deploying on AWS with region=%q", b.Region)
|
logrus.Debugf("deploying on AWS with region=%q", b.Region)
|
||||||
err := b.aws.CheckRequirements(ctx, b.Region)
|
err := b.aws.CheckRequirements(ctx, b.Region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -124,7 +130,7 @@ func (b *ecsAPIService) Up(ctx context.Context, project *types.Project, options
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if options.Detach {
|
if options.Start.Attach == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
signalChan := make(chan os.Signal, 1)
|
signalChan := make(chan os.Signal, 1)
|
||||||
|
|
|
@ -72,6 +72,12 @@ func NewComposeService() (compose.Service, error) {
|
||||||
|
|
||||||
// Up executes the equivalent to a `compose up`
|
// Up executes the equivalent to a `compose up`
|
||||||
func (s *composeService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
|
func (s *composeService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
|
||||||
|
return progress.Run(ctx, func(ctx context.Context) error {
|
||||||
|
return s.up(ctx, project)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *composeService) up(ctx context.Context, project *types.Project) error {
|
||||||
w := progress.ContextWriter(ctx)
|
w := progress.ContextWriter(ctx)
|
||||||
|
|
||||||
eventName := "Convert to Helm charts"
|
eventName := "Convert to Helm charts"
|
||||||
|
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/compose-cli/api/compose"
|
"github.com/docker/compose-cli/api/compose"
|
||||||
"github.com/docker/compose-cli/api/errdefs"
|
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/docker/cli/cli/config/configfile"
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
|
@ -45,10 +44,6 @@ type composeService struct {
|
||||||
configFile *configfile.ConfigFile
|
configFile *configfile.ConfigFile
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
|
|
||||||
return errdefs.ErrNotImplemented
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCanonicalContainerName(c moby.Container) string {
|
func getCanonicalContainerName(c moby.Container) string {
|
||||||
// Names return container canonical name /foo + link aliases /linked_by/foo
|
// Names return container canonical name /foo + link aliases /linked_by/foo
|
||||||
for _, name := range c.Names {
|
for _, name := range c.Names {
|
||||||
|
|
|
@ -43,9 +43,15 @@ import (
|
||||||
"github.com/docker/compose-cli/utils"
|
"github.com/docker/compose-cli/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *composeService) Create(ctx context.Context, project *types.Project, opts compose.CreateOptions) error {
|
func (s *composeService) Create(ctx context.Context, project *types.Project, options compose.CreateOptions) error {
|
||||||
if len(opts.Services) == 0 {
|
return progress.Run(ctx, func(ctx context.Context) error {
|
||||||
opts.Services = project.ServiceNames()
|
return s.create(ctx, project, options)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *composeService) create(ctx context.Context, project *types.Project, options compose.CreateOptions) error {
|
||||||
|
if len(options.Services) == 0 {
|
||||||
|
options.Services = project.ServiceNames()
|
||||||
}
|
}
|
||||||
|
|
||||||
var observedState Containers
|
var observedState Containers
|
||||||
|
@ -56,7 +62,7 @@ func (s *composeService) Create(ctx context.Context, project *types.Project, opt
|
||||||
containerState := NewContainersState(observedState)
|
containerState := NewContainersState(observedState)
|
||||||
ctx = context.WithValue(ctx, ContainersKey{}, containerState)
|
ctx = context.WithValue(ctx, ContainersKey{}, containerState)
|
||||||
|
|
||||||
err = s.ensureImagesExists(ctx, project, observedState, opts.QuietPull)
|
err = s.ensureImagesExists(ctx, project, observedState, options.QuietPull)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -83,7 +89,7 @@ func (s *composeService) Create(ctx context.Context, project *types.Project, opt
|
||||||
}
|
}
|
||||||
orphans := observedState.filter(isNotService(allServiceNames...))
|
orphans := observedState.filter(isNotService(allServiceNames...))
|
||||||
if len(orphans) > 0 {
|
if len(orphans) > 0 {
|
||||||
if opts.RemoveOrphans {
|
if options.RemoveOrphans {
|
||||||
w := progress.ContextWriter(ctx)
|
w := progress.ContextWriter(ctx)
|
||||||
err := s.removeContainers(ctx, w, orphans, nil)
|
err := s.removeContainers(ctx, w, orphans, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -100,10 +106,10 @@ func (s *composeService) Create(ctx context.Context, project *types.Project, opt
|
||||||
prepareServicesDependsOn(project)
|
prepareServicesDependsOn(project)
|
||||||
|
|
||||||
return InDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error {
|
return InDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error {
|
||||||
if utils.StringContains(opts.Services, service.Name) {
|
if utils.StringContains(options.Services, service.Name) {
|
||||||
return s.ensureService(c, project, service, opts.Recreate, opts.Inherit, opts.Timeout)
|
return s.ensureService(c, project, service, options.Recreate, options.Inherit, options.Timeout)
|
||||||
}
|
}
|
||||||
return s.ensureService(c, project, service, opts.RecreateDependencies, opts.Inherit, opts.Timeout)
|
return s.ensureService(c, project, service, options.RecreateDependencies, options.Inherit, options.Timeout)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *composeService) Kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
|
func (s *composeService) Kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
|
||||||
|
return progress.Run(ctx, func(ctx context.Context) error {
|
||||||
|
return s.kill(ctx, project, options)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *composeService) kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
|
||||||
w := progress.ContextWriter(ctx)
|
w := progress.ContextWriter(ctx)
|
||||||
|
|
||||||
var containers Containers
|
var containers Containers
|
||||||
|
|
|
@ -50,7 +50,7 @@ func TestKillAll(t *testing.T) {
|
||||||
api.EXPECT().ContainerKill(anyCancellableContext(), "456", "").Return(nil)
|
api.EXPECT().ContainerKill(anyCancellableContext(), "456", "").Return(nil)
|
||||||
api.EXPECT().ContainerKill(anyCancellableContext(), "789", "").Return(nil)
|
api.EXPECT().ContainerKill(anyCancellableContext(), "789", "").Return(nil)
|
||||||
|
|
||||||
err := tested.Kill(ctx, &project, compose.KillOptions{})
|
err := tested.kill(ctx, &project, compose.KillOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ func TestKillSignal(t *testing.T) {
|
||||||
api.EXPECT().ContainerList(ctx, projectFilterListOpt()).Return([]apitypes.Container{testContainer("service1", "123")}, nil)
|
api.EXPECT().ContainerList(ctx, projectFilterListOpt()).Return([]apitypes.Container{testContainer("service1", "123")}, nil)
|
||||||
api.EXPECT().ContainerKill(anyCancellableContext(), "123", "SIGTERM").Return(nil)
|
api.EXPECT().ContainerKill(anyCancellableContext(), "123", "SIGTERM").Return(nil)
|
||||||
|
|
||||||
err := tested.Kill(ctx, &project, compose.KillOptions{Signal: "SIGTERM"})
|
err := tested.kill(ctx, &project, compose.KillOptions{Signal: "SIGTERM"})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,45 +19,40 @@ package compose
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/docker/compose-cli/api/compose"
|
|
||||||
"github.com/docker/compose-cli/api/progress"
|
|
||||||
"github.com/docker/compose-cli/utils"
|
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
moby "github.com/docker/docker/api/types"
|
moby "github.com/docker/docker/api/types"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
|
"github.com/docker/compose-cli/api/compose"
|
||||||
|
"github.com/docker/compose-cli/api/progress"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *composeService) Start(ctx context.Context, project *types.Project, options compose.StartOptions) error {
|
func (s *composeService) Start(ctx context.Context, project *types.Project, options compose.StartOptions) error {
|
||||||
return progress.Run(ctx, func(ctx context.Context) error {
|
return progress.Run(ctx, func(ctx context.Context) error {
|
||||||
return s.start(ctx, project, options)
|
return s.start(ctx, project, options, nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) start(ctx context.Context, project *types.Project, options compose.StartOptions) error {
|
func (s *composeService) start(ctx context.Context, project *types.Project, options compose.StartOptions, listener func(event compose.ContainerEvent)) error {
|
||||||
listener := options.Attach
|
if len(options.AttachTo) == 0 {
|
||||||
if len(options.Services) == 0 {
|
options.AttachTo = project.ServiceNames()
|
||||||
options.Services = project.ServiceNames()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
eg, ctx := errgroup.WithContext(ctx)
|
eg, ctx := errgroup.WithContext(ctx)
|
||||||
if listener != nil {
|
if listener != nil {
|
||||||
attached, err := s.attach(ctx, project, listener, options.Services)
|
attached, err := s.attach(ctx, project, listener, options.AttachTo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
return s.watchContainers(project, options.Services, listener, attached)
|
return s.watchContainers(project, options.AttachTo, listener, attached)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
err := InDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error {
|
err := InDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error {
|
||||||
if utils.StringContains(options.Services, service.Name) {
|
return s.startService(ctx, project, service)
|
||||||
return s.startService(ctx, project, service)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 Docker Compose CLI authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package compose
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/docker/compose-cli/api/compose"
|
||||||
|
"github.com/docker/compose-cli/api/progress"
|
||||||
|
|
||||||
|
"github.com/compose-spec/compose-go/types"
|
||||||
|
"github.com/docker/cli/cli"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *composeService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
|
||||||
|
err := progress.Run(ctx, func(ctx context.Context) error {
|
||||||
|
err := s.create(ctx, project, options.Create)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.start(ctx, project, options.Start, nil)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.Start.Attach == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
printer := compose.NewLogPrinter(options.Start.Attach)
|
||||||
|
|
||||||
|
signalChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
stopFunc := func() error {
|
||||||
|
ctx := context.Background()
|
||||||
|
return progress.Run(ctx, func(ctx context.Context) error {
|
||||||
|
go func() {
|
||||||
|
<-signalChan
|
||||||
|
s.Kill(ctx, project, compose.KillOptions{}) // nolint:errcheck
|
||||||
|
}()
|
||||||
|
|
||||||
|
return s.Stop(ctx, project, compose.StopOptions{})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
<-signalChan
|
||||||
|
printer.Cancel()
|
||||||
|
fmt.Println("Gracefully stopping... (press Ctrl+C again to force)")
|
||||||
|
stopFunc() // nolint:errcheck
|
||||||
|
}()
|
||||||
|
|
||||||
|
var exitCode int
|
||||||
|
eg, ctx := errgroup.WithContext(ctx)
|
||||||
|
eg.Go(func() error {
|
||||||
|
code, err := printer.Run(options.Start.CascadeStop, options.Start.ExitCodeFrom, stopFunc)
|
||||||
|
exitCode = code
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
err = s.start(ctx, project, options.Start, printer.HandleEvent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = eg.Wait()
|
||||||
|
if exitCode != 0 {
|
||||||
|
errMsg := ""
|
||||||
|
if err != nil {
|
||||||
|
errMsg = err.Error()
|
||||||
|
}
|
||||||
|
return cli.StatusError{StatusCode: exitCode, Status: errMsg}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче