Browse Source

add (temporary) support for use_api_socket

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 6 months ago
parent
commit
9a5fa05ad6
3 changed files with 104 additions and 1 deletions
  1. 90 0
      pkg/compose/apiSocket.go
  2. 7 0
      pkg/compose/create.go
  3. 7 1
      pkg/compose/run.go

+ 90 - 0
pkg/compose/apiSocket.go

@@ -0,0 +1,90 @@
+/*
+   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 (
+	"bytes"
+	"fmt"
+	"strings"
+
+	"github.com/compose-spec/compose-go/v2/types"
+	"github.com/docker/cli/cli/config/configfile"
+)
+
+// --use-api-socket is not actually supported by the Docker Engine
+// but is a client-side hack (see https://github.com/docker/cli/blob/master/cli/command/container/create.go#L246)
+// we replicate here by transforming the project model
+
+func (s *composeService) useAPISocket(project *types.Project) (*types.Project, error) {
+	useAPISocket := false
+	for _, service := range project.Services {
+		if service.UseAPISocket {
+			useAPISocket = true
+			break
+		}
+	}
+	if !useAPISocket {
+		return project, nil
+	}
+
+	socket := s.dockerCli.DockerEndpoint().Host
+	if !strings.HasPrefix(socket, "unix://") {
+		return nil, fmt.Errorf("use_api_socket can only be used with unix sockets: docker endpoint %s is incompatible", socket)
+	}
+	socket = strings.TrimPrefix(socket, "unix://") // should we confirm absolute path?
+
+	creds, err := s.dockerCli.ConfigFile().GetAllCredentials()
+	if err != nil {
+		return nil, fmt.Errorf("resolving credentials failed: %w", err)
+	}
+	newConfig := &configfile.ConfigFile{
+		AuthConfigs: creds,
+	}
+	var configBuf bytes.Buffer
+	if err := newConfig.SaveToWriter(&configBuf); err != nil {
+		return nil, fmt.Errorf("saving creds for API socket: %w", err)
+	}
+
+	project.Configs["#apisocket"] = types.ConfigObjConfig{
+		Content: configBuf.String(),
+	}
+
+	for name, service := range project.Services {
+		service.Volumes = append(service.Volumes, types.ServiceVolumeConfig{
+			Type:   types.VolumeTypeBind,
+			Source: socket,
+			Target: "/var/run/docker.sock",
+		})
+
+		_, envvarPresent := service.Environment["DOCKER_CONFIG"]
+
+		// If the DOCKER_CONFIG env var is already present, we assume the client knows
+		// what they're doing and don't inject the creds.
+		if !envvarPresent {
+			// Set our special little location for the config file.
+			path := "/run/secrets/docker"
+			service.Environment["DOCKER_CONFIG"] = &path
+		}
+
+		service.Configs = append(service.Configs, types.ServiceConfigObjConfig{
+			Source: "#apisocket",
+			Target: "/run/secrets/docker/config.json",
+		})
+		project.Services[name] = service
+	}
+	return project, nil
+}

+ 7 - 0
pkg/compose/create.go

@@ -114,6 +114,13 @@ func (s *composeService) create(ctx context.Context, project *types.Project, opt
 				"--remove-orphans flag to clean it up.", orphans.names())
 		}
 	}
+
+	// Temporary implementation of use_api_socket until we get actual support inside docker engine
+	project, err = s.useAPISocket(project)
+	if err != nil {
+		return err
+	}
+
 	return newConvergence(options.Services, observedState, networks, volumes, s).apply(ctx, project, options)
 }
 

+ 7 - 1
pkg/compose/run.go

@@ -59,6 +59,12 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.
 }
 
 func (s *composeService) prepareRun(ctx context.Context, project *types.Project, opts api.RunOptions) (string, error) {
+	// Temporary implementation of use_api_socket until we get actual support inside docker engine
+	project, err := s.useAPISocket(project)
+	if err != nil {
+		return "", err
+	}
+
 	err = progress.Run(ctx, func(ctx context.Context) error {
 		return s.startDependencies(ctx, project, opts)
 	}, s.stdinfo())
@@ -171,7 +177,7 @@ func applyRunOptions(project *types.Project, service *types.ServiceConfig, opts
 
 func (s *composeService) startDependencies(ctx context.Context, project *types.Project, options api.RunOptions) error {
 	var dependencies []string
-	for name, _ := range project.Services {
+	for name := range project.Services {
 		if name != options.Service {
 			dependencies = append(dependencies, name)
 		}