github.com/containerd/nerdctl@v1.7.7/pkg/composer/up.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package composer 18 19 import ( 20 "context" 21 "fmt" 22 "os" 23 24 "github.com/compose-spec/compose-go/types" 25 "github.com/containerd/log" 26 "github.com/containerd/nerdctl/pkg/composer/serviceparser" 27 "github.com/containerd/nerdctl/pkg/reflectutil" 28 ) 29 30 type UpOptions struct { 31 Detach bool 32 NoBuild bool 33 NoColor bool 34 NoLogPrefix bool 35 ForceBuild bool 36 IPFS bool 37 QuietPull bool 38 RemoveOrphans bool 39 Scale map[string]uint64 // map of service name to replicas 40 } 41 42 func (c *Composer) Up(ctx context.Context, uo UpOptions, services []string) error { 43 for shortName := range c.project.Networks { 44 if err := c.upNetwork(ctx, shortName); err != nil { 45 return err 46 } 47 } 48 49 for shortName := range c.project.Volumes { 50 if err := c.upVolume(ctx, shortName); err != nil { 51 return err 52 } 53 } 54 55 for shortName, secret := range c.project.Secrets { 56 obj := types.FileObjectConfig(secret) 57 if err := validateFileObjectConfig(obj, shortName, "service", c.project); err != nil { 58 return err 59 } 60 } 61 62 for shortName, config := range c.project.Configs { 63 obj := types.FileObjectConfig(config) 64 if err := validateFileObjectConfig(obj, shortName, "config", c.project); err != nil { 65 return err 66 } 67 } 68 69 var parsedServices []*serviceparser.Service 70 // use WithServices to sort the services in dependency order 71 if err := c.project.WithServices(services, func(svc types.ServiceConfig) error { 72 if replicas, ok := uo.Scale[svc.Name]; ok { 73 if svc.Deploy == nil { 74 svc.Deploy = &types.DeployConfig{} 75 } 76 svc.Deploy.Replicas = &replicas 77 } 78 ps, err := serviceparser.Parse(c.project, svc) 79 if err != nil { 80 return err 81 } 82 parsedServices = append(parsedServices, ps) 83 return nil 84 }); err != nil { 85 return err 86 } 87 88 // remove orphan containers before the service has be started 89 // FYI: https://github.com/docker/compose/blob/v2.3.4/pkg/compose/create.go#L91-L112 90 orphans, err := c.getOrphanContainers(ctx, parsedServices) 91 if err != nil && uo.RemoveOrphans { 92 return fmt.Errorf("error getting orphaned containers: %s", err) 93 } 94 if len(orphans) > 0 { 95 if uo.RemoveOrphans { 96 if err := c.removeContainers(ctx, orphans, RemoveOptions{Stop: true, Volumes: true}); err != nil { 97 return fmt.Errorf("error removing orphaned containers: %s", err) 98 } 99 } else { 100 log.G(ctx).Warnf("found %d orphaned containers: %v, you can run this command with the --remove-orphans flag to clean it up", len(orphans), orphans) 101 } 102 } 103 104 return c.upServices(ctx, parsedServices, uo) 105 } 106 107 func validateFileObjectConfig(obj types.FileObjectConfig, shortName, objType string, project *types.Project) error { 108 if unknown := reflectutil.UnknownNonEmptyFields(&obj, "Name", "External", "File"); len(unknown) > 0 { 109 log.L.Warnf("Ignoring: %s %s: %+v", objType, shortName, unknown) 110 } 111 if obj.External.External || obj.External.Name != "" { 112 return fmt.Errorf("%s %q: external object is not supported", objType, shortName) 113 } 114 if obj.File == "" { 115 return fmt.Errorf("%s %q: lacks file path", objType, shortName) 116 } 117 fullPath := project.RelativePath(obj.File) 118 if _, err := os.Stat(fullPath); err != nil { 119 return fmt.Errorf("%s %q: failed to open file %q: %w", objType, shortName, fullPath, err) 120 } 121 return nil 122 }