Skip to content

Commit

Permalink
add compute core APIs
Browse files Browse the repository at this point in the history
Signed-off-by: Hamza El-Saawy <hamzaelsaawy@microsoft.com>
  • Loading branch information
helsaawy committed Dec 6, 2023
1 parent cff5c86 commit 44e0bea
Show file tree
Hide file tree
Showing 13 changed files with 926 additions and 0 deletions.
55 changes: 55 additions & 0 deletions internal/computecore/callback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//go:build windows

package computecore

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

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)

hcsOperationCompletionPtr uintptr
)

func (f HCSOperationCompletion) asCallback() hcsOperationCompletionPtr {
if f == nil {
return hcsOperationCompletionPtr(0)
}
return hcsOperationCompletionPtr(windows.NewCallback(
// NewCallback expects a function with one uintptr-sized result
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)

hcsEventCallbackPtr uintptr

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

View workflow job for this annotation

GitHub Actions / lint (windows)

type `hcsEventCallbackPtr` is unused (unused)
)

func (f HCSEventCallback) asCallback() hcsEventCallbackPtr {

Check failure on line 44 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 hcsEventCallbackPtr(0)
}
return hcsEventCallbackPtr(windows.NewCallback(
// NewCallback expects a function with one uintptr-sized result
func(event *HCSEvent, hcsContext uintptr) uintptr {
f(event, hcsContext)
return 0
},
))
}
121 changes: 121 additions & 0 deletions internal/computecore/computecore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//go:build windows

package computecore

import (
"bytes"
"context"
"encoding/json"
"fmt"
"strings"

"go.opencensus.io/trace"
"golang.org/x/sys/windows"

hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
"github.com/Microsoft/hcsshim/internal/interop"
"github.com/Microsoft/hcsshim/internal/log"
"github.com/Microsoft/hcsshim/internal/oc"
)

// TODO: HcsSetComputeSystemCallback & HcsSetProcessCallback

type (
// Handle to a compute HCSSystem.
HCSSystem windows.Handle

// Handle to a HCSProcess running in a compute system.
HCSProcess windows.Handle

// Handle to a HCSCallback registered on a compute system or process handle.
HCSCallback windows.Handle
)

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

// HRESULT WINAPI
// HcsEnumerateComputeSystems(
// _In_opt_ PCWSTR query,
// _In_ HCS_OPERATION operation
// );
//sys hcsEnumerateComputeSystems(query string, operation HCSOperation) (hr error) = computecore.HcsEnumerateComputeSystems?

func EnumerateComputeSystems(ctx context.Context, query *hcsschema.SystemQuery) (properties []hcsschema.Properties, err error) {
ctx, span := oc.StartSpan(ctx, "computecore::HcsEnumerateComputeSystems", oc.WithClientSpanKind)
defer func() {
if len(properties) != 0 {
span.AddAttributes(trace.StringAttribute("properties", log.Format(ctx, properties)))
}
oc.SetSpanStatus(span, err)
span.End()
}()

return parseResults[[]hcsschema.Properties](runOperation(
ctx,
func(_ context.Context, op HCSOperation) (err error) {
q := ""
if query != nil {
q, err = encode(query)
if err != nil {
return err
}
}

tryAddAttributes(ctx, trace.StringAttribute("query", q))
return hcsEnumerateComputeSystems(q, op)
},
))
}

func parseResults[T any](result string, err error) (v T, _ error) {
if err != nil {
// TODO: try to parse result as ResultError
return v, err
}
err = json.Unmarshal([]byte(result), &v)
return v, err
}

func runOperation(ctx context.Context, f func(ctx context.Context, op HCSOperation) error) (result string, err error) {
// don't use operation callback, since it will fail if the compute system has a callback
op, err := EmptyOperation(ctx)
if err != nil {
return "", fmt.Errorf("operation creation: %w", err)
}
defer op.Close() //nolint:staticcheck // ignore close error

if err := f(ctx, op); err != nil {
return "", err
}

result, err = op.WaitForOperationResult(ctx, 0)
return result, err
}

