Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HCS v2 bindings for creating compute systems. #2277

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
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
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ require (
go.uber.org/mock v0.4.0
golang.org/x/sync v0.8.0
golang.org/x/sys v0.25.0
golang.org/x/tools v0.24.0
google.golang.org/grpc v1.66.0
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
google.golang.org/protobuf v1.34.2
Expand Down Expand Up @@ -111,10 +112,9 @@ require (
go.opentelemetry.io/otel/sdk v1.28.0 // indirect
go.opentelemetry.io/otel/trace v1.28.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -410,8 +410,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
65 changes: 65 additions & 0 deletions internal/computecore/callback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//go:build windows

package computecore

import "golang.org/x/sys/windows"

// Callback functions must be converted to a uintptr via [windows.NewCallback] before being
// passed to a syscall.
//
// Additionally, [windows.NewCallback] expects functions to return a uintptr result,
// so callbacks must be modified ahead of time.
//
// Create a dedicated type uintptr for each callback to ensure type safety.

// The `void* context` parameter is for an arbitrary payload to passed into the callback,
// allowing for operation-specific data to be provided to a more generic callback.

type (
// Function type for the completion callback of an operation.
//
// typedef void (CALLBACK *HCS_OPERATION_COMPLETION)(
// _In_ HCS_OPERATION operation,
// _In_opt_ void* context
// );
HCSOperationCompletion func(op HCSOperation, hcsContext uintptr)

hcsOperationCompletionUintptr uintptr
)

func (f HCSOperationCompletion) asCallback() hcsOperationCompletionUintptr {

Check failure on line 30 in internal/computecore/callback.go

View workflow job for this annotation

GitHub Actions / lint (windows)

func `HCSOperationCompletion.asCallback` is unused (unused)
if f == nil {
return hcsOperationCompletionUintptr(0)
}
return hcsOperationCompletionUintptr(windows.NewCallback(
func(op HCSOperation, hcsContext uintptr) uintptr {
f(op, hcsContext)
return 0
},
))
}

type (
// Function type for compute system event callbacks.
//
// typedef void (CALLBACK *HCS_EVENT_CALLBACK)(
// _In_ HCS_EVENT* event,
// _In_opt_ void* context
// );
HCSEventCallback func(event *HCSEvent, hcsContext uintptr)

hcsEventCallbackUintptr uintptr

Check failure on line 51 in internal/computecore/callback.go

View workflow job for this annotation

GitHub Actions / lint (windows)

type `hcsEventCallbackUintptr` is unused (unused)
)

func (f HCSEventCallback) asCallback() hcsEventCallbackUintptr {

Check failure on line 54 in internal/computecore/callback.go

View workflow job for this annotation

GitHub Actions / lint (windows)

func `HCSEventCallback.asCallback` is unused (unused)
if f == nil {
return hcsEventCallbackUintptr(0)
}
return hcsEventCallbackUintptr(windows.NewCallback(
// NewCallback expects a function with one uintptr-sized result
func(event *HCSEvent, hcsContext uintptr) uintptr {
f(event, hcsContext)
return 0
},
))
}
127 changes: 127 additions & 0 deletions internal/computecore/computecore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
//go:build windows

package computecore

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"syscall"
"time"

"github.com/Microsoft/hcsshim/internal/interop"
"github.com/Microsoft/hcsshim/internal/log"
"github.com/Microsoft/hcsshim/internal/logfields"
"github.com/Microsoft/hcsshim/internal/oc"
"github.com/Microsoft/hcsshim/internal/timeout"

"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
"golang.org/x/sys/windows"
)

//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go ./*.go

//sys hcsCreateComputeSystem(id string, configuration string, operation HCSOperation, security_descriptor *uint32, computeSystem *HCSSystem) (hr error) = computecore.HcsCreateComputeSystem?

type (
HCSSystem syscall.Handle
)

func HcsCreateComputeSystem(ctx context.Context, id string, configuration string, operation HCSOperation, securityDescriptor *uint32) (computeSystem HCSSystem, result string, hr error) {
ctx, span := oc.StartSpan(ctx, "computecore::HcsCreateComputeSystem")
defer span.End()
defer func() {
if result != "" {
span.AddAttributes(trace.StringAttribute("resultDocument", result))
}
if !errors.Is(hr, windows.ERROR_VMCOMPUTE_OPERATION_PENDING) {
oc.SetSpanStatus(span, hr)
}
}()
span.AddAttributes(
trace.StringAttribute("id", id),
trace.StringAttribute("configuration", configuration),
)

return computeSystem, result, execute(ctx, timeout.SystemCreate, func() error {
var resultp *uint16
err := hcsCreateComputeSystem(id, configuration, operation, securityDescriptor, &computeSystem)
if resultp != nil {

Check failure on line 53 in internal/computecore/computecore.go

View workflow job for this annotation

GitHub Actions / lint (windows)

nilness: impossible condition: nil != nil (govet)
result = interop.ConvertAndFreeCoTaskMemString(resultp)
}

// FIXME: here we synchronously wait for operation result, but in the future we should probably switch
// to notification model.
if opResult, opErr := operation.WaitForResult(ctx); opErr != nil {
log.G(ctx).WithError(opErr).Error("Failed to wait for operation result")
return opErr
} else {
log.G(ctx).WithField("result", opResult).Debug("operation completed")
}
return err
})
}

func bufferToString(buffer *uint16) string {
if buffer == nil {
return ""
}
return interop.ConvertAndFreeCoTaskMemString(buffer)
}

func encode(v any) (string, error) {

Check failure on line 76 in internal/computecore/computecore.go

View workflow job for this annotation

GitHub Actions / lint (windows)

func `encode` is unused (unused)
// TODO: pool of encoders/buffers
buf := &bytes.Buffer{}
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
enc.SetIndent("", "")

if err := enc.Encode(v); err != nil {
return "", fmt.Errorf("json encoding: %w", err)
}

// encoder.Encode appends a newline to the end
return strings.TrimSpace(buf.String()), nil
}

func execute(ctx context.Context, timeout time.Duration, f func() error) error {
now := time.Now()
if timeout > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, timeout)
defer cancel()
}

