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
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
# Not necessary to upload cache on self-hosted runner(s)
# ~/go/pkg/mod and ~/.cache/go-build stay on disk between runs automatically.
cache: false
go-version: '1.25'
go-version: '1.25.4'

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
Expand Down
12 changes: 7 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ jobs:
# Not necessary to upload cache on self-hosted runner(s)
# ~/go/pkg/mod and ~/.cache/go-build stay on disk between runs automatically.
cache: false
go-version: '1.25'
go-version: '1.25.4'

- name: Install dependencies
run: |
set -xe
if ! command -v mkfs.erofs &> /dev/null; then
if ! command -v mkfs.erofs &> /dev/null || \
! command -v mkfs.ext4 &> /dev/null || \
! command -v iptables &> /dev/null; then
sudo apt-get update
sudo apt-get install -y erofs-utils
sudo apt-get install -y erofs-utils e2fsprogs iptables
fi
go mod download

Expand Down Expand Up @@ -67,7 +69,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-go@v6
with:
go-version: '1.25'
go-version: '1.25.4'
cache: false
- name: Login to Docker Hub
uses: docker/login-action@v3
Expand Down Expand Up @@ -107,7 +109,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-go@v6
with:
go-version: '1.25'
go-version: '1.25.4'
cache: false
- name: Install dependencies
run: brew list caddy &>/dev/null || brew install caddy
Expand Down
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,13 @@ endif
# Linux tests (as root for network capabilities)
test-linux: ensure-ch-binaries ensure-caddy-binaries build-embedded
@VERBOSE_FLAG=""; \
TEST_PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$$PATH"; \
if [ -n "$(VERBOSE)" ]; then VERBOSE_FLAG="-v"; fi; \
if [ -n "$(TEST)" ]; then \
echo "Running specific test: $(TEST)"; \
sudo env "PATH=$$PATH" "DOCKER_CONFIG=$${DOCKER_CONFIG:-$$HOME/.docker}" go test -tags containers_image_openpgp -run=$(TEST) $$VERBOSE_FLAG -timeout=300s ./...; \
sudo env "PATH=$$TEST_PATH" "DOCKER_CONFIG=$${DOCKER_CONFIG:-$$HOME/.docker}" go test -tags containers_image_openpgp -run=$(TEST) $$VERBOSE_FLAG -timeout=300s ./...; \
else \
sudo env "PATH=$$PATH" "DOCKER_CONFIG=$${DOCKER_CONFIG:-$$HOME/.docker}" go test -tags containers_image_openpgp $$VERBOSE_FLAG -timeout=300s ./...; \
sudo env "PATH=$$TEST_PATH" "DOCKER_CONFIG=$${DOCKER_CONFIG:-$$HOME/.docker}" go test -tags containers_image_openpgp $$VERBOSE_FLAG -timeout=300s ./...; \
fi

# macOS tests (no sudo needed, adds e2fsprogs to PATH)
Expand Down
2 changes: 1 addition & 1 deletion cmd/api/api/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (s *ApiService) CreateInstance(ctx context.Context, request oapi.CreateInst
size = int64(sizeBytes)
}

