mirror of
https://github.com/docker/compose.git
synced 2025-07-26 07:04:32 +02:00
a single place for shell-out command setup
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
8faf1eb808
commit
60ee6adcd2
@ -34,7 +34,6 @@ import (
|
|||||||
|
|
||||||
"github.com/compose-spec/compose-go/v2/types"
|
"github.com/compose-spec/compose-go/v2/types"
|
||||||
"github.com/docker/cli/cli-plugins/manager"
|
"github.com/docker/cli/cli-plugins/manager"
|
||||||
"github.com/docker/cli/cli-plugins/socket"
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/compose/v2/pkg/api"
|
"github.com/docker/compose/v2/pkg/api"
|
||||||
"github.com/docker/compose/v2/pkg/progress"
|
"github.com/docker/compose/v2/pkg/progress"
|
||||||
@ -45,8 +44,6 @@ import (
|
|||||||
"github.com/moby/buildkit/util/progress/progressui"
|
"github.com/moby/buildkit/util/progress/progressui"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"go.opentelemetry.io/otel"
|
|
||||||
"go.opentelemetry.io/otel/propagation"
|
|
||||||
"golang.org/x/sync/errgroup"
|
"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)
|
logrus.Debugf("Executing bake with args: %v", args)
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, buildx.Path, 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
|
err = s.prepareShellOut(ctx, project, cmd)
|
||||||
server, err := socket.NewPluginServer(nil)
|
if err != nil {
|
||||||
if err == nil {
|
return nil, err
|
||||||
defer server.Close() //nolint:errcheck
|
|
||||||
cmd.Env = replace(cmd.Env, socket.EnvKey, server.Addr().String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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.Stdout = s.stdout()
|
||||||
cmd.Stdin = bytes.NewBuffer(b)
|
cmd.Stdin = bytes.NewBuffer(b)
|
||||||
pipe, err := cmd.StderrPipe()
|
pipe, err := cmd.StderrPipe()
|
||||||
@ -443,22 +426,6 @@ func toBakeSecrets(project *types.Project, secrets []types.ServiceSecretConfig)
|
|||||||
return s
|
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 {
|
func dockerFilePath(ctxName string, dockerfile string) string {
|
||||||
if dockerfile == "" {
|
if dockerfile == "" {
|
||||||
return ""
|
return ""
|
||||||
|
@ -21,7 +21,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -32,8 +31,6 @@ import (
|
|||||||
"github.com/docker/cli/cli-plugins/manager"
|
"github.com/docker/cli/cli-plugins/manager"
|
||||||
"github.com/docker/compose/v2/pkg/progress"
|
"github.com/docker/compose/v2/pkg/progress"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"go.opentelemetry.io/otel"
|
|
||||||
"go.opentelemetry.io/otel/propagation"
|
|
||||||
"golang.org/x/sync/errgroup"
|
"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")
|
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()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error checking available models: %w", err)
|
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 {
|
eg.Go(func() error {
|
||||||
w := progress.ContextWriter(gctx)
|
w := progress.ContextWriter(gctx)
|
||||||
if !slices.Contains(availableModels, config.Model) {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = s.configureModel(gctx, dockerModel, config, w)
|
return s.configureModel(gctx, dockerModel, project, config, w)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.Event(progress.CreatedEvent(config.Name))
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return eg.Wait()
|
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{
|
w.Event(progress.Event{
|
||||||
ID: model.Name,
|
ID: model.Name,
|
||||||
Status: progress.Working,
|
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)
|
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()
|
stream, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -150,7 +148,7 @@ func (s *composeService) pullModel(ctx context.Context, dockerModel *manager.Plu
|
|||||||
return err
|
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{
|
w.Event(progress.Event{
|
||||||
ID: config.Name,
|
ID: config.Name,
|
||||||
Status: progress.Working,
|
Status: progress.Working,
|
||||||
@ -167,13 +165,20 @@ func (s *composeService) configureModel(ctx context.Context, dockerModel *manage
|
|||||||
args = append(args, config.RuntimeFlags...)
|
args = append(args, config.RuntimeFlags...)
|
||||||
}
|
}
|
||||||
cmd := exec.CommandContext(ctx, dockerModel.Path, args...)
|
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()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) setModelVariables(ctx context.Context, dockerModel *manager.Plugin, project *types.Project) error {
|
func (s *composeService) setModelVariables(ctx context.Context, dockerModel *manager.Plugin, project *types.Project) error {
|
||||||
cmd := exec.CommandContext(ctx, dockerModel.Path, "status", "--json")
|
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()
|
statusOut, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error checking docker-model status: %w", err)
|
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
|
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 {
|
type Model struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Tags []string `json:"tags"`
|
Tags []string `json:"tags"`
|
||||||
|
@ -29,16 +29,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/compose-spec/compose-go/v2/types"
|
||||||
|
"github.com/docker/cli/cli-plugins/manager"
|
||||||
|
"github.com/docker/cli/cli/config"
|
||||||
"github.com/docker/compose/v2/pkg/progress"
|
"github.com/docker/compose/v2/pkg/progress"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type JsonMessage struct {
|
type JsonMessage struct {
|
||||||
@ -199,29 +195,11 @@ func (s *composeService) setupPluginCommand(ctx context.Context, project *types.
|
|||||||
args = append(args, service.Name)
|
args = append(args, service.Name)
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, path, args...)
|
cmd := exec.CommandContext(ctx, path, args...)
|
||||||
// exec provider command with same environment Compose is running
|
|
||||||
env := types.NewMapping(os.Environ())
|
err := s.prepareShellOut(ctx, project, cmd)
|
||||||
// but remove DOCKER_CLI_PLUGIN... variable so plugin can detect it run standalone
|
if err != nil {
|
||||||
delete(env, manager.ReexecEnvvar)
|
return nil, err
|
||||||
// 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())
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
return cmd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
61
pkg/compose/shellout.go
Normal file
61
pkg/compose/shellout.go
Normal file
@ -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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user