Skip to content

Commit

Permalink
batches: fix Docker Desktop for Linux handling (#813)
Browse files Browse the repository at this point in the history
If the default temporary directory is in use _and_ Docker Desktop for
Linux is the default Docker context, this changes the temporary
directory to be one under `~/.cache/sourcegraph/batch-tmp` instead.

Fixes #754.
  • Loading branch information
LawnGnome authored Aug 10, 2022
1 parent da6bd59 commit b86c397
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ All notable changes to `src-cli` are documented in this file.

### Fixed

- The default directory used to mount files into containers will be automatically changed to a temporary directory within `$HOME` if Docker Desktop for Linux is in use. [#754](https://github.com/sourcegraph/src-cli/issues/754)

### Removed

## 3.42.3
Expand Down
22 changes: 22 additions & 0 deletions cmd/src/batch_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,28 @@ func executeBatchSpec(ctx context.Context, ui ui.ExecUI, opts executeBatchSpecOp
return err
}

// On Linux only, we also need to figure out if we need to override the
// temporary directory — Docker Desktop restricts file mounts to /home only
// by default.
//
// We could interrogate ~/.docker/desktop/settings.json for extra bonus
// points here, but that feels like overkill. Basically, if it's
// desktop-linux, we'll just assume the user has the default /home mount
// available and go from there.
if runtime.GOOS == "linux" && opts.flags.tempDir == batchDefaultTempDirPrefix() {
context, err := docker.CurrentContext(ctx)
if err != nil {
return err
}

if context == "desktop-linux" {
opts.flags.tempDir = path.Join(path.Dir(opts.flags.cacheDir), "batch-tmp")

// Ensure the directory exists and is writable.
os.MkdirAll(opts.flags.tempDir, os.ModePerm)
}
}

// Parse flags and build up our service and executor options.
ui.ParsingBatchSpec()
batchSpec, rawSpec, err := parseBatchSpec(ctx, opts.file, svc, false)
Expand Down
25 changes: 25 additions & 0 deletions internal/batches/docker/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,31 @@ import (
"github.com/sourcegraph/src-cli/internal/exec"
)

// CurrentContext returns the name of the current Docker context (not to be
// confused with a Go context).
func CurrentContext(ctx context.Context) (string, error) {
dctx, cancel, err := withFastCommandContext(ctx)
if err != nil {
return "", err
}
defer cancel()

args := []string{"context", "inspect", "--format", "{{ .Name }}"}
out, err := exec.CommandContext(dctx, "docker", args...).CombinedOutput()
if errors.IsDeadlineExceeded(err) || errors.IsDeadlineExceeded(dctx.Err()) {
return "", newFastCommandTimeoutError(dctx, args...)
} else if err != nil {
return "", err
}

name := string(bytes.TrimSpace(out))
if name == "" {
return "", errors.New("no context returned from Docker")
}

return name, nil
}

// NCPU returns the number of CPU cores available to Docker.
func NCPU(ctx context.Context) (int, error) {
dctx, cancel, err := withFastCommandContext(ctx)
Expand Down
56 changes: 56 additions & 0 deletions internal/batches/docker/info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,62 @@ import (
"github.com/sourcegraph/src-cli/internal/exec/expect"
)

func Test_CurrentContext(t *testing.T) {
ctx := context.Background()

t.Run("docker fails", func(t *testing.T) {
expect.Commands(t, contextInspectFailure())

name, err := CurrentContext(ctx)
assert.Empty(t, name)
assert.Error(t, err)
})

t.Run("docker times out", func(t *testing.T) {
tctx, cancel := context.WithTimeout(ctx, -1*time.Second)
t.Cleanup(cancel)

expect.Commands(t, contextInspectSuccess("desktop-linux"))

name, err := CurrentContext(tctx)
assert.Zero(t, name)
var terr *fastCommandTimeoutError
assert.ErrorAs(t, err, &terr)
assert.Equal(t, []string{"context", "inspect", "--format", "{{ .Name }}"}, terr.args)
assert.Equal(t, fastCommandTimeoutDefault, terr.timeout)
})

t.Run("docker succeeds, but returns nothing", func(t *testing.T) {
expect.Commands(t, contextInspectSuccess(""))

name, err := CurrentContext(ctx)
assert.Empty(t, name)
assert.Error(t, err)
})

t.Run("docker succeeds", func(t *testing.T) {
expect.Commands(t, contextInspectSuccess("desktop-linux"))

name, err := CurrentContext(ctx)
assert.Equal(t, "desktop-linux", name)
assert.NoError(t, err)
})
}

func contextInspectSuccess(ncpu string) *expect.Expectation {
return expect.NewLiteral(
expect.Behaviour{Stdout: []byte(fmt.Sprintf("%s\n", ncpu))},
"docker", "context", "inspect", "--format", "{{ .Name }}",
)
}

func contextInspectFailure() *expect.Expectation {
return expect.NewLiteral(
expect.Behaviour{ExitCode: 1},
"docker", "context", "inspect", "--format", "{{ .Name }}",
)
}

func Test_NCPU(t *testing.T) {
ctx := context.Background()

Expand Down

0 comments on commit b86c397

Please sign in to comment.