2020-05-14 21:13:07 +02:00
/ *
2020-09-22 12:13:00 +02:00
Copyright 2020 Docker Compose CLI authors
2020-05-14 21:13:07 +02:00
2020-06-18 16:13:24 +02:00
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
2020-05-14 21:13:07 +02:00
2020-06-18 16:13:24 +02:00
http : //www.apache.org/licenses/LICENSE-2.0
2020-05-14 21:13:07 +02:00
2020-06-18 16:13:24 +02:00
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 .
2020-05-14 21:13:07 +02:00
* /
2020-05-14 21:04:38 +02:00
package compose
import (
"context"
2020-12-02 18:23:01 +01:00
"fmt"
2020-12-01 20:58:03 +01:00
"os"
2021-02-12 11:16:36 +01:00
"strconv"
"strings"
2020-05-14 21:04:38 +02:00
2021-08-31 18:53:24 +02:00
"github.com/docker/compose/v2/cmd/formatter"
2021-08-31 15:41:20 +02:00
2020-12-07 09:09:12 +01:00
"github.com/compose-spec/compose-go/types"
2021-06-15 09:57:38 +02:00
"github.com/spf13/cobra"
2021-08-31 18:53:24 +02:00
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/utils"
2020-05-14 21:04:38 +02:00
)
2021-01-20 11:02:46 +01:00
// composeOptions hold options common to `up` and `run` to run compose project
type composeOptions struct {
* projectOptions
}
2021-01-06 14:23:37 +01:00
type upOptions struct {
2021-01-20 11:02:46 +01:00
* composeOptions
2021-03-02 22:27:34 -03:00
Detach bool
noStart bool
noDeps bool
cascadeStop bool
exitCodeFrom string
scale [ ] string
noColor bool
noPrefix bool
attachDependencies bool
2021-09-07 08:24:52 +02:00
attach [ ] string
2021-10-11 17:52:31 +02:00
wait bool
2021-03-02 14:33:26 +01:00
}
2021-03-02 09:30:26 +01:00
func ( opts upOptions ) apply ( project * types . Project , services [ ] string ) error {
if opts . noDeps {
enabled , err := project . GetServices ( services ... )
if err != nil {
return err
}
for _ , s := range project . Services {
2021-03-19 09:49:14 +01:00
if ! utils . StringContains ( services , s . Name ) {
2021-03-02 09:30:26 +01:00
project . DisabledServices = append ( project . DisabledServices , s )
}
}
project . Services = enabled
}
if opts . exitCodeFrom != "" {
_ , err := project . GetService ( opts . exitCodeFrom )
if err != nil {
return err
}
}
for _ , scale := range opts . scale {
split := strings . Split ( scale , "=" )
if len ( split ) != 2 {
return fmt . Errorf ( "invalid --scale option %q. Should be SERVICE=NUM" , scale )
}
name := split [ 0 ]
replicas , err := strconv . Atoi ( split [ 1 ] )
if err != nil {
return err
}
2021-09-15 09:02:48 +02:00
err = setServiceScale ( project , name , uint64 ( replicas ) )
2021-03-02 09:30:26 +01:00
if err != nil {
return err
}
}
return nil
}
2021-06-14 16:26:14 +02:00
func upCommand ( p * projectOptions , backend api . Service ) * cobra . Command {
2021-06-07 14:21:55 +02:00
up := upOptions { }
create := createOptions { }
2020-05-14 21:04:38 +02:00
upCmd := & cobra . Command {
2020-12-10 15:43:20 +01:00
Use : "up [SERVICE...]" ,
Short : "Create and start containers" ,
2021-10-13 09:11:44 +05:30
PreRunE : AdaptCmd ( func ( ctx context . Context , cmd * cobra . Command , args [ ] string ) error {
2021-06-07 14:21:55 +02:00
create . timeChanged = cmd . Flags ( ) . Changed ( "timeout" )
2021-10-11 17:52:31 +02:00
return validateFlags ( & up , & create )
2021-05-19 18:49:33 +02:00
} ) ,
2021-06-07 14:21:55 +02:00
RunE : p . WithServices ( func ( ctx context . Context , project * types . Project , services [ ] string ) error {
2021-09-24 08:22:50 +02:00
ignore := project . Environment [ "COMPOSE_IGNORE_ORPHANS" ]
create . ignoreOrphans = strings . ToLower ( ignore ) == "true"
if create . ignoreOrphans && create . removeOrphans {
return fmt . Errorf ( "COMPOSE_IGNORE_ORPHANS and --remove-orphans cannot be combined" )
}
2021-06-07 14:21:55 +02:00
return runUp ( ctx , backend , create , up , project , services )
2021-04-15 12:43:18 +02:00
} ) ,
2021-06-10 09:28:06 -03:00
ValidArgsFunction : serviceCompletion ( p ) ,
2020-05-14 21:04:38 +02:00
}
2021-01-20 11:02:46 +01:00
flags := upCmd . Flags ( )
2021-06-07 14:21:55 +02:00
flags . BoolVarP ( & up . Detach , "detach" , "d" , false , "Detached mode: Run containers in the background" )
flags . BoolVar ( & create . Build , "build" , false , "Build images before starting containers." )
flags . BoolVar ( & create . noBuild , "no-build" , false , "Don't build an image, even if it's missing." )
flags . BoolVar ( & create . removeOrphans , "remove-orphans" , false , "Remove containers for services not defined in the Compose file." )
flags . StringArrayVar ( & up . scale , "scale" , [ ] string { } , "Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present." )
flags . BoolVar ( & up . noColor , "no-color" , false , "Produce monochrome output." )
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." )
flags . BoolVar ( & create . noRecreate , "no-recreate" , false , "If containers already exist, don't recreate them. Incompatible with --force-recreate." )
flags . BoolVar ( & up . noStart , "no-start" , false , "Don't start the services after creating them." )
flags . BoolVar ( & up . cascadeStop , "abort-on-container-exit" , false , "Stops all containers if any container was stopped. Incompatible with -d" )
flags . StringVar ( & up . exitCodeFrom , "exit-code-from" , "" , "Return the exit code of the selected service container. Implies --abort-on-container-exit" )
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 ( & up . noDeps , "no-deps" , false , "Don't start linked services." )
flags . BoolVar ( & create . recreateDeps , "always-recreate-deps" , false , "Recreate dependent containers. Incompatible with --no-recreate." )
flags . BoolVarP ( & create . noInherit , "renew-anon-volumes" , "V" , false , "Recreate anonymous volumes instead of retrieving data from the previous containers." )
flags . BoolVar ( & up . attachDependencies , "attach-dependencies" , false , "Attach to dependent containers." )
flags . BoolVar ( & create . quietPull , "quiet-pull" , false , "Pull without printing progress information." )
2021-09-07 08:24:52 +02:00
flags . StringArrayVar ( & up . attach , "attach" , [ ] string { } , "Attach to service output." )
2021-10-11 17:52:31 +02:00
flags . BoolVar ( & up . wait , "wait" , false , "Wait for services to be running|healthy. Implies detached mode." )
2020-09-21 17:05:49 +02:00
2020-05-14 21:04:38 +02:00
return upCmd
}
2021-10-11 17:52:31 +02:00
func validateFlags ( up * upOptions , create * createOptions ) error {
if up . exitCodeFrom != "" {
up . cascadeStop = true
}
if up . wait {
if up . attachDependencies || up . cascadeStop || len ( up . attach ) > 0 {
return fmt . Errorf ( "--wait cannot be combined with --abort-on-container-exit, --attach or --attach-dependencies" )
}
up . Detach = true
}
if create . Build && create . noBuild {
return fmt . Errorf ( "--build and --no-build are incompatible" )
}
if up . Detach && ( up . attachDependencies || up . cascadeStop || len ( up . attach ) > 0 ) {
return fmt . Errorf ( "--detach cannot be combined with --abort-on-container-exit, --attach or --attach-dependencies" )
}
if create . forceRecreate && create . noRecreate {
return fmt . Errorf ( "--force-recreate and --no-recreate are incompatible" )
}
if create . recreateDeps && create . noRecreate {
return fmt . Errorf ( "--always-recreate-deps and --no-recreate are incompatible" )
}
return nil
}
2021-06-14 16:26:14 +02:00
func runUp ( ctx context . Context , backend api . Service , createOptions createOptions , upOptions upOptions , project * types . Project , services [ ] string ) error {
2021-04-06 10:37:31 +02:00
if len ( project . Services ) == 0 {
return fmt . Errorf ( "no service selected" )
}
2021-06-07 14:21:55 +02:00
createOptions . Apply ( project )
err := upOptions . apply ( project , services )
2020-12-01 20:58:03 +01:00
if err != nil {
return err
}
2020-12-02 18:23:01 +01:00
2021-06-14 16:26:14 +02:00
var consumer api . LogConsumer
2021-06-07 14:21:55 +02:00
if ! upOptions . Detach {
consumer = formatter . NewLogConsumer ( ctx , os . Stdout , ! upOptions . noColor , ! upOptions . noPrefix )
2021-01-28 11:09:35 +01:00
}
2021-06-07 14:21:55 +02:00
attachTo := services
2021-09-07 08:24:52 +02:00
if len ( upOptions . attach ) > 0 {
attachTo = upOptions . attach
}
2021-06-07 14:21:55 +02:00
if upOptions . attachDependencies {
attachTo = project . ServiceNames ( )
2021-03-02 22:27:34 -03:00
}
2021-06-14 16:26:14 +02:00
create := api . CreateOptions {
2021-09-20 12:31:41 +02:00
Services : services ,
2021-06-07 14:21:55 +02:00
RemoveOrphans : createOptions . removeOrphans ,
2021-09-24 08:22:50 +02:00
IgnoreOrphans : createOptions . ignoreOrphans ,
2021-06-07 14:21:55 +02:00
Recreate : createOptions . recreateStrategy ( ) ,
RecreateDependencies : createOptions . dependenciesRecreateStrategy ( ) ,
Inherit : ! createOptions . noInherit ,
Timeout : createOptions . GetTimeout ( ) ,
QuietPull : createOptions . quietPull ,
2020-12-02 18:23:01 +01:00
}
2021-06-07 14:21:55 +02:00
if upOptions . noStart {
return backend . Create ( ctx , project , create )
2021-02-09 16:51:48 +01:00
}
2021-02-08 11:42:42 +01:00
2021-06-14 16:26:14 +02:00
return backend . Up ( ctx , project , api . UpOptions {
2021-06-07 14:21:55 +02:00
Create : create ,
2021-06-14 16:26:14 +02:00
Start : api . StartOptions {
2021-06-07 14:21:55 +02:00
Attach : consumer ,
AttachTo : attachTo ,
ExitCodeFrom : upOptions . exitCodeFrom ,
CascadeStop : upOptions . cascadeStop ,
2021-10-11 17:52:31 +02:00
Wait : upOptions . wait ,
2021-06-07 14:21:55 +02:00
} ,
2021-02-08 11:04:46 +01:00
} )
2021-02-09 12:40:31 +01:00
}
2021-09-15 09:02:48 +02:00
func setServiceScale ( project * types . Project , name string , replicas uint64 ) error {
2021-02-12 14:54:57 +01:00
for i , s := range project . Services {
if s . Name == name {
service , err := project . GetService ( name )
if err != nil {
return err
}
2021-09-15 09:02:48 +02:00
if service . Deploy == nil {
service . Deploy = & types . DeployConfig { }
}
service . Deploy . Replicas = & replicas
2021-02-12 14:54:57 +01:00
project . Services [ i ] = service
return nil
}
}
return fmt . Errorf ( "unknown service %q" , name )
}