diff --git a/pkg/api/labels.go b/pkg/api/labels.go index fd8d6a98e..32b0ca111 100644 --- a/pkg/api/labels.go +++ b/pkg/api/labels.go @@ -47,6 +47,8 @@ const ( OneoffLabel = "com.docker.compose.oneoff" // SlugLabel stores unique slug used for one-off container identity SlugLabel = "com.docker.compose.slug" + // ImageDigestLabel stores digest of the container image used to run service + ImageDigestLabel = "com.docker.compose.image" // VersionLabel stores the compose tool version used to run application VersionLabel = "com.docker.compose.version" ) diff --git a/pkg/compose/build.go b/pkg/compose/build.go index 7d978ffde..dba0dab8b 100644 --- a/pkg/compose/build.go +++ b/pkg/compose/build.go @@ -28,7 +28,6 @@ import ( _ "github.com/docker/buildx/driver/docker" // required to get default driver registered "github.com/docker/buildx/util/buildflags" xprogress "github.com/docker/buildx/util/progress" - moby "github.com/docker/docker/api/types" bclient "github.com/moby/buildkit/client" "github.com/moby/buildkit/session" "github.com/moby/buildkit/session/auth/authprovider" @@ -80,7 +79,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti } } - _, err := s.doBuild(ctx, project, opts, Containers{}, options.Progress) + _, err := s.doBuild(ctx, project, opts, options.Progress) if err == nil { if len(imagesToBuild) > 0 && !options.Quiet { utils.DisplayScanSuggestMsg() @@ -90,7 +89,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti return err } -func (s *composeService) ensureImagesExists(ctx context.Context, project *types.Project, observedState Containers, quietPull bool) error { +func (s *composeService) ensureImagesExists(ctx context.Context, project *types.Project, quietPull bool) error { for _, service := range project.Services { if service.Image == "" && service.Build == nil { return fmt.Errorf("invalid service %q. Must specify either image or build", service.Name) @@ -111,37 +110,41 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types. if quietPull { mode = xprogress.PrinterModeQuiet } - opts, imagesToBuild, err := s.getBuildOptions(project, images) + opts, err := s.getBuildOptions(project, images) if err != nil { return err } - builtImages, err := s.doBuild(ctx, project, opts, observedState, mode) + builtImages, err := s.doBuild(ctx, project, opts, mode) if err != nil { return err } - if len(imagesToBuild) > 0 { + if len(builtImages) > 0 { utils.DisplayScanSuggestMsg() } for name, digest := range builtImages { images[name] = digest } - // set digest as service.Image + // set digest as com.docker.compose.image label so we can detect outdated containers for i, service := range project.Services { - digest, ok := images[getImageName(service, project.Name)] + image := getImageName(service, project.Name) + digest, ok := images[image] if ok { - project.Services[i].Image = digest + if project.Services[i].Labels == nil { + project.Services[i].Labels = types.Labels{} + } + project.Services[i].Labels[api.ImageDigestLabel] = digest + project.Services[i].Image = image } } return nil } -func (s *composeService) getBuildOptions(project *types.Project, images map[string]string) (map[string]build.Options, []string, error) { +func (s *composeService) getBuildOptions(project *types.Project, images map[string]string) (map[string]build.Options, error) { opts := map[string]build.Options{} - imagesToBuild := []string{} for _, service := range project.Services { if service.Image == "" && service.Build == nil { - return nil, nil, fmt.Errorf("invalid service %q. Must specify either image or build", service.Name) + return nil, fmt.Errorf("invalid service %q. Must specify either image or build", service.Name) } imageName := getImageName(service, project.Name) _, localImagePresent := images[imageName] @@ -150,16 +153,15 @@ func (s *composeService) getBuildOptions(project *types.Project, images map[stri if localImagePresent && service.PullPolicy != types.PullPolicyBuild { continue } - imagesToBuild = append(imagesToBuild, imageName) opt, err := s.toBuildOptions(project, service, imageName) if err != nil { - return nil, nil, err + return nil, err } opts[imageName] = opt continue } } - return opts, imagesToBuild, nil + return opts, nil } @@ -182,7 +184,7 @@ func (s *composeService) getLocalImagesDigests(ctx context.Context, project *typ return images, nil } -func (s *composeService) doBuild(ctx context.Context, project *types.Project, opts map[string]build.Options, observedState Containers, mode string) (map[string]string, error) { +func (s *composeService) doBuild(ctx context.Context, project *types.Project, opts map[string]build.Options, mode string) (map[string]string, error) { info, err := s.apiClient.Info(ctx) if err != nil { return nil, err @@ -227,18 +229,6 @@ func (s *composeService) doBuild(ctx context.Context, project *types.Project, op return nil, WrapCategorisedComposeError(err, BuildFailure) } - cw := progress.ContextWriter(ctx) - for _, c := range observedState { - for imageName := range opts { - if c.Image == imageName { - err = s.removeContainers(ctx, cw, []moby.Container{c}, nil, false) - if err != nil { - return nil, err - } - } - } - } - imagesBuilt := map[string]string{} for name, img := range response { if img == nil || len(img.ExporterResponse) == 0 { diff --git a/pkg/compose/create.go b/pkg/compose/create.go index 4c85bb447..9e56b248f 100644 --- a/pkg/compose/create.go +++ b/pkg/compose/create.go @@ -60,7 +60,7 @@ func (s *composeService) create(ctx context.Context, project *types.Project, opt return err } - err = s.ensureImagesExists(ctx, project, observedState, options.QuietPull) + err = s.ensureImagesExists(ctx, project, options.QuietPull) if err != nil { return err } diff --git a/pkg/compose/run.go b/pkg/compose/run.go index 4da052e49..d70b87201 100644 --- a/pkg/compose/run.go +++ b/pkg/compose/run.go @@ -33,12 +33,7 @@ import ( ) func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts api.RunOptions) (int, error) { - observedState, err := s.getContainers(ctx, project.Name, oneOffInclude, true) - if err != nil { - return 0, err - } - - containerID, err := s.prepareRun(ctx, project, observedState, opts) + containerID, err := s.prepareRun(ctx, project, opts) if err != nil { return 0, err } @@ -131,7 +126,7 @@ func (s *composeService) runInteractive(ctx context.Context, containerID string, } } -func (s *composeService) prepareRun(ctx context.Context, project *types.Project, observedState Containers, opts api.RunOptions) (string, error) { +func (s *composeService) prepareRun(ctx context.Context, project *types.Project, opts api.RunOptions) (string, error) { service, err := project.GetService(opts.Service) if err != nil { return "", err @@ -152,7 +147,7 @@ func (s *composeService) prepareRun(ctx context.Context, project *types.Project, service.Labels = service.Labels.Add(api.SlugLabel, slug) service.Labels = service.Labels.Add(api.OneoffLabel, "True") - if err := s.ensureImagesExists(ctx, project, observedState, false); err != nil { // all dependencies already checked, but might miss service img + if err := s.ensureImagesExists(ctx, project, false); err != nil { // all dependencies already checked, but might miss service img return "", err } if err := s.waitDependencies(ctx, project, service); err != nil { diff --git a/pkg/compose/up.go b/pkg/compose/up.go index 8db486b1d..acf4fdf23 100644 --- a/pkg/compose/up.go +++ b/pkg/compose/up.go @@ -37,7 +37,10 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options if err != nil { return err } - return s.start(ctx, project, options.Start, nil) + if options.Start.Attach == nil { + return s.start(ctx, project, options.Start, nil) + } + return nil }) if err != nil { return err