Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
2edaf05
chore(deps): Bump Go to 1.27.3
jedevc Oct 26, 2026
8c1b9db
feat(cmd): Add unikraft build --remote
dragosgheorghioiu Oct 26, 2026
5743221
feat(cmd): Add unikraft build --remote (#581)
craciunoiuc Oct 26, 2026
0337d71
feat(cmd): Add unikraft buildkit commands
dragosgheorghioiu Oct 27, 2026
8bf2424
feat(cmd): Add unikraft buildkit create (#582)
craciunoiuc Oct 27, 2026
4cf691f
gomod(deps): Bump x/shell and sdk/plugins/sandbox
dragosgheorghioiu Oct 27, 2026
6d7a17b
fix(cmd): Exit exec with the signal's status
dragosgheorghioiu Oct 27, 2026
8776b70
fix(cmd): Exit exec with the signal's status (#587)
nurof3n Oct 27, 2026
eb09b0c
test(cmd): Move the buildkit test to x/buildkit
dragosgheorghioiu Oct 29, 2026
ab412c9
fix(cmd): Keep exec's stderr apart from stdout
dragosgheorghioiu Oct 30, 2026
620e688
ci: Use the shared promote workflow
jedevc Oct 30, 2026
d67c333
test(integration): Inspect, copy and run http+oci tags
jedevc Oct 15, 2026
58caa6e
feat(cmd): Manage builders through the build API
dragosgheorghioiu Oct 30, 2026
8af68be
test(integration): Inspect, copy and run http+oci tags (#557)
jedevc Oct 30, 2026
86b3b75
feat(cmd): Manage builders through the build API (#594)
dragosgheorghioiu Oct 30, 2026
2f75a5f
ci: Use the shared promote workflow (#593)
jedevc Oct 30, 2026
3090e0f
chore(deps): Bump Go to 1.27.3 (#580)
jedevc Oct 30, 2026
86cfcd0
docs(cmd): Describe every resource field in help
dragosgheorghioiu Oct 7, 2026
12c6f32
fix(cmd): Keep exec's stderr apart from stdout (#591)
jedevc Nov 2, 2026
8685216
fix: Build and copy images without a profile
jedevc Nov 2, 2026
36808a2
feat(cmd): Link metros to the console
jedevc Nov 2, 2026
91f1b90
fix: Build and copy images without a profile (#597)
jedevc Nov 2, 2026
4fcaa5d
feat(cmd): Link metros to the console (#598)
jedevc Nov 3, 2026
35e9d18
chore: Drop the remote Taskfiles experiment
jedevc Nov 3, 2026
7700475
docs(cmd): Drop the vCPU limit note from active instances
jedevc Nov 3, 2026
4f05d00
chore: Drop the remote Taskfiles experiment (#599)
jedevc Nov 3, 2026
22bc455
docs(cmd): Describe every resource field in help (#534)
jedevc Nov 3, 2026
b57c922
refactor(model): Rename resources to models
dragosgheorghioiu Oct 23, 2026
0cada94
refactor(model): Rename resources to models (#583)
jedevc Nov 3, 2026
a8c13d2
test(cmd): Move the buildkit test to x/buildkit (#589)
dragosgheorghioiu Nov 4, 2026
28a2eb2
test(cmd): Move the build tests to x/build
dragosgheorghioiu Nov 4, 2026
04d7b9f
gomod(deps): Bump the Go SDK
craciunoiuc Nov 5, 2026
d403d4f
feat(model/value): Parse dotted keys
jedevc Aug 31, 2026
04e6921
feat(instances): Show network interfaces
jedevc Aug 31, 2026
cab404a
feat(instances): Configure NICs on create
jedevc Aug 31, 2026
583092e
test(integration): Cover instance relays
jedevc Aug 31, 2026
04eebde
feat(instances): Add network interfaces and relays (#481)
craciunoiuc Nov 5, 2026
8674dad
test(cmd): Move the build tests to x/build (#601)
craciunoiuc Nov 5, 2026
e6e7c6a
feat(config): Use the Linux config path on macOS
aabedraba Nov 6, 2026
c7e8417
feat(mcp): Add an MCP server command
dragosgheorghioiu Nov 5, 2026
cae0a85
feat: Add mcp server command (#554)
dragosgheorghioiu Nov 6, 2026
4db7ed4
feat(config): Use the Linux config path on macOS (#603)
aabedraba Nov 6, 2026
9b0c692
fix(cmd): Keep the instances of a partial create
craciunoiuc Oct 27, 2026
86a5310
refactor(model): Pass the create fields in
craciunoiuc Nov 3, 2026
f93d5ff
feat(cmd): Add '--rollout' when creating
craciunoiuc Sep 7, 2026
1fcb6b0
feat(cmd): Add a recreate mode to the rollout
craciunoiuc Nov 3, 2026
96eaf20
feat(cmd): Select a rollout by tags
craciunoiuc Nov 3, 2026
2883ec1
feat(cmd): Add a target to the rollout
craciunoiuc Oct 12, 2026
740cade
feat(cmd): Get, list and delete builders
dragosgheorghioiu Nov 3, 2026
42c810d
feat(cmd): Get, list and delete builders (#600)
craciunoiuc Nov 6, 2026
666718b
ci: Log in to Docker Hub in integration tests
dragosgheorghioiu Nov 6, 2026
b05446c
ci: Log in to Docker Hub in integration tests (#604)
craciunoiuc Nov 6, 2026
2152764
feat(cmd): Add rollout mechanics when creating (#309)
dragosgheorghioiu Nov 6, 2026
1a8fe82
gomod(deps): Bump go-archivefs
craciunoiuc Nov 6, 2026
bbd832b
gomod(deps): Bump the Go SDK
craciunoiuc Nov 6, 2026
fd43407
gomod(deps): Bump go-archivefs and the Go SDK (#605)
dragosgheorghioiu Nov 6, 2026
a159802
gomod(deps): Bump x/build and x/buildkit
craciunoiuc Nov 6, 2026
c5ddeba
gomod(deps): Bump x/build and x/buildkit (#607)
craciunoiuc Nov 6, 2026
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
6 changes: 2 additions & 4 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@ When reviewing a change, first determine whether it adds or changes user-facing
- New, removed, or changed positional arguments, flags, options, aliases, defaults, or validation.
- Changes to command behavior, output, exit status, authentication, configuration, resource workflows, or other documented CLI interactions.

For every user-facing change, check whether the change has appropriate integration-test coverage. Repository integration tests live in:
- `cmd/unikraft/integration/` — end-to-end CLI tests, organised as one file per command area, with shared fixtures in `helpers_test.go`.
- `internal/cmd/build_integration_test.go` — integration-level builder tests that exercise real builds through `unikraft.com/x/build` rather than the CLI surface.
For every user-facing change, check whether the change has appropriate integration-test coverage. Repository integration tests live in `cmd/unikraft/integration/`: end-to-end CLI tests, organised as one file per command area, with shared fixtures in `helpers_test.go`. The builder tests that exercise real builds through `unikraft.com/x/build` live in that module.

Both use the shared runner/helpers in the `internal/integration/` package; that package holds the harness itself, not test cases. All of them run through `task integration`, which targets `./cmd/unikraft/integration` and `./internal/cmd`.
They use the shared runner/helpers in the `internal/integration/` package; that package holds the harness itself, not test cases. They run through `task integration`, which targets `./cmd/unikraft/integration`.

If integration tests are missing or do not cover the changed user-facing behavior, leave one **medium-level** review comment. The comment must:

Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,6 @@ jobs:

- name: Build
shell: bash
env:
TASK_X_REMOTE_TASKFILES: 1
run: |
VERSION=pr-${{ github.event.number }} GOOS=${{ matrix.GOOS }} GOARCH=${{ matrix.GOARCH }} task --yes ${{ matrix.task || 'cli' }}

Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/check-static.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ permissions:
jobs:
go:
runs-on: ubuntu-latest
env:
TASK_X_REMOTE_TASKFILES: 1
steps:
- uses: actions/checkout@v7

Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/promote.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: promote

on:
push:
branches: [prod-staging]
workflow_dispatch:

permissions:
contents: read

jobs:
promote:
uses: unikraft-cloud/x/.github/workflows/promote.yaml@prod-staging
secrets:
gh_pat: ${{ secrets.GH_PAT }}
17 changes: 13 additions & 4 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
env:
TASK_X_REMOTE_TASKFILES: 1
defaults:
run:
shell: bash
Expand Down Expand Up @@ -86,7 +84,6 @@ jobs:
matrix:
target: [STAGING, STABLE]
env:
TASK_X_REMOTE_TASKFILES: 1
UNIKRAFT_SERVER_STAGING: staging
UNIKRAFT_SERVER_STABLE: stable
UNIKRAFT_SERVER_PROD: prod
Expand All @@ -109,6 +106,12 @@ jobs:
with:
platforms: arm64

- name: Login to Docker Hub
uses: unikraft-cloud/x/.github/actions/login-docker-hub@prod-staging
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Set Go variables
id: goenv
run: |
Expand Down Expand Up @@ -161,6 +164,7 @@ jobs:
UNIKRAFT_TOKEN: ${{ secrets.UNIKRAFT_TOKEN }}
UNIKRAFT_ENDPOINT: ${{ secrets[format('UNIKRAFT_ENDPOINT_{0}', matrix.target)] }}
UNIKRAFT_X_TEST_SERVER: ${{ env[format('UNIKRAFT_SERVER_{0}', matrix.target)] }}
UNIKRAFT_X_TEST_BUILD_URL: ${{ matrix.target == 'STABLE' && secrets.UNIKRAFT_BUILD_URL_STABLE || '' }}

# The hosted Windows runners reach only the public prod endpoint. The runners
# take turns, so that both do not use the same organization at once.
Expand All @@ -175,7 +179,6 @@ jobs:
runs-on: ${{ matrix.runner }}
needs: integration
env:
TASK_X_REMOTE_TASKFILES: 1
UNIKRAFT_SERVER_PROD: prod
defaults:
run:
Expand All @@ -200,6 +203,12 @@ jobs:
with:
platforms: arm64

- name: Login to Docker Hub
uses: unikraft-cloud/x/.github/actions/login-docker-hub@prod-staging
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Set Go variables
id: goenv
run: |
Expand Down
3 changes: 0 additions & 3 deletions .taskrc.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
experiments:
REMOTE_TASKFILES: 1

remote:
insecure: false
offline: false
Expand Down
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This repo contains the Go-based Unikraft CLI. Use this file for contributor-faci

## Development practices

- Use `task` for builds and workflows; enable Remote Taskfiles with `TASK_X_REMOTE_TASKFILES=1`.
- Use `task` for builds and workflows.
- Build the CLI with `task cli`; the binary is placed in `dist/`, and called `unikraft`.
- Build documentation with `task docs`; outputs include Markdown docs in `dist/docs/` and man pages in `dist/man/`.
- Quality gates: `task lint`, `task test`. Run these after making changes to ensure code quality.
Expand All @@ -17,10 +17,10 @@ This repo contains the Go-based Unikraft CLI. Use this file for contributor-faci
## Architecture overview

- Single CLI binary with a command tree that routes to internal services.
- Core `Resource` interface models API objects with `Type`, `Key`, `Fields`, and `Raw`, and is extended by get/list/create/edit/delete behaviors.
- Resource data is expressed as a tree of `Field` nodes: each node has a name, optional value, nested subfields, and an `Elem` template for homogeneous arrays; patch metadata and verbosity drive editing and output.
- Resource utilities live alongside the resource layer (inside `internal/`): struct↔field conversion, field cloning/merging/deduping, map/slice encoding, path-based filtering/selection, and patch helpers for edits.
- Core logic lives under `internal/` and is organized by domain: configuration/profile handling, API client communication, resource abstractions, output/logging utilities, multi-region support, and shared helpers.
- Core `Model` interface (`internal/model`) models API objects with `Type`, `Key`, `Fields`, and `Raw`, and is extended by get/list/create/edit/delete behaviors.
- Model data is expressed as a tree of `Field` nodes: each node has a name, optional value, nested subfields, and an `Elem` template for homogeneous arrays; patch metadata and verbosity drive editing and output.
- Model utilities live alongside the model layer (inside `internal/model`): struct↔field conversion, field cloning/merging/deduping, map/slice encoding, path-based filtering/selection, and patch helpers for edits.
- Core logic lives under `internal/` and is organized by domain: configuration/profile handling, API client communication, model abstractions, output/logging utilities, multi-region support, and shared helpers.
- Build artifacts and generated docs go to `dist/`; developer tooling/scripts live at the repo root and under `tools/`.

## Important dependencies
Expand Down
12 changes: 3 additions & 9 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,7 @@

- [Go](https://golang.org/dl/) 1.25+
- Git
- [Task](https://taskfile.dev/) (or GNU Make, which wraps Task transparently)

Enable Remote Taskfiles:

```sh
export TASK_X_REMOTE_TASKFILES=1
```
- [Task](https://taskfile.dev/) v3.53.1+ (or GNU Make, which wraps Task transparently)

## Building

Expand Down Expand Up @@ -81,10 +75,10 @@ automatically determines the git tag — no manual tagging is needed.

## Architecture

The CLI follows a resource-oriented architecture:
The CLI follows a model-oriented architecture, where each API resource has a model:

- **Commands** (`internal/cmd/`) — Kong-based command definitions with subcommand routing
- **Resources** (`internal/resource/`) — Unified interface for API objects with field introspection
- **Models** (`internal/model/`) — Unified interface for API objects with field introspection
- **Multi-Metro** (`internal/multimetro/`) — Client abstraction for global infrastructure operations
- **Configuration** (`internal/config/`) — Profile and credential management
- **Telemetry** (`internal/telemetry/`) — Usage analytics (opt-out via `--no-telemetry`)
Expand Down
4 changes: 1 addition & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,14 @@ Q ?= @
GO ?= go

ifeq ($(shell command -v task >/dev/null 2>&1 && echo yes),)
TASK ?= $(GO) run -v github.com/go-task/task/v3/cmd/task@v3.48.0 --yes
TASK ?= $(GO) run -v github.com/go-task/task/v3/cmd/task@v3.54.0 --yes
else
TASK ?= task --yes
endif
ifeq ($(Q),)
TASK := $(TASK) --verbose
endif

export TASK_X_REMOTE_TASKFILES=1

.DEFAULT_GOAL := help
.PHONY: help

Expand Down
51 changes: 48 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,7 @@ Or, if you need to directly specify a metro endpoint, you can manually create a
profile:

```yaml
# Linux: ~/.config/unikraft/config.yaml
# MacOS: ~/Library/Application\ Support/unikraft/config.yaml
# Linux and macOS: ~/.config/unikraft/config.yaml
# Windows: %AppData%\unikraft\config.yaml
profile: default
profiles:
Expand Down Expand Up @@ -296,6 +295,7 @@ unikraft instances logs my-instance
| `run` | Run an image as an instance |
| `build` | Build a Unikraft project into a VM image |
| `tui` | Browse resources in a TUI |
| `mcp` | Serve Unikraft Cloud to AI assistants over the Model Context Protocol |
| `metros` | List available metro locations |
| `instances` | Manage instances (list, get, create, edit, delete, logs, start, stop, restart) |
| `volumes` | Manage persistent volumes (list, get, create, edit, delete, clone) |
Expand Down Expand Up @@ -362,7 +362,9 @@ unikraft build . --output my-org/my-app:latest

## Configuration

The CLI stores configuration in `~/.config/unikraft/config.yaml` (or the path specified by `UNIKRAFT_CONFIG`).
The CLI stores configuration in `~/.config/unikraft/config.yaml` on Linux and macOS, and in `%AppData%\unikraft\config.yaml` on Windows.
On Linux and macOS, if `XDG_CONFIG_HOME` is set, the CLI uses `$XDG_CONFIG_HOME/unikraft/config.yaml`.
If `UNIKRAFT_CONFIG` is set, the CLI uses that path.

### Environment Variables

Expand All @@ -374,6 +376,7 @@ The CLI stores configuration in `~/.config/unikraft/config.yaml` (or the path sp
| `UNIKRAFT_LOG_LEVEL` | Set log level (`trace`, `debug`, `info`, `warn`, `error`, `fatal`) |
| `UNIKRAFT_LOG_TYPE` | Set output format (`text`, `json`) |
| `UNIKRAFT_TELEMETRY` | Enable/disable usage analytics (`true`, `false`) |
| `UNIKRAFT_DOCS_URL` | Documentation site that `unikraft mcp` serves as resources; empty serves none |
| `UKC_TOKEN` | API token for legacy profiles / `UNIKRAFT_PROFILE_ENV` |
| `UKC_METRO` | Metro name (e.g. `fra`) or full endpoint URL for legacy profiles / `UNIKRAFT_PROFILE_ENV` |
| `UKC_ALLOW_INSECURE` | Skip TLS verification for the metro from `UKC_METRO` (`true`, `false`) |
Expand Down Expand Up @@ -411,6 +414,48 @@ unikraft instances get my-instance --fields name,state,image
unikraft instances get my-instance -v
```

## MCP Server

`unikraft mcp` lets AI assistants such as Claude Code, Claude Desktop or Cursor manage Unikraft Cloud through the [Model Context Protocol](https://modelcontextprotocol.io).
Every resource type gets typed tools such as `instance_list` or `volume_create`, instances also get `instance_control` and `instance_logs`, and sandboxes get `sandbox_exec`, `sandbox_read` and `sandbox_write`.
The tools use the current profile, and the documentation is served as `unikraft://docs` resources.

Register the server with a client as a stdio server:

```json
{"mcpServers": {"unikraft": {"command": "unikraft", "args": ["mcp"]}}}
```

With Claude Code:

```sh
claude mcp add unikraft -- unikraft mcp
```

Serve fewer tools to keep the assistant's context small, or only the ones that cannot change anything:

```sh
# Only the instance, volume and metro tools
unikraft mcp --resources instance,volume,metro

# Only list, image_get, instance_logs and sandbox_read
unikraft mcp --read-only
```

Pass `--listen` to serve over HTTP at `/mcp` instead, and `--tls-cert` with `--tls-key` for HTTPS.
Clients then register the server by URL:

```sh
unikraft mcp --listen 127.0.0.1:8080
```

```json
{"mcpServers": {"unikraft": {"type": "http", "url": "http://127.0.0.1:8080/mcp"}}}
```

Only loopback addresses are accepted unless `--allow-remote` is given.
Every client acts with the profile's credentials, so put an authenticating proxy in front of a server that others can reach.

## Shell Completion

Generate completion scripts for your shell:
Expand Down
2 changes: 1 addition & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ tasks:
- task: go:test
vars:
go_tags: "integration"
package: ./cmd/unikraft/integration ./internal/cmd
package: ./cmd/unikraft/integration
CLI_ARGS: '{{if .TEST}}-run "{{.TEST}}"{{end}}'

golden:
Expand Down
15 changes: 15 additions & 0 deletions cmd/unikraft/help_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func TestHelp(t *testing.T) {
run("run", runHelpTests)
run("config", configHelpTests)
run("api", apiHelpTests)
run("mcp", mcpHelpTests)
}

// TestVersion checks that `unikraft version` output contains expected fields.
Expand Down Expand Up @@ -206,6 +207,13 @@ func buildHelpTests(t *testing.T, unikraftPath string) {
r := integ.NewTestEnv(t, unikraftPath)
integ.Gild(t, cli(r),
[]string{"unikraft", "build", "--help"},
[]string{"unikraft", "builder", "--help"},
[]string{"unikraft", "builder", "create", "--help"},
[]string{"unikraft", "builder", "get", "--help"},
[]string{"unikraft", "builder", "list", "--help"},
[]string{"unikraft", "builder", "delete", "--help"},
[]string{"unikraft", "builder", "du", "--help"},
[]string{"unikraft", "builder", "prune", "--help"},
)
}

Expand All @@ -231,6 +239,13 @@ func apiHelpTests(t *testing.T, unikraftPath string) {
)
}

func mcpHelpTests(t *testing.T, unikraftPath string) {
r := integ.NewTestEnv(t, unikraftPath)
integ.Gild(t, cli(r),
[]string{"unikraft", "mcp", "--help"},
)
}

// cli returns a callback that runs a CLI command and formats its output for
// golden comparison. It tolerates command failures and records the exit code.
func cli(env *integ.TestEnv) func(*testing.T, []string) string {
Expand Down
48 changes: 48 additions & 0 deletions cmd/unikraft/integration/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
package integration

import (
"encoding/pem"
"fmt"
"os"
"path/filepath"
"testing"

"github.com/containerd/continuity/fs/fstest"
Expand Down Expand Up @@ -318,4 +321,49 @@ cmd: ["sh", "-c", "cat /etc/unikraft-e2e && echo KRAFTFILE_CMD_USED"]

r.Run(t, []string{"unikraft", "instance", "delete", "test-" + instName})
})

t.Run("remote", func(t *testing.T) {
buildURL := os.Getenv("UNIKRAFT_X_TEST_BUILD_URL")
if buildURL == "" {
t.Skip("remote build test requires UNIKRAFT_X_TEST_BUILD_URL")
}
r := runner(t, true, []string{staging, stable})

image := r.Profile.Organization + "/remote-e2e:" + uniq()
instName := uniq()

// The build service's certificate may be self-signed.
certFile := filepath.Join(t.TempDir(), "build-service.pem")
cert := integ.HTTPGetTLSCerts(t, buildURL, "")[0]
require.NoError(t, os.WriteFile(certFile, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}), 0o644))

dir := t.TempDir()
require.NoError(t, fstest.Apply(
fstest.CreateFile("Dockerfile", []byte(`
FROM busybox:latest
RUN --mount=type=secret,id=greeting cp /run/secrets/greeting /greeting
`), 0o644),
fstest.CreateFile("Kraftfile", []byte(`
spec: v0.7
runtime: base-compat:latest
rootfs:
format: erofs
source: ./Dockerfile
cmd: ["sh", "-c", "cat /greeting"]
`), 0o644),
fstest.CreateFile("greeting", []byte("REMOTE_BUILD_OK\n"), 0o644),
).Apply(dir))

// The service reads the project and the secret through the session.
r.Run(t, []string{"unikraft", "build", ".", "--remote", buildURL, "--secret", "id=greeting,src=" + filepath.Join(dir, "greeting"), "--output", image},
integ.WithWorkDir(dir), integ.WithEnv("SSL_CERT_FILE="+certFile))

r.Run(t, []string{"unikraft", "run", "--name", "test-" + instName, "--metro", r.MetroName, "--output", "quiet", "--image", image})
r.Run(t, []string{"unikraft", "--timeout", "10s", "instance", "wait", "--until", "state==stopped", "test-" + instName})

out := r.Run(t, []string{"unikraft", "instance", "logs", "test-" + instName})
assert.Regexp(t, `REMOTE_BUILD_OK`, out)

r.Run(t, []string{"unikraft", "instance", "delete", "test-" + instName})
})
}
Loading
Loading