Browse Source

a single place for shell-out command setup

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 5 months ago
parent
commit
60ee6adcd2
4 changed files with 92 additions and 94 deletions
  1. 3 36
      pkg/compose/build_bake.go
  2. 22 30
      pkg/compose/model.go
  3. 6 28
      pkg/compose/plugins.go
  4. 61 0
      pkg/compose/shellout.go

+ 3 - 36
pkg/compose/build_bake.go

@@ -34,7 +34,6 @@ import (
 
 	"github.com/compose-spec/compose-go/v2/types"
 	"github.com/docker/cli/cli-plugins/manager"
-	"github.com/docker/cli/cli-plugins/socket"
 	"github.com/docker/cli/cli/command"
 	"github.com/docker/compose/v2/pkg/api"
 	"github.com/docker/compose/v2/pkg/progress"
@@ -45,8 +44,6 @@ import (
 	"github.com/moby/buildkit/util/progress/progressui"
 	"github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
-	"go.opentelemetry.io/otel"
-	"go.opentelemetry.io/otel/propagation"
 	"golang.org/x/sync/errgroup"
 )
 
@@ -294,26 +291,12 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
 	logrus.Debugf("Executing bake with args: %v", args)
 
 	cmd := exec.CommandContext(ctx, buildx.Path, args...)
-	// Remove DOCKER_CLI_PLUGIN... variable so buildx can detect it run standalone
-	cmd.Env = filter(project.Environment.Values(), manager.ReexecEnvvar)
 
-	// Use docker/cli mechanism to propagate termination signal to child process
-	server, err := socket.NewPluginServer(nil)
-	if err == nil {
-		defer server.Close() //nolint:errcheck
-		cmd.Env = replace(cmd.Env, socket.EnvKey, server.Addr().String())
+	err = s.prepareShellOut(ctx, project, cmd)
+	if err != nil {
+		return nil, err
 	}
 
-	cmd.Env = append(cmd.Env,
-		fmt.Sprintf("DOCKER_CONTEXT=%s", s.dockerCli.CurrentContext()),
-		fmt.Sprintf("DOCKER_HOST=%s", s.dockerCli.DockerEndpoint().Host),
-	)
-
-	// propagate opentelemetry context to child process, see https://github.com/open-telemetry/oteps/blob/main/text/0258-env-context-baggage-carriers.md
-	carrier := propagation.MapCarrier{}
-	otel.GetTextMapPropagator().Inject(ctx, &carrier)
-	cmd.Env = append(cmd.Env, types.Mapping(carrier).Values()...)
-
 	cmd.Stdout = s.stdout()
 	cmd.Stdin = bytes.NewBuffer(b)
 	pipe, err := cmd.StderrPipe()
@@ -443,22 +426,6 @@ func toBakeSecrets(project *types.Project, secrets []types.ServiceSecretConfig)
 	return s
 }
 
-func filter(environ []string, variable string) []string {
-	prefix := variable + "="
-	filtered := make([]string, 0, len(environ))
-	for _, val := range environ {
-		if !strings.HasPrefix(val, prefix) {
-			filtered = append(filtered, val)
-		}
-	}
-	return filtered
-}
-
-func replace(environ []string, variable, value string) []string {
-	filtered := filter(environ, variable)
-	return append(filtered, fmt.Sprintf("%s=%s", variable, value))
-}
-
 func dockerFilePath(ctxName string, dockerfile string) string {
 	if dockerfile == "" {
 		return ""

+ 22 - 30
pkg/compose/model.go

@@ -21,7 +21,6 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-	"os"
 	"os/exec"
 	"slices"
 	"strconv"
@@ -32,8 +31,6 @@ import (
 	"github.com/docker/cli/cli-plugins/manager"
 	"github.com/docker/compose/v2/pkg/progress"
 	"github.com/spf13/cobra"
-	"go.opentelemetry.io/otel"
-	"go.opentelemetry.io/otel/propagation"
 	"golang.org/x/sync/errgroup"
 )
 
@@ -51,7 +48,11 @@ func (s *composeService) ensureModels(ctx context.Context, project *types.Projec
 	}
 
 	cmd := exec.CommandContext(ctx, dockerModel.Path, "ls", "--json")
-	s.setupChildProcess(ctx, cmd)
+	err = s.prepareShellOut(ctx, project, cmd)
+	if err != nil {
+		return err
+	}
+
 	output, err := cmd.CombinedOutput()
 	if err != nil {
 		return fmt.Errorf("error checking available models: %w", err)
@@ -85,23 +86,18 @@ func (s *composeService) ensureModels(ctx context.Context, project *types.Projec
 		eg.Go(func() error {
 			w := progress.ContextWriter(gctx)
 			if !slices.Contains(availableModels, config.Model) {
-				err = s.pullModel(gctx, dockerModel, config, quietPull, w)
+				err = s.pullModel(gctx, dockerModel, project, config, quietPull, w)
 				if err != nil {
 					return err
 				}
 			}
-			err = s.configureModel(gctx, dockerModel, config, w)
-			if err != nil {
-				return err
-			}
-			w.Event(progress.CreatedEvent(config.Name))
-			return nil
+			return s.configureModel(gctx, dockerModel, project, config, w)
 		})
 	}
 	return eg.Wait()
 }
 
-func (s *composeService) pullModel(ctx context.Context, dockerModel *manager.Plugin, model types.ModelConfig, quietPull bool, w progress.Writer) error {
+func (s *composeService) pullModel(ctx context.Context, dockerModel *manager.Plugin, project *types.Project, model types.ModelConfig, quietPull bool, w progress.Writer) error {
 	w.Event(progress.Event{
 		ID:     model.Name,
 		Status: progress.Working,
@@ -109,8 +105,10 @@ func (s *composeService) pullModel(ctx context.Context, dockerModel *manager.Plu
 	})
 
 	cmd := exec.CommandContext(ctx, dockerModel.Path, "pull", model.Model)
-	s.setupChildProcess(ctx, cmd)
-
+	err := s.prepareShellOut(ctx, project, cmd)
+	if err != nil {
+		return err
+	}
 	stream, err := cmd.StdoutPipe()
 	if err != nil {
 		return err
@@ -150,7 +148,7 @@ func (s *composeService) pullModel(ctx context.Context, dockerModel *manager.Plu
 	return err
 }
 
-func (s *composeService) configureModel(ctx context.Context, dockerModel *manager.Plugin, config types.ModelConfig, w progress.Writer) error {
+func (s *composeService) configureModel(ctx context.Context, dockerModel *manager.Plugin, project *types.Project, config types.ModelConfig, w progress.Writer) error {
 	w.Event(progress.Event{
 		ID:     config.Name,
 		Status: progress.Working,
@@ -167,13 +165,20 @@ func (s *composeService) configureModel(ctx context.Context, dockerModel *manage
 		args = append(args, config.RuntimeFlags...)
 	}
 	cmd := exec.CommandContext(ctx, dockerModel.Path, args...)
-	s.setupChildProcess(ctx, cmd)
+	err := s.prepareShellOut(ctx, project, cmd)
+	if err != nil {
+		return err
+	}
 	return cmd.Run()
 }
 
 func (s *composeService) setModelVariables(ctx context.Context, dockerModel *manager.Plugin, project *types.Project) error {
 	cmd := exec.CommandContext(ctx, dockerModel.Path, "status", "--json")
-	s.setupChildProcess(ctx, cmd)
+	err := s.prepareShellOut(ctx, project, cmd)
+	if err != nil {
+		return err
+	}
+
 	statusOut, err := cmd.CombinedOutput()
 	if err != nil {
 		return fmt.Errorf("error checking docker-model status: %w", err)
@@ -211,19 +216,6 @@ func (s *composeService) setModelVariables(ctx context.Context, dockerModel *man
 	return nil
 }
 
-func (s *composeService) setupChildProcess(gctx context.Context, cmd *exec.Cmd) {
-	// exec provider command with same environment Compose is running
-	env := types.NewMapping(os.Environ())
-	// but remove DOCKER_CLI_PLUGIN... variable so plugin can detect it run standalone
-	delete(env, manager.ReexecEnvvar)
-	// propagate opentelemetry context to child process, see https://github.com/open-telemetry/oteps/blob/main/text/0258-env-context-baggage-carriers.md
-	carrier := propagation.MapCarrier{}
-	otel.GetTextMapPropagator().Inject(gctx, &carrier)
-	env.Merge(types.Mapping(carrier))
-	env["DOCKER_CONTEXT"] = s.dockerCli.CurrentContext()
-	cmd.Env = env.Values()
-}
-
 type Model struct {
 	Id      string   `json:"id"`
 	Tags    []string `json:"tags"`

+ 6 - 28
pkg/compose/plugins.go

@@ -29,16 +29,12 @@ import (
 	"strings"
 	"sync"
 
-	"github.com/docker/compose/v2/pkg/progress"
-	"github.com/sirupsen/logrus"
-	"github.com/spf13/cobra"
-	"go.opentelemetry.io/otel"
-	"go.opentelemetry.io/otel/propagation"
-
 	"github.com/compose-spec/compose-go/v2/types"
 	"github.com/docker/cli/cli-plugins/manager"
-	"github.com/docker/cli/cli-plugins/socket"
 	"github.com/docker/cli/cli/config"
+	"github.com/docker/compose/v2/pkg/progress"
+	"github.com/sirupsen/logrus"
+	"github.com/spf13/cobra"
 )
 
 type JsonMessage struct {
@@ -199,29 +195,11 @@ func (s *composeService) setupPluginCommand(ctx context.Context, project *types.
 	args = append(args, service.Name)
 
 	cmd := exec.CommandContext(ctx, path, args...)
-	// exec provider command with same environment Compose is running
-	env := types.NewMapping(os.Environ())
-	// but remove DOCKER_CLI_PLUGIN... variable so plugin can detect it run standalone
-	delete(env, manager.ReexecEnvvar)
-	// and add the explicit environment variables set for service
-	for key, val := range service.Environment.RemoveEmpty().ToMapping() {
-		env[key] = val
-	}
-	cmd.Env = env.Values()
 
-	// Use docker/cli mechanism to propagate termination signal to child process
-	server, err := socket.NewPluginServer(nil)
-	if err == nil {
-		defer server.Close() //nolint:errcheck
-		cmd.Env = replace(cmd.Env, socket.EnvKey, server.Addr().String())
+	err := s.prepareShellOut(ctx, project, cmd)
+	if err != nil {
+		return nil, err
 	}
-
-	cmd.Env = append(cmd.Env, fmt.Sprintf("DOCKER_CONTEXT=%s", s.dockerCli.CurrentContext()))
-
-	// propagate opentelemetry context to child process, see https://github.com/open-telemetry/oteps/blob/main/text/0258-env-context-baggage-carriers.md
-	carrier := propagation.MapCarrier{}
-	otel.GetTextMapPropagator().Inject(ctx, &carrier)
-	cmd.Env = append(cmd.Env, types.Mapping(carrier).Values()...)
 	return cmd, nil
 }
 

+ 61 - 0
pkg/compose/shellout.go

@@ -0,0 +1,61 @@
+/*
+   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"
+	"os/exec"
+
+	"github.com/compose-spec/compose-go/v2/types"
+	"github.com/docker/cli/cli-plugins/manager"
+	"github.com/docker/cli/cli/context/docker"
+	"go.opentelemetry.io/otel"
+	"go.opentelemetry.io/otel/propagation"
+)
+
+// prepareShellOut prepare a shell-out command to be ran by Compose
+func (s *composeService) prepareShellOut(gctx context.Context, project *types.Project, cmd *exec.Cmd) error {
+	// exec command with same environment Compose is running
+	env := types.NewMapping(project.Environment.Values())
+
+	// remove DOCKER_CLI_PLUGIN... variable so a docker-cli plugin will detect it run standalone
+	delete(env, manager.ReexecEnvvar)
+
+	// propagate opentelemetry context to child process, see https://github.com/open-telemetry/oteps/blob/main/text/0258-env-context-baggage-carriers.md
+	carrier := propagation.MapCarrier{}
+	otel.GetTextMapPropagator().Inject(gctx, &carrier)
+	env.Merge(types.Mapping(carrier))
+
+	env["DOCKER_CONTEXT"] = s.dockerCli.CurrentContext()
+
+	metadata, err := s.dockerCli.ContextStore().GetMetadata(s.dockerCli.CurrentContext())
+	if err != nil {
+		return err
+	}
+	endpoint, err := docker.EndpointFromContext(metadata)
+	if err != nil {
+		return err
+	}
+	actualHost := s.dockerCli.DockerEndpoint().Host
+	if endpoint.Host != actualHost {
+		// We are running with `--host` or `DOCKER_HOST` which overrides selected context
+		env["DOCKER_HOST"] = actualHost
+	}
+
+	cmd.Env = env.Values()
+	return nil
+}