func tryAddAttributes(ctx context.Context, attributes ...trace.Attribute) {
if s := trace.FromContext(ctx); s != nil {
s.AddAttributes(attributes...)
}
}

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

func encode(v any) (string, error) {
// 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
}
2 changes: 2 additions & 0 deletions internal/computecore/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// This package provides bindings and utility functions for dealing with ComputeCore.dll Win32 APIs.
package computecore
113 changes: 113 additions & 0 deletions internal/computecore/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//go:build windows

package computecore

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

const hcsHResultPrefix = 0x80370000

// 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(hcsHResultPrefix + 0x0100)

Check failure on line 16 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(hcsHResultPrefix + 0x0101)

Check failure on line 19 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(hcsHResultPrefix + 0x0102)

Check failure on line 22 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(hcsHResultPrefix + 0x0105)

Check failure on line 25 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(hcsHResultPrefix + 0x0106)

Check failure on line 28 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(hcsHResultPrefix + 0x0107)

Check failure on line 31 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)

// A connection could not be established with the container or virtual machine.
HCS_E_CONNECT_FAILED = windows.Errno(hcsHResultPrefix + 0x0108)

// The operation timed out because a response was not received from the virtual machine or container.
HCS_E_CONNECTION_TIMEOUT = windows.Errno(hcsHResultPrefix + 0x0109)

// The connection with the virtual machine or container was closed.
HCS_E_CONNECTION_CLOSED = windows.Errno(hcsHResultPrefix + 0x010A)

// An unknown internal message was received by the virtual machine or container.
HCS_E_UNKNOWN_MESSAGE = windows.Errno(hcsHResultPrefix + 0x010B)

// 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(hcsHResultPrefix + 0x010C)

// The virtual machine or container JSON document is invalid.
HCS_E_INVALID_JSON = windows.Errno(hcsHResultPrefix + 0x010D)

// A virtual machine or container with the specified identifier does not exist.
HCS_E_SYSTEM_NOT_FOUND = windows.Errno(hcsHResultPrefix + 0x010E)

// A virtual machine or container with the specified identifier already exists.
HCS_E_SYSTEM_ALREADY_EXISTS = windows.Errno(hcsHResultPrefix + 0x010F)

// The virtual machine or container with the specified identifier is not running.
HCS_E_SYSTEM_ALREADY_STOPPED = windows.Errno(hcsHResultPrefix + 0x0110)

// A communication protocol error has occurred between the virtual machine or container and the host.
HCS_E_PROTOCOL_ERROR = windows.Errno(hcsHResultPrefix + 0x0111)

// The container image contains a layer with an unrecognized format.
HCS_E_INVALID_LAYER = windows.Errno(hcsHResultPrefix + 0x0112)

// 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(hcsHResultPrefix + 0x0113)

// The operation could not be started because a required feature is not installed.
HCS_E_SERVICE_NOT_AVAILABLE = windows.Errno(hcsHResultPrefix + 0x0114)

// The operation has not started.
HCS_E_OPERATION_NOT_STARTED = windows.Errno(hcsHResultPrefix + 0x0115)

// The operation is already running.
HCS_E_OPERATION_ALREADY_STARTED = windows.Errno(hcsHResultPrefix + 0x0116)

// The operation is still running.
HCS_E_OPERATION_PENDING = windows.Errno(hcsHResultPrefix + 0x0117)

// The operation did not complete in time.
HCS_E_OPERATION_TIMEOUT = windows.Errno(hcsHResultPrefix + 0x0118)

// An event callback has already been registered on this handle.
HCS_E_OPERATION_SYSTEM_CALLBACK_ALREADY_SET = windows.Errno(hcsHResultPrefix + 0x0119)

// Not enough memory available to return the result of the operation.
HCS_E_OPERATION_RESULT_ALLOCATION_FAILED = windows.Errno(hcsHResultPrefix + 0x011A)

// 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(hcsHResultPrefix + 0x011B)

