diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c928f6a..29edccb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,14 +12,14 @@ on: jobs: build: - runs-on: "windows-2022" + runs-on: "windows-2025" steps: - uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v4 with: - go-version: '1.22' + go-version: '1.25' - name: Verify Dependencies run: go mod verify diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 1129b27..09464d4 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -8,14 +8,14 @@ on: jobs: integration: - runs-on: windows-2022 + runs-on: windows-2025 steps: - uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v4 with: - go-version: '1.22' + go-version: '1.25' - name: Run integration tests run: | diff --git a/hcn/hcnerrors.go b/hcn/hcnerrors.go index 78c2ab6..72f22cb 100644 --- a/hcn/hcnerrors.go +++ b/hcn/hcnerrors.go @@ -37,7 +37,7 @@ func checkForErrors(methodName string, hr error, resultBuffer *uint16) error { } if errorFound { - returnError := new(hr, methodName, result) + returnError := newHcnError(hr, methodName, result) logrus.Debugf(returnError.Error()) // HCN errors logged for debugging. return returnError } @@ -52,6 +52,11 @@ const ( ERROR_NOT_FOUND = ErrorCode(windows.ERROR_NOT_FOUND) HCN_E_PORT_ALREADY_EXISTS ErrorCode = ErrorCode(windows.HCN_E_PORT_ALREADY_EXISTS) HCN_E_NOTIMPL ErrorCode = ErrorCode(windows.E_NOTIMPL) + HCN_E_NETWORK_NOT_FOUND ErrorCode = ErrorCode(windows.HCN_E_NETWORK_NOT_FOUND) + HCN_E_ENDPOINT_NOT_FOUND ErrorCode = ErrorCode(windows.HCN_E_ENDPOINT_NOT_FOUND) + HCN_E_PORT_NOT_FOUND ErrorCode = ErrorCode(windows.HCN_E_PORT_NOT_FOUND) + HCN_E_INVALID_IP ErrorCode = ErrorCode(windows.HCN_E_INVALID_IP) + HCN_E_ADAPTER_NOT_FOUND ErrorCode = ErrorCode(windows.HCN_E_ADAPTER_NOT_FOUND) ) type HcnError struct { @@ -83,7 +88,27 @@ func IsNotImplemented(err error) bool { return CheckErrorWithCode(err, HCN_E_NOTIMPL) } -func new(hr error, title string, rest string) error { +func IsNetworkNotFoundError(err error) bool { + return CheckErrorWithCode(err, HCN_E_NETWORK_NOT_FOUND) +} + +func IsEndpointNotFoundError(err error) bool { + return CheckErrorWithCode(err, HCN_E_ENDPOINT_NOT_FOUND) +} + +func IsPortNotFoundError(err error) bool { + return CheckErrorWithCode(err, HCN_E_PORT_NOT_FOUND) +} + +func IsInvalidIPError(err error) bool { + return CheckErrorWithCode(err, HCN_E_INVALID_IP) +} + +func IsAdapterNotFoundError(err error) bool { + return CheckErrorWithCode(err, HCN_E_ADAPTER_NOT_FOUND) +} + +func newHcnError(hr error, title string, rest string) error { err := &HcnError{} hnsError := hns.NewHnsError(hr, title, rest) err.HnsError = hnsError.(*hns.HnsError) //nolint:errorlint diff --git a/hcn/hcnerrors_unit_test.go b/hcn/hcnerrors_unit_test.go new file mode 100644 index 0000000..aaa8168 --- /dev/null +++ b/hcn/hcnerrors_unit_test.go @@ -0,0 +1,151 @@ +//go:build windows +// +build windows + +package hcn + +import ( + "fmt" + "testing" + + "golang.org/x/sys/windows" +) + +func TestHCNErrorHelpers(t *testing.T) { + for _, tc := range []struct { + name string + err error + checkFn func(error) bool + expected bool + }{ + // IsElementNotFoundError + { + name: "IsElementNotFoundError with matching error", + err: newHcnError(windows.Errno(windows.ERROR_NOT_FOUND), "test", ""), + checkFn: IsElementNotFoundError, + expected: true, + }, + { + name: "IsElementNotFoundError with non-matching error", + err: newHcnError(windows.Errno(windows.E_NOTIMPL), "test", ""), + checkFn: IsElementNotFoundError, + expected: false, + }, + + // IsPortAlreadyExistsError + { + name: "IsPortAlreadyExistsError with matching error", + err: newHcnError(windows.Errno(windows.HCN_E_PORT_ALREADY_EXISTS), "test", ""), + checkFn: IsPortAlreadyExistsError, + expected: true, + }, + { + name: "IsPortAlreadyExistsError with non-matching error", + err: newHcnError(windows.Errno(windows.ERROR_NOT_FOUND), "test", ""), + checkFn: IsPortAlreadyExistsError, + expected: false, + }, + + // IsNotImplemented + { + name: "IsNotImplemented with matching error", + err: newHcnError(windows.Errno(windows.E_NOTIMPL), "test", ""), + checkFn: IsNotImplemented, + expected: true, + }, + { + name: "IsNotImplemented with non-matching error", + err: newHcnError(windows.Errno(windows.ERROR_NOT_FOUND), "test", ""), + checkFn: IsNotImplemented, + expected: false, + }, + + // IsNetworkNotFound + { + name: "IsNetworkNotFoundError with matching error", + err: newHcnError(windows.Errno(windows.HCN_E_NETWORK_NOT_FOUND), "test", ""), + checkFn: IsNetworkNotFoundError, + expected: true, + }, + { + name: "IsNetworkNotFoundError with non-matching error", + err: newHcnError(windows.Errno(windows.ERROR_NOT_FOUND), "test", ""), + checkFn: IsNetworkNotFoundError, + expected: false, + }, + + // IsEndpointNotFoundError + { + name: "IsEndpointNotFoundError with matching error", + err: newHcnError(windows.Errno(windows.HCN_E_ENDPOINT_NOT_FOUND), "test", ""), + checkFn: IsEndpointNotFoundError, + expected: true, + }, + { + name: "IsEndpointNotFoundError with non-matching error", + err: newHcnError(windows.Errno(windows.ERROR_NOT_FOUND), "test", ""), + checkFn: IsEndpointNotFoundError, + expected: false, + }, + + // IsPortNotFoundError + { + name: "IsPortNotFoundError with matching error", + err: newHcnError(windows.Errno(windows.HCN_E_PORT_NOT_FOUND), "test", ""), + checkFn: IsPortNotFoundError, + expected: true, + }, + { + name: "IsPortNotFoundError with non-matching error", + err: newHcnError(windows.Errno(windows.ERROR_NOT_FOUND), "test", ""), + checkFn: IsPortNotFoundError, + expected: false, + }, + + // IsInvalidIPError + { + name: "IsInvalidIPError with matching error", + err: newHcnError(windows.Errno(windows.HCN_E_INVALID_IP), "test", ""), + checkFn: IsInvalidIPError, + expected: true, + }, + { + name: "IsInvalidIPError with non-matching error", + err: newHcnError(windows.Errno(windows.ERROR_NOT_FOUND), "test", ""), + checkFn: IsInvalidIPError, + expected: false, + }, + + // IsAdapterNotFoundError + { + name: "IsAdapterNotFoundError with matching error", + err: newHcnError(windows.Errno(windows.HCN_E_ADAPTER_NOT_FOUND), "test", ""), + checkFn: IsAdapterNotFoundError, + expected: true, + }, + { + name: "IsAdapterNotFoundError with non-matching error", + err: newHcnError(windows.Errno(windows.ERROR_NOT_FOUND), "test", ""), + checkFn: IsAdapterNotFoundError, + expected: false, + }, + + // Non-HcnError + { + name: "non-HcnError returns false", + err: fmt.Errorf("random error"), + checkFn: IsPortAlreadyExistsError, + expected: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + if got := tc.checkFn(tc.err); got != tc.expected { + t.Errorf("expected %t, got %t for error: %v", tc.expected, got, tc.err) + } + // Also test wrapped error + wrapped := fmt.Errorf("wrapped: %w", tc.err) + if got := tc.checkFn(wrapped); got != tc.expected { + t.Errorf("expected %t for wrapped error, got %t for error: %v", tc.expected, got, wrapped) + } + }) + } +} diff --git a/hcn/hcnloadbalancer_test.go b/hcn/hcnloadbalancer_test.go index b6dbe20..8a6235e 100644 --- a/hcn/hcnloadbalancer_test.go +++ b/hcn/hcnloadbalancer_test.go @@ -1,7 +1,5 @@ -//go:build windows && integration_loadbalancer -// +build windows,integration_loadbalancer - -// TODO: https://github.com/microsoft/hnslib/issues/19 +//go:build windows && integration +// +build windows,integration package hcn @@ -72,7 +70,12 @@ func TestCreateUpdateDeleteLoadBalancer(t *testing.T) { loadBalancer, err = loadBalancer.Update(loadBalancer.Id) if err != nil { - t.Fatal(err) + if IsNotImplemented(err) { + t.Logf("LoadBalancer Update is not implemented on this version of Windows") + return + } else { + t.Fatal(err) + } } if len(loadBalancer.HostComputeEndpoints) != 2 { @@ -83,7 +86,11 @@ func TestCreateUpdateDeleteLoadBalancer(t *testing.T) { loadBalancer, err = loadBalancer.Update(loadBalancer.Id) if err != nil { - t.Fatal(err) + if IsNotImplemented(err) { + t.Logf("LoadBalancer Update is not implemented on this version of Windows") + } else { + t.Fatal(err) + } } if len(loadBalancer.HostComputeEndpoints) != 1 { @@ -210,7 +217,6 @@ func TestAddLoadBalancer(t *testing.T) { if err != nil { t.Fatal(err) } - loadBalancer, err := AddLoadBalancer([]HostComputeEndpoint{*endpoint}, LoadBalancerFlagsNone, LoadBalancerPortMappingFlagsNone, "10.0.0.1", []string{"1.1.1.2", "1.1.1.3"}, 6, 8080, 80) if err != nil { t.Fatal(err) diff --git a/hcn/hcntestconsts_test.go b/hcn/hcntestconsts_test.go new file mode 100644 index 0000000..d7b8d4d --- /dev/null +++ b/hcn/hcntestconsts_test.go @@ -0,0 +1,11 @@ +//go:build windows +// +build windows + +package hcn + +const ( + NatTestNetworkName string = "GoTestNat" + NatTestEndpointName string = "GoTestNatEndpoint" + OverlayTestNetworkName string = "GoTestOverlay" + BridgeTestNetworkName string = "GoTestL2Bridge" +) diff --git a/hcn/hcnutils_test.go b/hcn/hcnutils_test.go index 536f33f..9919630 100644 --- a/hcn/hcnutils_test.go +++ b/hcn/hcnutils_test.go @@ -1,10 +1,11 @@ -//go:build windows && integration -// +build windows,integration +//go:build windows +// +build windows package hcn import ( "encoding/json" + "time" ) func CreateSubnet(AddressPrefix string, NextHop string, DestPrefix string) *Subnet { @@ -35,6 +36,7 @@ func cleanup(networkName string) { return } } + time.Sleep(2 * time.Second) } func HcnGenerateNATNetwork(subnet *Subnet) *HostComputeNetwork { diff --git a/hcn/hnsv1_test.go b/hcn/hnsv1_test.go index 9090bbd..923365c 100644 --- a/hcn/hnsv1_test.go +++ b/hcn/hnsv1_test.go @@ -10,13 +10,6 @@ import ( "github.com/Microsoft/hnslib" ) -const ( - NatTestNetworkName string = "GoTestNat" - NatTestEndpointName string = "GoTestNatEndpoint" - OverlayTestNetworkName string = "GoTestOverlay" - BridgeTestNetworkName string = "GoTestL2Bridge" -) - func TestMain(m *testing.M) { os.Exit(m.Run()) }