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 }