// The virtual machine or container reported a critical error and was stopped or restarted.
HCS_E_GUEST_CRITICAL_ERROR = windows.Errno(hcsHResultPrefix + 0x011C)

// The process information is not available.
HCS_E_PROCESS_INFO_NOT_AVAILABLE = windows.Errno(hcsHResultPrefix + 0x011D)

// The host compute system service has disconnected unexpectedly.
HCS_E_SERVICE_DISCONNECT = windows.Errno(hcsHResultPrefix + 0x011E)

// The process has already exited.
HCS_E_PROCESS_ALREADY_STOPPED = windows.Errno(hcsHResultPrefix + 0x011F)

// The virtual machine or container is not configured to perform the operation.
HCS_E_SYSTEM_NOT_CONFIGURED_FOR_OPERATION = windows.Errno(hcsHResultPrefix + 0x0120)

// The operation has already been cancelled.
HCS_E_OPERATION_ALREADY_CANCELLED = windows.Errno(hcsHResultPrefix + 0x0121)
)
63 changes: 63 additions & 0 deletions internal/computecore/event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//go:build windows

package computecore

// typedef struct HCS_EVENT
// {
// HCS_EVENT_TYPE Type;
// PCWSTR EventData;
// HCS_OPERATION Operation;
// } HCS_EVENT;
//
// https://learn.microsoft.com/en-us/virtualization/api/hcs/reference/hcs_event
type HCSEvent struct {
Type HCSEventType
// eventData provides additional data for the event as a JSON document.
//
// the type of JSON document depends on the event type:
//
// HcsEventSystemExited github.com/Microsoft/hcsshim/internal/hcs/schema2.SystemExitStatus
// HcsEventSystemCrashInitiated github.com/Microsoft/hcsshim/internal/hcs/schema2.CrashReport
// HcsEventSystemCrashReport github.com/Microsoft/hcsshim/internal/hcs/schema2.CrashReport
// HcsEventProcessExited github.com/Microsoft/hcsshim/internal/hcs/schema2.ProcessStatus
EventData *uint16
// Handle to a completed operation, if Type is eventOperationCallback.
// This is only possible when HcsSetComputeSystemCallback has specified event option HcsEventOptionEnableOperationCallbacks.
Operation HCSOperation
}

//go:generate go run golang.org/x/tools/cmd/stringer -type=HCSEventType -trimprefix=Event event.go

// HCSEventType indicates the event type for callbacks registered by hcsSetComputeSystemCallback or hcsSetProcessCallback.
//
// See [documentation] for more info.
//
// [documentation]: https://learn.microsoft.com/en-us/virtualization/api/hcs/reference/hcs_event_type
type HCSEventType int32

const (
EventInvalid = HCSEventType(0x00000000)
EventSystemExited = HCSEventType(0x00000001)
EventSystemCrashInitiated = HCSEventType(0x00000002)
EventSystemCrashReport = HCSEventType(0x00000003)
EventSystemRdpEnhancedModeStateChanged = HCSEventType(0x00000004)
EventSystemSiloJobCreated = HCSEventType(0x00000005)
EventSystemGuestConnectionClosed = HCSEventType(0x00000006)
EventProcessExited = HCSEventType(0x00010000)
EventOperationCallback = HCSEventType(0x01000000)
EventServiceDisconnect = HCSEventType(0x02000000)
)

//go:generate go run golang.org/x/tools/cmd/stringer -type=HCSEventOptions -trimprefix=EventOption event.go

// HCSEventOptions defines the options for an event callback registration, used in HcsSetComputeSystemCallback and HcsSetProcessCallback.
//
// See [documentation] for more info.
//
// [documentation]: https://learn.microsoft.com/en-us/virtualization/api/hcs/reference/hcs_event_options
type HCSEventOptions int32

const (
EventOptionNone = HCSEventOptions(0x00000000)
EventOptionEnableOperationCallbacks = HCSEventOptions(0x00000001)
)
24 changes: 24 additions & 0 deletions internal/computecore/hcseventoptions_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 44e0bea

Please sign in to comment.