Browse Source

DRAFT external services plugin support

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 8 months ago
parent
commit
e67348222f
6 changed files with 117 additions and 4 deletions
  1. 1 1
      go.mod
  2. 2 2
      go.sum
  3. 1 1
      pkg/compose/build.go
  4. 3 0
      pkg/compose/convergence.go
  5. 107 0
      pkg/compose/plugins.go
  6. 3 0
      pkg/compose/pull.go

+ 1 - 1
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

+ 2 - 2
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=

+ 1 - 1
pkg/compose/build.go

@@ -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)
 		}
 	}

+ 3 - 0
pkg/compose/convergence.go

@@ -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 - 0
pkg/compose/plugins.go

@@ -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
+}

+ 3 - 0
pkg/compose/pull.go

@@ -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
 	}