// Parse hotplug_size (default: 3GB)
// Parse hotplug_size (optional; omitted/empty means no hotplug memory)
hotplugSize := int64(0)
if request.Body.HotplugSize != nil && *request.Body.HotplugSize != "" {
var hotplugBytes datasize.ByteSize
Expand Down
62 changes: 62 additions & 0 deletions cmd/api/api/instances_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package api

import (
"context"
"os"
"testing"
"time"

"github.com/c2h5oh/datasize"
"github.com/kernel/hypeman/lib/hypervisor"
"github.com/kernel/hypeman/lib/instances"
"github.com/kernel/hypeman/lib/oapi"
"github.com/kernel/hypeman/lib/paths"
"github.com/kernel/hypeman/lib/system"
Expand Down Expand Up @@ -128,6 +132,64 @@ func TestCreateInstance_InvalidSizeFormat(t *testing.T) {
assert.Contains(t, badReq.Message, "invalid size format")
}

type captureCreateManager struct {
instances.Manager
lastReq *instances.CreateInstanceRequest
}

func (m *captureCreateManager) CreateInstance(ctx context.Context, req instances.CreateInstanceRequest) (*instances.Instance, error) {
reqCopy := req
m.lastReq = &reqCopy

now := time.Now()
return &instances.Instance{
StoredMetadata: instances.StoredMetadata{
Id: "inst-hotplug-default",
Name: req.Name,
Image: req.Image,
Size: req.Size,
HotplugSize: req.HotplugSize,
OverlaySize: req.OverlaySize,
Vcpus: req.Vcpus,
CreatedAt: now,
HypervisorType: hypervisor.TypeCloudHypervisor,
},
State: instances.StateRunning,
}, nil
}

func TestCreateInstance_OmittedHotplugSizeDefaultsToZero(t *testing.T) {
svc := newTestService(t)

origMgr := svc.InstanceManager
mockMgr := &captureCreateManager{Manager: origMgr}
svc.InstanceManager = mockMgr

size := "1GB"
overlaySize := "10GB"
resp, err := svc.CreateInstance(ctx(), oapi.CreateInstanceRequestObject{
Body: &oapi.CreateInstanceRequest{
Name: "test-no-hotplug",
Image: "docker.io/library/alpine:latest",
Size: &size,
OverlaySize: &overlaySize,
},
})
require.NoError(t, err)

created, ok := resp.(oapi.CreateInstance201JSONResponse)
require.True(t, ok, "expected 201 response")
assert.NotNil(t, mockMgr.lastReq, "CreateInstance should be called")
assert.Equal(t, int64(0), mockMgr.lastReq.HotplugSize, "omitted hotplug_size should not allocate default memory")

instance := oapi.Instance(created)
require.NotNil(t, instance.HotplugSize)

var hotplugBytes datasize.ByteSize
require.NoError(t, hotplugBytes.UnmarshalText([]byte(*instance.HotplugSize)))
assert.Equal(t, int64(0), int64(hotplugBytes), "response should report zero hotplug_size when omitted")
}

func TestInstanceLifecycle_StopStart(t *testing.T) {
// Require KVM access for VM creation
if _, err := os.Stat("/dev/kvm"); os.IsNotExist(err) {
Expand Down
3 changes: 0 additions & 3 deletions lib/instances/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,6 @@ func (m *manager) createInstance(
size = 1 * 1024 * 1024 * 1024 // 1GB default
}
hotplugSize := req.HotplugSize
if hotplugSize == 0 {
hotplugSize = 3 * 1024 * 1024 * 1024 // 3GB default
}
overlaySize := req.OverlaySize
if overlaySize == 0 {
overlaySize = 10 * 1024 * 1024 * 1024 // 10GB default
Expand Down
2 changes: 1 addition & 1 deletion lib/instances/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ type CreateInstanceRequest struct {
Name string // Required
Image string // Required: OCI reference
Size int64 // Base memory in bytes (default: 1GB)
HotplugSize int64 // Hotplug memory in bytes (default: 3GB)
HotplugSize int64 // Hotplug memory in bytes (default: 0, set explicitly to enable)
OverlaySize int64 // Overlay disk size in bytes (default: 10GB)
Vcpus int // Default 2
NetworkBandwidthDownload int64 // Download rate limit bytes/sec (0 = auto, proportional to CPU)
Expand Down
3 changes: 1 addition & 2 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,7 @@ components:
example: "2GB"
hotplug_size:
type: string
description: Additional memory for hotplug (human-readable format like "3GB", "1G")
default: "3GB"
description: Additional memory for hotplug (human-readable format like "3GB", "1G"). Omit to disable hotplug memory.
example: "2GB"
overlay_size:
type: string
Expand Down