deadline, ok := ctx.Deadline()
trueTimeout := timeout
if ok {
trueTimeout = deadline.Sub(now)
log.G(ctx).WithFields(logrus.Fields{
logfields.Timeout: trueTimeout,
"desiredTimeout": timeout,
}).Trace("Executing syscall with deadline")
}

done := make(chan error, 1)
go func() {
done <- f()
}()

select {
case <-ctx.Done():
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
log.G(ctx).WithField(logfields.Timeout, trueTimeout).
Warning("Syscall did not complete within operation timeout. This may indicate a platform issue. " +
"If it appears to be making no forward progress, obtain the stacks and see if there is a syscall " +
"stuck in the platform API for a significant length of time.")
}
return ctx.Err()
case err := <-done:
return err
}

}
1 change: 1 addition & 0 deletions internal/computecore/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package computecore
111 changes: 111 additions & 0 deletions internal/computecore/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//go:build windows

package computecore

import "golang.org/x/sys/windows"

// HCS specific error codes.
//
// See [documentation] for more info.
//
// [documentation]: https://learn.microsoft.com/en-us/virtualization/api/hcs/reference/hcshresult
const (
// The virtual machine or container exited unexpectedly while starting.
HCS_E_TERMINATED_DURING_START windows.Errno = 0x80370100

Check failure on line 14 in internal/computecore/errors.go

View workflow job for this annotation

GitHub Actions / lint (windows)

ST1003: should not use ALL_CAPS in Go names; use CamelCase instead (stylecheck)

// The container operating system does not match the host operating system.
HCS_E_IMAGE_MISMATCH windows.Errno = 0x80370101

Check failure on line 17 in internal/computecore/errors.go

View workflow job for this annotation

GitHub Actions / lint (windows)

ST1003: should not use ALL_CAPS in Go names; use CamelCase instead (stylecheck)

// The virtual machine could not be started because a required feature is not installed.
HCS_E_HYPERV_NOT_INSTALLED windows.Errno = 0x80370102

Check failure on line 20 in internal/computecore/errors.go

View workflow job for this annotation

GitHub Actions / lint (windows)

ST1003: should not use ALL_CAPS in Go names; use CamelCase instead (stylecheck)

// The requested virtual machine or container operation is not valid in the current state.
HCS_E_INVALID_STATE windows.Errno = 0x80370105

Check failure on line 23 in internal/computecore/errors.go

View workflow job for this annotation

GitHub Actions / lint (windows)

ST1003: should not use ALL_CAPS in Go names; use CamelCase instead (stylecheck)

// The virtual machine or container exited unexpectedly.
HCS_E_UNEXPECTED_EXIT windows.Errno = 0x80370106

Check failure on line 26 in internal/computecore/errors.go

View workflow job for this annotation

GitHub Actions / lint (windows)

ST1003: should not use ALL_CAPS in Go names; use CamelCase instead (stylecheck)

// The virtual machine or container was forcefully exited.
HCS_E_TERMINATED windows.Errno = 0x80370107

// A connection could not be established with the container or virtual machine.
HCS_E_CONNECT_FAILED windows.Errno = 0x80370108

// The operation timed out because a response was not received from the virtual machine or container.
HCS_E_CONNECTION_TIMEOUT windows.Errno = 0x80370109

// The connection with the virtual machine or container was closed.
HCS_E_CONNECTION_CLOSED windows.Errno = 0x8037010A

// An unknown internal message was received by the virtual machine or container.
HCS_E_UNKNOWN_MESSAGE windows.Errno = 0x8037010B

// The virtual machine or container does not support an available version of the communication protocol with the host.
HCS_E_UNSUPPORTED_PROTOCOL_VERSION windows.Errno = 0x8037010C

// The virtual machine or container JSON document is invalid.
HCS_E_INVALID_JSON windows.Errno = 0x8037010D

// A virtual machine or container with the specified identifier does not exist.
HCS_E_SYSTEM_NOT_FOUND windows.Errno = 0x8037010E

// A virtual machine or container with the specified identifier already exists.
HCS_E_SYSTEM_ALREADY_EXISTS windows.Errno = 0x8037010F

// The virtual machine or container with the specified identifier is not running.
HCS_E_SYSTEM_ALREADY_STOPPED windows.Errno = 0x80370110

// A communication protocol error has occurred between the virtual machine or container and the host.
HCS_E_PROTOCOL_ERROR windows.Errno = 0x80370111

// The container image contains a layer with an unrecognized format.
HCS_E_INVALID_LAYER windows.Errno = 0x80370112

// To use this container image, you must join the Windows Insider Program.
// Please see https://go.microsoft.com/fwlink/?linkid=850659 for more information.
HCS_E_WINDOWS_INSIDER_REQUIRED windows.Errno = 0x80370113

// The operation could not be started because a required feature is not installed.
HCS_E_SERVICE_NOT_AVAILABLE windows.Errno = 0x80370114

// The operation has not started.
HCS_E_OPERATION_NOT_STARTED windows.Errno = 0x80370115

// The operation is already running.
HCS_E_OPERATION_ALREADY_STARTED windows.Errno = 0x80370116

// The operation is still running.
HCS_E_OPERATION_PENDING windows.Errno = 0x80370117

// The operation did not complete in time.
HCS_E_OPERATION_TIMEOUT windows.Errno = 0x80370118

// An event callback has already been registered on this handle.
HCS_E_OPERATION_SYSTEM_CALLBACK_ALREADY_SET windows.Errno = 0x80370119

// Not enough memory available to return the result of the operation.
HCS_E_OPERATION_RESULT_ALLOCATION_FAILED windows.Errno = 0x8037011A

// Insufficient privileges.
// Only administrators or users that are members of the Hyper-V Administrators user group are permitted to access virtual machines or containers.
// To add yourself to the Hyper-V Administrators user group, please see https://aka.ms/hcsadmin for more information.
HCS_E_ACCESS_DENIED windows.Errno = 0x8037011B

// The virtual machine or container reported a critical error and was stopped or restarted.
HCS_E_GUEST_CRITICAL_ERROR windows.Errno = 0x8037011C

// The process information is not available.
HCS_E_PROCESS_INFO_NOT_AVAILABLE windows.Errno = 0x8037011D

// The host compute system service has disconnected unexpectedly.
HCS_E_SERVICE_DISCONNECT windows.Errno = 0x8037011E

// The process has already exited.
HCS_E_PROCESS_ALREADY_STOPPED windows.Errno = 0x8037011F

// The virtual machine or container is not configured to perform the operation.
HCS_E_SYSTEM_NOT_CONFIGURED_FOR_OPERATION windows.Errno = 0x80370120

// The operation has already been cancelled.
HCS_E_OPERATION_ALREADY_CANCELLED windows.Errno = 0x80370121
)
Loading
Loading