1
0
mirror of https://github.com/docker/compose.git synced 2025-04-08 17:05:13 +02:00

DRAFT external services plugin support

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2025-03-26 17:07:32 +01:00 committed by Guillaume Lours
parent b543380708
commit e67348222f
6 changed files with 117 additions and 4 deletions

2
go.mod

@ -8,7 +8,7 @@ require (
github.com/Microsoft/go-winio v0.6.2
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/buger/goterm v1.0.4
github.com/compose-spec/compose-go/v2 v2.4.10-0.20250319114556-312596f4c1fe
github.com/compose-spec/compose-go/v2 v2.4.10-0.20250327151131-f48efd965e24
github.com/containerd/containerd/v2 v2.0.4
github.com/containerd/platforms v1.0.0-rc.1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc

4
go.sum

@ -83,8 +83,8 @@ github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
github.com/compose-spec/compose-go/v2 v2.4.10-0.20250319114556-312596f4c1fe h1:gl5+6pDRe/b8tbqJOXvNOZWNQe4aFLymlMV0iqFp9GI=
github.com/compose-spec/compose-go/v2 v2.4.10-0.20250319114556-312596f4c1fe/go.mod h1:6k5l/0TxCg0/2uLEhRVEsoBWBprS2uvZi32J7xub3lo=
github.com/compose-spec/compose-go/v2 v2.4.10-0.20250327151131-f48efd965e24 h1:dIo4KMeWqnbC/hcWm0kHf+AzEcgUdUpjO+1LEoEdhiI=
github.com/compose-spec/compose-go/v2 v2.4.10-0.20250327151131-f48efd965e24/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA=
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=

@ -263,7 +263,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
func (s *composeService) ensureImagesExists(ctx context.Context, project *types.Project, buildOpts *api.BuildOptions, quietPull bool) error {
for name, service := range project.Services {
if service.Image == "" && service.Build == nil {
if service.External == nil && service.Image == "" && service.Build == nil {
return fmt.Errorf("invalid service %q. Must specify either image or build", name)
}
}

@ -110,6 +110,9 @@ func (c *convergence) apply(ctx context.Context, project *types.Project, options
}
func (c *convergence) ensureService(ctx context.Context, project *types.Project, service types.ServiceConfig, recreate string, inherit bool, timeout *time.Duration) error { //nolint:gocyclo
if service.External != nil {
return c.service.runPlugin(ctx, project, service, "create")
}
expected, err := getScale(service)
if err != nil {
return err

107
pkg/compose/plugins.go Normal file

@ -0,0 +1,107 @@
/*
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 (
"bufio"
"context"
"errors"
"fmt"
"os"
"os/exec"
"strings"
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli-plugins/manager"
"github.com/docker/cli/cli-plugins/socket"
"github.com/spf13/cobra"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
"golang.org/x/sync/errgroup"
)
func (s *composeService) runPlugin(ctx context.Context, project *types.Project, service types.ServiceConfig, command string) error {
x := *service.External
if x.Type != "model" {
return fmt.Errorf("unsupported external service type %s", x.Type)
}
plugin, err := manager.GetPlugin(x.Type, s.dockerCli, &cobra.Command{})
if err != nil {
return err
}
model, ok := x.Options["model"]
if !ok {
return errors.New("model option is required")
}
args := []string{"pull", model}
cmd := exec.CommandContext(ctx, plugin.Path, args...)
// Remove DOCKER_CLI_PLUGIN... variable so plugin can detect it run standalone
cmd.Env = filter(os.Environ(), 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.Cancel = server.Close
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()...)
var variables []string
eg := errgroup.Group{}
out, err := cmd.StdoutPipe()
if err != nil {
return err
}
cmd.Stderr = os.Stderr
err = cmd.Start()
if err != nil {
return err
}
eg.Go(cmd.Wait)
scanner := bufio.NewScanner(out)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
line := scanner.Text()
variables = append(variables, line)
}
err = eg.Wait()
if err != nil {
return err
}
variable := fmt.Sprintf("%s_URL", strings.ToUpper(service.Name))
// FIXME can we obtain this URL from Docker Destktop API ?
url := "http://host.docker.internal:12434/engines/llama.cpp/v1/"
for name, s := range project.Services {
if _, ok := s.DependsOn[service.Name]; ok {
s.Environment[variable] = &url
project.Services[name] = s
}
}
return nil
}

@ -335,6 +335,9 @@ func (s *composeService) pullRequiredImages(ctx context.Context, project *types.
}
func mustPull(service types.ServiceConfig, images map[string]api.ImageSummary) (bool, error) {
if service.External != nil {
return false, nil
}
if service.Image == "" {
return false, nil
}