Skip to content
Draft
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
49 changes: 27 additions & 22 deletions .github/workflows/pr-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,8 @@ jobs:
install-kind: false
requires-secret: false
install-microsandbox: true
test-timeout: 1200s
job-timeout-minutes: 25

# Snapshot tests

Expand Down Expand Up @@ -943,35 +945,36 @@ jobs:
--crun-path /usr/local/bin/crun \
--bootstrap-timeout 270s

- name: get microsandbox latest version
- name: cache microsandbox release bundle (Linux)
if: matrix.install-microsandbox == true && runner.os == 'Linux'
id: msb-version
run: |
version=$(curl -fsSL https://api.github.com/repos/superradcompany/microsandbox/releases/latest | grep '"tag_name"' | head -1 | sed 's/.*"tag_name":[[:space:]]*"\([^"]*\)".*/\1/')
echo "version=$version" >> "$GITHUB_OUTPUT"

- name: cache microsandbox (Linux)
if: matrix.install-microsandbox == true && runner.os == 'Linux'
id: msb-cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.microsandbox
~/.local/bin/msb
~/.local/bin/microsandbox
key: ${{ runner.os }}-${{ runner.arch }}-microsandbox-${{ steps.msb-version.outputs.version }}

- name: Install microsandbox (Linux)
if: matrix.install-microsandbox == true && runner.os == 'Linux' && steps.msb-cache.outputs.cache-hit != 'true'
run: |
curl -fsSL https://install.microsandbox.dev | sh
path: ${{ runner.temp }}/microsandbox-linux-x86_64-v0.7.7.tar.gz
key: ${{ runner.os }}-${{ runner.arch }}-microsandbox-bundle-v0.7.7

- name: configure microsandbox (Linux)
- name: install checksum-pinned microsandbox (Linux)
if: matrix.install-microsandbox == true && runner.os == 'Linux'
timeout-minutes: 5
run: |
archive="${RUNNER_TEMP}/microsandbox-linux-x86_64-v0.7.7.tar.gz"
if [ ! -f "$archive" ]; then
curl -fsSL --retry 3 -o "$archive" \
https://github.com/superradcompany/microsandbox/releases/download/v0.7.7/microsandbox-linux-x86_64.tar.gz
fi
echo "b3cc4a5e3f52dfdd938a6f67ac4a9a959ddfe304bab56de4964044b8613f01bb $archive" | sha256sum -c -
staging="$(mktemp -d)"
trap 'rm -rf "$staging"' EXIT
tar -xzf "$archive" -C "$staging"
mkdir -p "$HOME/.microsandbox/bin" "$HOME/.microsandbox/lib" "$HOME/.local/bin"
install -m 755 "$staging/msb" "$HOME/.microsandbox/bin/msb"
install -m 644 "$staging/libkrunfw.so.5.6.1" "$HOME/.microsandbox/lib/"
ln -sf libkrunfw.so.5.6.1 "$HOME/.microsandbox/lib/libkrunfw.so.5"
ln -sf libkrunfw.so.5 "$HOME/.microsandbox/lib/libkrunfw.so"
ln -sf "$HOME/.microsandbox/bin/msb" "$HOME/.local/bin/msb"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
# The e2e test skips gracefully if KVM is unavailable on the runner.
"$HOME/.local/bin/msb" doctor || true
"$HOME/.local/bin/msb" --version
sudo test -r /dev/kvm
sudo test -w /dev/kvm

- name: remove docker
if: matrix.label == 'docker-install' && (matrix.requires-secret == false || needs.can-read-secret.outputs.secret-set == 'true')
Expand Down Expand Up @@ -1011,6 +1014,7 @@ jobs:
GH_CREDENTIAL_USERNAME: x-access-token
TEST_TIMEOUT: ${{ matrix.test-timeout || '1500s' }}
FLAKE_ATTEMPTS: ${{ matrix.flake-attempts || '1' }}
DEVSY_REQUIRE_MICROSANDBOX: ${{ matrix.install-microsandbox == true && 'true' || 'false' }}
run: |
if [ "${{ runner.os }}" == "Linux" ]; then
if [ "${{ matrix.install-podman || '' }}" = "rootless" ]; then
Expand All @@ -1027,6 +1031,7 @@ jobs:
./e2e.test -test.v -ginkgo.v -test.timeout "${TEST_TIMEOUT}" -ginkgo.fail-on-empty -ginkgo.flake-attempts="${FLAKE_ATTEMPTS}" --ginkgo.label-filter="${{ matrix.label }}"
else
sudo \
DEVSY_REQUIRE_MICROSANDBOX="${DEVSY_REQUIRE_MICROSANDBOX}" \
GH_USERNAME="${GH_USERNAME}" \
GH_ACCESS_TOKEN="${GH_ACCESS_TOKEN}" \
GH_CREDENTIAL_USERNAME="${GH_CREDENTIAL_USERNAME}" \
Expand Down
2 changes: 1 addition & 1 deletion THIRD_PARTY_LICENSES.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ To regenerate this file after changing dependencies, run `task cli:licenses`.
| [github.com/devsy-org/agentapi](https://github.com/devsy-org/agentapi) | `v1.0.1` | MPL-2.0 |
| [github.com/devsy-org/api](https://github.com/devsy-org/api) | `v1.1.0` | MPL-2.0 |
| [github.com/devsy-org/apiserver](https://github.com/devsy-org/apiserver) | `v1.5.4` | Apache-2.0 |
| [github.com/devsy-org/devsy-runtime-sdk](https://github.com/devsy-org/devsy-runtime-sdk) | `v1.4.0` | MPL-2.0 |
| [github.com/devsy-org/devsy-runtime-sdk](https://github.com/devsy-org/devsy-runtime-sdk) | `v1.5.2` | MPL-2.0 |
| [github.com/devsy-org/ssh](https://github.com/devsy-org/ssh) | `v1.2.9` | BSD-3-Clause |
| [github.com/distribution/reference](https://github.com/distribution/reference) | `v0.6.0` | Apache-2.0 |
| [github.com/docker/cli](https://github.com/docker/cli) | `v29.8.0+incompatible` | Apache-2.0 |
Expand Down
76 changes: 76 additions & 0 deletions cmd/internal/agentworkspace/binaries_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package agentworkspace

import (
"context"
"crypto/sha256"
"encoding/hex"
"io/fs"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"runtime"
"testing"

"github.com/devsy-org/devsy/pkg/config"
"github.com/devsy-org/devsy/pkg/provider"
"github.com/stretchr/testify/require"
)

func TestExistingContentPreparesAgentBinaries(t *testing.T) {
for _, tc := range []struct {
name string
validChecksum bool
}{{"success", true}, {"checksum failure", false}} {
t.Run(tc.name, func(t *testing.T) {
t.Setenv(config.EnvHome, t.TempDir())
payload := []byte("workspace-runtime-fixture")
server := httptest.NewServer(
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write(payload)
}),
)
t.Cleanup(server.Close)
info := existingContentRuntime(t, server.URL, payload)
marker := filepath.Join(info.ContentFolder, "user-data")
require.NoError(t, os.WriteFile(marker, []byte("preserved"), 0o600))
if !tc.validChecksum {
info.Agent.Binaries["RUNTIME"][0].Checksum = hex.EncodeToString(
make([]byte, sha256.Size),
)
}
exists, err := InitContentFolder(context.Background(), info)
require.True(t, exists)
if tc.validChecksum {
require.NoError(t, err)
data, err := fs.ReadFile(os.DirFS(info.Origin), "binaries/runtime/runtime-fixture")
require.NoError(t, err)
require.Equal(t, payload, data)
} else {
require.ErrorContains(t, err, "checksum")
}
data, err := fs.ReadFile(os.DirFS(info.ContentFolder), "user-data")
require.NoError(t, err)
require.Equal(t, "preserved", string(data))
})
}
}

func existingContentRuntime(t *testing.T, url string, payload []byte) *provider.AgentWorkspaceInfo {
t.Helper()
home := t.TempDir()
origin := filepath.Join(home, "contexts", config.DefaultContext, "workspaces", "binary-test")
require.NoError(t, os.MkdirAll(origin, 0o750))
sum := sha256.Sum256(payload)
return &provider.AgentWorkspaceInfo{
Origin: origin, ContentFolder: t.TempDir(),
Workspace: &provider.Workspace{Context: config.DefaultContext, ID: "binary-test"},
Agent: provider.ProviderAgentConfig{
DataPath: home,
Binaries: map[string][]*provider.ProviderBinary{"RUNTIME": {{
OS: runtime.GOOS, Arch: runtime.GOARCH, Path: url, Name: "runtime-fixture",
Checksum: hex.EncodeToString(sum[:]),
}}},
},
}
}
5 changes: 4 additions & 1 deletion cmd/internal/agentworkspace/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ func InitContentFolder(
return false, err
}
if exists {
return true, nil
return true, downloadWorkspaceBinaries(ctx, workspaceInfo)
}

if err := createContentFolder(workspaceInfo.ContentFolder); err != nil {
Expand Down Expand Up @@ -288,6 +288,9 @@ func downloadWorkspaceBinaries(
ctx context.Context,
workspaceInfo *provider.AgentWorkspaceInfo,
) error {
if len(workspaceInfo.Agent.Binaries) == 0 {
return nil
}
binariesDir, err := agent.GetAgentBinariesDir(
workspaceInfo.Agent.DataPath,
workspaceInfo.Workspace.Context,
Expand Down
11 changes: 6 additions & 5 deletions cmd/internal/container_tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,12 @@ func (cmd *ContainerTunnelCmd) Run(cobraCtx context.Context) error {
return agent.Tunnel(ctx, agent.TunnelOptions{
Exec: func(ctx context.Context, req agent.ExecRequest) error {
return runner.Command(ctx, devcontainer.CommandParams{
User: req.User,
Command: req.Command,
Stdin: req.Stdin,
Stdout: req.Stdout,
Stderr: req.Stderr,
User: req.User,
Command: req.Command,
Stdin: req.Stdin,
Stdout: req.Stdout,
Stderr: req.Stderr,
RawStdout: true,
})
},
RuntimeHealthCheck: containerRuntimeHealthCheck(workspaceInfo),
Expand Down
7 changes: 4 additions & 3 deletions cmd/workspace/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ func (cmd *SSHCmd) runInteractiveTunnelSession(
TermMode: cmd.TermMode,
InstallTerminfo: cmd.InstallTerminfo,
},
Exec: func(ctx context.Context, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
Exec: func(ctx context.Context, stdin io.Reader, stdout io.Writer, _ io.Writer) error {
if cmd.SSHKeepAliveInterval != DisableSSHKeepAlive {
go startSSHKeepAlive(ctx, params.containerClient, cmd.SSHKeepAliveInterval)
}
Expand All @@ -551,11 +551,12 @@ func (cmd *SSHCmd) runInteractiveTunnelSession(
Command: params.command,
Stdin: stdin,
Stdout: stdout,
Stderr: stderr,
Stderr: params.writer,
EnvVars: params.envVars,
})
},
Stderr: params.writer,
// Guest stderr is user data; only the outer helper emits JSON diagnostics.
Stderr: os.Stderr,
})
}

Expand Down
Loading
Loading