Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions THIRD_PARTY_LICENSES.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ To regenerate this file after changing dependencies, run `task cli:licenses`.
| [github.com/containerd/console](https://github.com/containerd/console) | `v1.0.5` | Apache-2.0 |
| [github.com/containerd/containerd/api](https://github.com/containerd/containerd) | `v1.10.0` | Apache-2.0 |
| [github.com/containerd/containerd/v2](https://github.com/containerd/containerd) | `v2.2.2` | Apache-2.0 |
| [github.com/containerd/continuity](https://github.com/containerd/continuity) | `v0.4.5` | Apache-2.0 |
| [github.com/containerd/continuity](https://github.com/containerd/continuity) | `v0.5.0` | Apache-2.0 |
| [github.com/containerd/errdefs](https://github.com/containerd/errdefs) | `v1.0.0` | Apache-2.0 |
| [github.com/containerd/errdefs/pkg](https://github.com/containerd/errdefs) | `v0.3.0` | Apache-2.0 |
| [github.com/containerd/log](https://github.com/containerd/log) | `v0.1.0` | Apache-2.0 |
Expand Down Expand Up @@ -243,7 +243,7 @@ To regenerate this file after changing dependencies, run `task cli:licenses`.
| [github.com/tidwall/pretty](https://github.com/tidwall/pretty) | `v1.2.1` | MIT |
| [github.com/tklauser/go-sysconf](https://github.com/tklauser/go-sysconf) | `v0.3.16` | BSD-3-Clause |
| [github.com/tklauser/numcpus](https://github.com/tklauser/numcpus) | `v0.11.0` | Apache-2.0 |
| [github.com/tonistiigi/fsutil](https://github.com/tonistiigi/fsutil) | `v0.0.0-20251211185533-a2aa163d723f` | MIT |
| [github.com/tonistiigi/fsutil](https://github.com/tonistiigi/fsutil) | `v0.0.0-20260609174605-b61e79c0c046` | MIT |
| [github.com/tonistiigi/go-csvvalue](https://github.com/tonistiigi/go-csvvalue) | `v0.0.0-20240814133006-030d3b2625d0` | MIT |
| [github.com/tonistiigi/units](https://github.com/tonistiigi/units) | `v0.0.0-20180711220420-6950e57a87ea` | MIT |
| [github.com/tonistiigi/vt100](https://github.com/tonistiigi/vt100) | `v0.0.0-20240514184818-90bafcd6abab` | MIT |
Expand Down
47 changes: 30 additions & 17 deletions cmd/internal/container_tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,13 @@ func (cmd *ContainerTunnelCmd) Run(ctx context.Context) error {
stdin io.Reader,
stdout, stderr io.Writer,
) error {
return runner.Command(ctx, user, command, stdin, stdout, stderr)
return runner.Command(ctx, devcontainer.CommandParams{
User: user,
Command: command,
Stdin: stdin,
Stdout: stdout,
Stderr: stderr,
})
},
User: cmd.User,
Stdin: os.Stdin,
Expand All @@ -113,29 +119,36 @@ func startDevContainer(
return err
}

// start container if necessary
// start container if it is missing or not running
if containerDetails == nil || containerDetails.State.Status != "running" {
// start container
_, err = StartContainer(ctx, runner, workspaceConfig)
if err != nil {
return err
}
} else if encoding.IsLegacyUID(workspaceConfig.Workspace.UID) {
// make sure workspace result is in devcontainer
buf := &bytes.Buffer{}
err = runner.Command(ctx, "root", "cat "+pkgconfig.DevContainerResultPath, nil, buf, buf)
if err != nil {
// start container
_, err = StartContainer(ctx, runner, workspaceConfig)
if err != nil {
return err
}
}
return err
}

// for legacy UIDs, ensure the workspace result is present in the container,
// restarting it when the result is missing
if encoding.IsLegacyUID(workspaceConfig.Workspace.UID) &&
!hasDevContainerResult(ctx, runner) {
_, err = StartContainer(ctx, runner, workspaceConfig)
return err
}

return nil
}

// hasDevContainerResult reports whether the devcontainer result file is readable
// inside the running container.
func hasDevContainerResult(ctx context.Context, runner devcontainer.Runner) bool {
buf := &bytes.Buffer{}
err := runner.Command(ctx, devcontainer.CommandParams{
User: "root",
Command: "cat " + pkgconfig.DevContainerResultPath,
Stdout: buf,
Stderr: buf,
})
return err == nil
}

func StartContainer(
ctx context.Context,
runner devcontainer.Runner,
Expand Down
2 changes: 2 additions & 0 deletions cmd/workspace/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ func NewBuildCmd(flags *flags.GlobalFlags) *cobra.Command {
StringVar(&cmd.ImageName, "image-name", "", "Alternative name for the built image")
buildCmd.Flags().
BoolVar(&cmd.NoBuild, "no-build", false, "Fail if the image must be built (enforce pre-built images only)")
buildCmd.Flags().
BoolVar(&cmd.Pull, "pull", false, "Always attempt to pull a newer version of the base image when building")

// TESTING
buildCmd.Flags().BoolVar(&cmd.ForceBuild, "force-build", false, "TESTING ONLY")
Expand Down
6 changes: 6 additions & 0 deletions cmd/workspace/up/up_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,12 @@ func (cmd *UpCmd) registerWorkspaceFlags(upCmd *cobra.Command) {
upCmd.Flags().
BoolVar(&cmd.Prebuild, "prebuild", false,
"If true will only run the prebuild lifecycle (onCreateCommand + updateContentCommand) then stop")
upCmd.Flags().
BoolVar(&cmd.Pull, "pull", false,
"Always attempt to pull a newer version of the base image when building")
upCmd.Flags().
BoolVar(&cmd.NoCache, "no-cache", false,
"Do not use the build cache when building the image")
upCmd.Flags().
BoolVar(&cmd.Recreate, "recreate", false, "If true will remove any existing containers and recreate them")
upCmd.Flags().BoolVar(&cmd.Recreate, "remove-existing-container", false, "Alias for --recreate")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "Go",
"dockerComposeFile": "./docker-compose.yaml",
"service": "app",
"workspaceFolder": "/workspaces",
"features": {
"ghcr.io/devsy-org/devcontainer-features/vcluster:1.0.1": {
"version": "v0.24.1"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM ghcr.io/devsy-org/test-images/go:1 AS base

RUN echo "base-stage" > /stage-marker.txt

FROM base AS dev

RUN echo "dev-stage" > /stage-marker.txt
RUN mkdir -p /app
WORKDIR /app
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: '3'

services:
app:
build:
context: .
dockerfile: Dockerfile
target: dev
command: sleep infinity
volumes:
- .:/workspaces:cached
37 changes: 37 additions & 0 deletions e2e/tests/up-docker-compose/up_docker_compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,43 @@ var _ = ginkgo.Describe(
To(gomega.ContainSubstring("vcluster version 0.24.1"))
}, ginkgo.SpecTimeout(framework.TimeoutLong()))

// Regression guard: a build-backed service with an explicit build.target
// and features must honor the real Dockerfile. Previously the Dockerfile
// contents were dropped when a target was set, producing a synthesized
// "FROM <image> AS <target>" that ignored the requested stage (and broke
// build-only services that have no top-level image).
ginkgo.It("features with build target", func(ctx context.Context) {
tempDir, workspace, err := tc.setupAndStartWorkspace(
ctx,
"tests/up-docker-compose/testdata/docker-compose-build-target-features",
"--debug",
)
framework.ExpectNoError(err)

ids, err := findComposeContainer(
ctx,
tc.dockerHelper,
tc.composeHelper,
workspace.UID,
"app",
)
framework.ExpectNoError(err)
gomega.Expect(ids).To(gomega.HaveLen(1), "1 compose container to be created")

// The "dev" stage overwrites the marker, so seeing "dev-stage"
// proves the real multi-stage Dockerfile (and its target) was built.
stageMarker, err := tc.execSSH(ctx, tempDir, "cat /stage-marker.txt")
framework.ExpectNoError(err)
gomega.Expect(strings.TrimSpace(stageMarker)).
To(gomega.Equal("dev-stage"), "the requested build target stage should be used")

// The feature must still be installed on top of the targeted stage.
vclusterVersionOutput, err := tc.execSSH(ctx, tempDir, "vcluster --version")
framework.ExpectNoError(err)
gomega.Expect(vclusterVersionOutput).
To(gomega.ContainSubstring("vcluster version 0.24.1"))
}, ginkgo.SpecTimeout(framework.TimeoutLong()))

ginkgo.It(
"does not retag shared image when applying features to image backed services",
func(ctx context.Context) {
Expand Down
Loading
Loading