github.com/docker/app@v0.9.1-beta3.0.20210611140623-a48f773ab002/internal/cnab/driver.go (about)

     1  package cnab
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"io"
     7  	"os"
     8  	"strings"
     9  
    10  	"github.com/pkg/errors"
    11  
    12  	"github.com/docker/app/internal/cliopts"
    13  	"github.com/docker/app/internal/store"
    14  
    15  	"github.com/deislabs/cnab-go/claim"
    16  	"github.com/deislabs/cnab-go/driver"
    17  	dockerDriver "github.com/deislabs/cnab-go/driver/docker"
    18  	"github.com/docker/app/internal"
    19  	"github.com/docker/cli/cli/command"
    20  	"github.com/docker/cli/cli/context/docker"
    21  	cliContext "github.com/docker/cli/cli/context/store"
    22  	"github.com/docker/docker/api/types/container"
    23  	"github.com/docker/docker/api/types/mount"
    24  )
    25  
    26  // BindMount
    27  type BindMount struct {
    28  	required bool
    29  	endpoint string
    30  }
    31  
    32  const defaultSocketPath string = "/var/run/docker.sock"
    33  
    34  func RequiredClaimBindMount(c claim.Claim, dockerCli command.Cli) (BindMount, error) {
    35  	var specifiedOrchestrator string
    36  	if rawOrchestrator, ok := c.Parameters[internal.ParameterOrchestratorName]; ok {
    37  		specifiedOrchestrator = rawOrchestrator.(string)
    38  	}
    39  
    40  	return RequiredBindMount(dockerCli.CurrentContext(), specifiedOrchestrator, dockerCli.ContextStore())
    41  }
    42  
    43  // RequiredBindMount Returns the path required to bind mount when running
    44  // the invocation image.
    45  func RequiredBindMount(targetContextName string, targetOrchestrator string, s cliContext.Store) (BindMount, error) {
    46  	if targetOrchestrator == "kubernetes" {
    47  		return BindMount{}, nil
    48  	}
    49  
    50  	if targetContextName == "" {
    51  		targetContextName = "default"
    52  	}
    53  
    54  	// in case of docker desktop, we want to rewrite the context in cases where it targets the local swarm or Kubernetes
    55  	s = &internal.DockerDesktopAwareStore{Store: s}
    56  
    57  	ctxMeta, err := s.GetMetadata(targetContextName)
    58  	if err != nil {
    59  		return BindMount{}, err
    60  	}
    61  	dockerCtx, err := command.GetDockerContext(ctxMeta)
    62  	if err != nil {
    63  		return BindMount{}, err
    64  	}
    65  	if dockerCtx.StackOrchestrator == command.OrchestratorKubernetes {
    66  		return BindMount{}, nil
    67  	}
    68  	dockerEndpoint, err := docker.EndpointFromContext(ctxMeta)
    69  	if err != nil {
    70  		return BindMount{}, err
    71  	}
    72  
    73  	host := dockerEndpoint.Host
    74  	return BindMount{isDockerHostLocal(host), socketPath(host)}, nil
    75  }
    76  
    77  func socketPath(host string) string {
    78  	if strings.HasPrefix(host, "unix://") {
    79  		return strings.TrimPrefix(host, "unix://")
    80  	}
    81  
    82  	return defaultSocketPath
    83  }
    84  
    85  func isDockerHostLocal(host string) bool {
    86  	return host == "" || strings.HasPrefix(host, "unix://") || strings.HasPrefix(host, "npipe://")
    87  }
    88  
    89  // prepareDriver prepares a driver per the user's request.
    90  func prepareDriver(dockerCli command.Cli, bindMount BindMount, stdout io.Writer) (driver.Driver, *bytes.Buffer) {
    91  	d := &dockerDriver.Driver{}
    92  	errBuf := bytes.NewBuffer(nil)
    93  	d.SetDockerCli(dockerCli)
    94  	if stdout != nil {
    95  		d.SetContainerOut(stdout)
    96  	}
    97  	d.SetContainerErr(errBuf)
    98  	if bindMount.required {
    99  		d.AddConfigurationOptions(func(config *container.Config, hostConfig *container.HostConfig) error {
   100  			config.User = "0:0"
   101  			mounts := []mount.Mount{
   102  				{
   103  					Type:   mount.TypeBind,
   104  					Source: bindMount.endpoint,
   105  					Target: bindMount.endpoint,
   106  				},
   107  			}
   108  			hostConfig.Mounts = mounts
   109  			return nil
   110  		})
   111  	}
   112  
   113  	// Load any driver-specific config out of the environment.
   114  	driverCfg := map[string]string{}
   115  	for env := range d.Config() {
   116  		if value, ok := os.LookupEnv(env); ok {
   117  			driverCfg[env] = value
   118  		}
   119  	}
   120  	d.SetConfig(driverCfg)
   121  
   122  	return d, errBuf
   123  }
   124  
   125  func SetupDriver(installation *store.Installation, dockerCli command.Cli, opts *cliopts.InstallerContextOptions, stdout io.Writer) (driver.Driver, *bytes.Buffer, error) {
   126  	dockerCli, err := opts.SetInstallerContext(dockerCli)
   127  	if err != nil {
   128  		return nil, nil, err
   129  	}
   130  	bind, err := RequiredClaimBindMount(installation.Claim, dockerCli)
   131  	if err != nil {
   132  		return nil, nil, err
   133  	}
   134  	driverImpl, errBuf := prepareDriver(dockerCli, bind, stdout)
   135  	return driverImpl, errBuf, nil
   136  }
   137  
   138  func WithRelocationMap(installation *store.Installation) func(op *driver.Operation) error {
   139  	return func(op *driver.Operation) error {
   140  		if err := addRelocationMapToFiles(op, installation); err != nil {
   141  			return err
   142  		}
   143  		relocateInvocationImage(op, installation)
   144  		return nil
   145  	}
   146  }
   147  
   148  func addRelocationMapToFiles(op *driver.Operation, installation *store.Installation) error {
   149  	data, err := json.Marshal(installation.RelocationMap)
   150  	if err != nil {
   151  		return errors.Wrap(err, "could not marshal relocation map")
   152  	}
   153  	op.Files["/cnab/app/relocation-mapping.json"] = string(data)
   154  
   155  	return nil
   156  }
   157  
   158  func relocateInvocationImage(op *driver.Operation, installation *store.Installation) {
   159  	invocImage := op.Image
   160  	if relocatedImage, ok := installation.RelocationMap[invocImage.Image]; ok {
   161  		invocImage.Image = relocatedImage
   162  		op.Image = invocImage
   163  	}
   164  }