diff --git a/pkg/kvm/blk_test.go b/pkg/kvm/blk_test.go index f81d37df..6eaabfbe 100644 --- a/pkg/kvm/blk_test.go +++ b/pkg/kvm/blk_test.go @@ -7,7 +7,6 @@ package kvm import ( "bytes" "context" - "errors" "testing" "github.com/opiproject/gospdk/spdk" @@ -15,6 +14,8 @@ import ( pb "github.com/opiproject/opi-api/storage/v1alpha1/gen/go" "github.com/opiproject/opi-spdk-bridge/pkg/frontend" "github.com/opiproject/opi-spdk-bridge/pkg/server" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" ) @@ -36,7 +37,8 @@ func TestCreateVirtioBlk(t *testing.T) { tests := map[string]struct { jsonRPC spdk.JSONRPC - expectError error + errCode codes.Code + errMsg string nonDefaultQmpAddress string buses []string @@ -55,21 +57,24 @@ func TestCreateVirtioBlk(t *testing.T) { ExpectQueryPci(testVirtioBlkID), }, "spdk failed to create virtio-blk": { - in: testCreateVirtioBlkRequest, - jsonRPC: alwaysFailingJSONRPC, - expectError: errStub, + in: testCreateVirtioBlkRequest, + jsonRPC: alwaysFailingJSONRPC, + errCode: status.Convert(errStub).Code(), + errMsg: status.Convert(errStub).Message(), }, "qemu chardev add failed": { - in: testCreateVirtioBlkRequest, - jsonRPC: alwaysSuccessfulJSONRPC, - expectError: errAddChardevFailed, + in: testCreateVirtioBlkRequest, + jsonRPC: alwaysSuccessfulJSONRPC, + errCode: status.Convert(errAddChardevFailed).Code(), + errMsg: status.Convert(errAddChardevFailed).Message(), mockQmpCalls: newMockQmpCalls(). ExpectAddChardev(testVirtioBlkID).WithErrorResponse(), }, "qemu device add failed": { - in: testCreateVirtioBlkRequest, - jsonRPC: alwaysSuccessfulJSONRPC, - expectError: errAddDeviceFailed, + in: testCreateVirtioBlkRequest, + jsonRPC: alwaysSuccessfulJSONRPC, + errCode: status.Convert(errAddDeviceFailed).Code(), + errMsg: status.Convert(errAddDeviceFailed).Message(), mockQmpCalls: newMockQmpCalls(). ExpectAddChardev(testVirtioBlkID). ExpectAddVirtioBlk(testVirtioBlkID, testVirtioBlkID).WithErrorResponse(). @@ -79,7 +84,8 @@ func TestCreateVirtioBlk(t *testing.T) { in: testCreateVirtioBlkRequest, nonDefaultQmpAddress: "/dev/null", jsonRPC: alwaysSuccessfulJSONRPC, - expectError: errMonitorCreation, + errCode: status.Convert(errMonitorCreation).Code(), + errMsg: status.Convert(errMonitorCreation).Message(), }, "valid virtio-blk creation with on first bus location": { in: &pb.CreateVirtioBlkRequest{VirtioBlk: &pb.VirtioBlk{ @@ -111,11 +117,12 @@ func TestCreateVirtioBlk(t *testing.T) { ExpectQueryPci(testVirtioBlkID), }, "virtio-blk creation with physical function goes out of buses": { - in: testCreateVirtioBlkRequest, - out: nil, - expectError: errDeviceEndpoint, - jsonRPC: alwaysSuccessfulJSONRPC, - buses: []string{"pci.opi.0"}, + in: testCreateVirtioBlkRequest, + out: nil, + errCode: status.Convert(errDeviceEndpoint).Code(), + errMsg: status.Convert(errDeviceEndpoint).Message(), + jsonRPC: alwaysSuccessfulJSONRPC, + buses: []string{"pci.opi.0"}, }, "negative physical function": { in: &pb.CreateVirtioBlkRequest{VirtioBlk: &pb.VirtioBlk{ @@ -123,10 +130,11 @@ func TestCreateVirtioBlk(t *testing.T) { VolumeId: &pc.ObjectKey{Value: "Malloc42"}, MaxIoQps: 1, }, VirtioBlkId: testVirtioBlkID}, - out: nil, - expectError: errDeviceEndpoint, - jsonRPC: alwaysSuccessfulJSONRPC, - buses: []string{"pci.opi.0"}, + out: nil, + errCode: status.Convert(errDeviceEndpoint).Code(), + errMsg: status.Convert(errDeviceEndpoint).Message(), + jsonRPC: alwaysSuccessfulJSONRPC, + buses: []string{"pci.opi.0"}, }, "nil pcie endpoint": { in: &pb.CreateVirtioBlkRequest{VirtioBlk: &pb.VirtioBlk{ @@ -134,9 +142,10 @@ func TestCreateVirtioBlk(t *testing.T) { VolumeId: &pc.ObjectKey{Value: "Malloc42"}, MaxIoQps: 1, }, VirtioBlkId: testVirtioBlkID}, - out: nil, - expectError: errNoPcieEndpoint, - jsonRPC: alwaysSuccessfulJSONRPC, + out: nil, + errCode: status.Convert(errNoPcieEndpoint).Code(), + errMsg: status.Convert(errNoPcieEndpoint).Message(), + jsonRPC: alwaysSuccessfulJSONRPC, }, } @@ -154,9 +163,18 @@ func TestCreateVirtioBlk(t *testing.T) { request := server.ProtoClone(test.in) out, err := kvmServer.CreateVirtioBlk(context.Background(), request) - if !errors.Is(err, test.expectError) { - t.Errorf("Expected error %v, got %v", test.expectError, err) + + if er, ok := status.FromError(err); ok { + if er.Code() != test.errCode { + t.Error("error code: expected", test.errCode, "received", er.Code()) + } + if er.Message() != test.errMsg { + t.Error("error message: expected", test.errMsg, "received", er.Message()) + } + } else { + t.Errorf("expected grpc error status") } + gotOut, _ := proto.Marshal(out) wantOut, _ := proto.Marshal(test.out) if !bytes.Equal(gotOut, wantOut) { @@ -172,7 +190,8 @@ func TestCreateVirtioBlk(t *testing.T) { func TestDeleteVirtioBlk(t *testing.T) { tests := map[string]struct { jsonRPC spdk.JSONRPC - expectError error + errCode codes.Code + errMsg string nonDefaultQmpAddress string mockQmpCalls *mockQmpCalls @@ -184,36 +203,41 @@ func TestDeleteVirtioBlk(t *testing.T) { ExpectDeleteChardev(testVirtioBlkID), }, "qemu device delete failed": { - jsonRPC: alwaysSuccessfulJSONRPC, - expectError: errDevicePartiallyDeleted, + jsonRPC: alwaysSuccessfulJSONRPC, + errCode: status.Convert(errDevicePartiallyDeleted).Code(), + errMsg: status.Convert(errDevicePartiallyDeleted).Message(), mockQmpCalls: newMockQmpCalls(). ExpectDeleteVirtioBlk(testVirtioBlkID).WithErrorResponse(). ExpectDeleteChardev(testVirtioBlkID), }, "qemu device delete failed by timeout": { - jsonRPC: alwaysSuccessfulJSONRPC, - expectError: errDevicePartiallyDeleted, + jsonRPC: alwaysSuccessfulJSONRPC, + errCode: status.Convert(errDevicePartiallyDeleted).Code(), + errMsg: status.Convert(errDevicePartiallyDeleted).Message(), mockQmpCalls: newMockQmpCalls(). ExpectDeleteVirtioBlk(testVirtioBlkID). ExpectDeleteChardev(testVirtioBlkID), }, "qemu chardev delete failed": { - jsonRPC: alwaysSuccessfulJSONRPC, - expectError: errDevicePartiallyDeleted, + jsonRPC: alwaysSuccessfulJSONRPC, + errCode: status.Convert(errDevicePartiallyDeleted).Code(), + errMsg: status.Convert(errDevicePartiallyDeleted).Message(), mockQmpCalls: newMockQmpCalls(). ExpectDeleteVirtioBlkWithEvent(testVirtioBlkID). ExpectDeleteChardev(testVirtioBlkID).WithErrorResponse(), }, "spdk failed to delete virtio-blk": { - jsonRPC: alwaysFailingJSONRPC, - expectError: errDevicePartiallyDeleted, + jsonRPC: alwaysFailingJSONRPC, + errCode: status.Convert(errDevicePartiallyDeleted).Code(), + errMsg: status.Convert(errDevicePartiallyDeleted).Message(), mockQmpCalls: newMockQmpCalls(). ExpectDeleteVirtioBlkWithEvent(testVirtioBlkID). ExpectDeleteChardev(testVirtioBlkID), }, "all qemu and spdk calls failed": { - jsonRPC: alwaysFailingJSONRPC, - expectError: errDeviceNotDeleted, + jsonRPC: alwaysFailingJSONRPC, + errCode: status.Convert(errDeviceNotDeleted).Code(), + errMsg: status.Convert(errDeviceNotDeleted).Message(), mockQmpCalls: newMockQmpCalls(). ExpectDeleteVirtioBlk(testVirtioBlkID).WithErrorResponse(). ExpectDeleteChardev(testVirtioBlkID).WithErrorResponse(), @@ -221,7 +245,8 @@ func TestDeleteVirtioBlk(t *testing.T) { "failed to create monitor": { nonDefaultQmpAddress: "/dev/null", jsonRPC: alwaysSuccessfulJSONRPC, - expectError: errMonitorCreation, + errCode: status.Convert(errMonitorCreation).Code(), + errMsg: status.Convert(errMonitorCreation).Message(), }, } @@ -242,9 +267,18 @@ func TestDeleteVirtioBlk(t *testing.T) { request := server.ProtoClone(testDeleteVirtioBlkRequest) _, err := kvmServer.DeleteVirtioBlk(context.Background(), request) - if !errors.Is(err, test.expectError) { - t.Errorf("Expected %v, got %v", test.expectError, err) + + if er, ok := status.FromError(err); ok { + if er.Code() != test.errCode { + t.Error("error code: expected", test.errCode, "received", er.Code()) + } + if er.Message() != test.errMsg { + t.Error("error message: expected", test.errMsg, "received", er.Message()) + } + } else { + t.Errorf("expected grpc error status") } + if !qmpServer.WereExpectedCallsPerformed() { t.Errorf("Not all expected calls were performed") } diff --git a/pkg/kvm/kvm_test.go b/pkg/kvm/kvm_test.go index 039ea97d..21c1ebe1 100644 --- a/pkg/kvm/kvm_test.go +++ b/pkg/kvm/kvm_test.go @@ -5,7 +5,6 @@ package kvm import ( - "errors" "fmt" "log" "net" @@ -18,10 +17,12 @@ import ( "time" "github.com/opiproject/gospdk/spdk" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) var ( - errStub = errors.New("stub error") + errStub = status.Error(codes.Internal, "stub error") alwaysSuccessfulJSONRPC = stubJSONRRPC{nil} alwaysFailingJSONRPC = stubJSONRRPC{errStub} diff --git a/pkg/kvm/nvme_test.go b/pkg/kvm/nvme_test.go index d091068d..d0237d5b 100644 --- a/pkg/kvm/nvme_test.go +++ b/pkg/kvm/nvme_test.go @@ -7,7 +7,6 @@ package kvm import ( "bytes" "context" - "errors" "log" "os" "path/filepath" @@ -19,6 +18,8 @@ import ( pb "github.com/opiproject/opi-api/storage/v1alpha1/gen/go" "github.com/opiproject/opi-spdk-bridge/pkg/frontend" "github.com/opiproject/opi-spdk-bridge/pkg/server" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" ) @@ -126,9 +127,10 @@ func TestCreateNvmeController(t *testing.T) { ctrlrDirExistsAfterOperation bool buses []string - in *pb.CreateNvmeControllerRequest - out *pb.NvmeController - expectError error + in *pb.CreateNvmeControllerRequest + out *pb.NvmeController + errCode codes.Code + errMsg string mockQmpCalls *mockQmpCalls }{ @@ -138,7 +140,8 @@ func TestCreateNvmeController(t *testing.T) { jsonRPC: alwaysSuccessfulJSONRPC, ctrlrDirExistsBeforeOperation: false, ctrlrDirExistsAfterOperation: true, - expectError: nil, + errCode: codes.OK, + errMsg: "", mockQmpCalls: newMockQmpCalls(). ExpectAddNvmeController(testNvmeControllerID, testSubsystemID). ExpectQueryPci(testNvmeControllerID), @@ -148,14 +151,16 @@ func TestCreateNvmeController(t *testing.T) { jsonRPC: alwaysFailingJSONRPC, ctrlrDirExistsBeforeOperation: false, ctrlrDirExistsAfterOperation: false, - expectError: errStub, + errCode: status.Convert(errStub).Code(), + errMsg: status.Convert(errStub).Message(), }, "qemu Nvme controller add failed": { in: testCreateNvmeControllerRequest, jsonRPC: alwaysSuccessfulJSONRPC, ctrlrDirExistsBeforeOperation: false, ctrlrDirExistsAfterOperation: false, - expectError: errAddDeviceFailed, + errCode: status.Convert(errAddDeviceFailed).Code(), + errMsg: status.Convert(errAddDeviceFailed).Message(), mockQmpCalls: newMockQmpCalls(). ExpectAddNvmeController(testNvmeControllerID, testSubsystemID).WithErrorResponse(), }, @@ -165,14 +170,16 @@ func TestCreateNvmeController(t *testing.T) { jsonRPC: alwaysSuccessfulJSONRPC, ctrlrDirExistsBeforeOperation: false, ctrlrDirExistsAfterOperation: false, - expectError: errMonitorCreation, + errCode: status.Convert(errMonitorCreation).Code(), + errMsg: status.Convert(errMonitorCreation).Message(), }, "Ctrlr dir already exists": { in: testCreateNvmeControllerRequest, jsonRPC: alwaysSuccessfulJSONRPC, ctrlrDirExistsBeforeOperation: true, ctrlrDirExistsAfterOperation: true, - expectError: errFailedToCreateNvmeDir, + errCode: status.Convert(errFailedToCreateNvmeDir).Code(), + errMsg: status.Convert(errFailedToCreateNvmeDir).Message(), }, "empty subsystem in request": { in: &pb.CreateNvmeControllerRequest{NvmeController: &pb.NvmeController{ @@ -188,7 +195,8 @@ func TestCreateNvmeController(t *testing.T) { jsonRPC: alwaysSuccessfulJSONRPC, ctrlrDirExistsBeforeOperation: false, ctrlrDirExistsAfterOperation: false, - expectError: errInvalidSubsystem, + errCode: status.Convert(errInvalidSubsystem).Code(), + errMsg: status.Convert(errInvalidSubsystem).Message(), }, "valid Nvme creation with on first bus location": { in: &pb.CreateNvmeControllerRequest{NvmeController: &pb.NvmeController{ @@ -232,11 +240,12 @@ func TestCreateNvmeController(t *testing.T) { ExpectQueryPci(testNvmeControllerID), }, "Nvme creation with physical function goes out of buses": { - in: testCreateNvmeControllerRequest, - out: nil, - expectError: errDeviceEndpoint, - jsonRPC: alwaysSuccessfulJSONRPC, - buses: []string{"pci.opi.0"}, + in: testCreateNvmeControllerRequest, + out: nil, + errCode: status.Convert(errDeviceEndpoint).Code(), + errMsg: status.Convert(errDeviceEndpoint).Message(), + jsonRPC: alwaysSuccessfulJSONRPC, + buses: []string{"pci.opi.0"}, }, "negative physical function": { in: &pb.CreateNvmeControllerRequest{ @@ -252,10 +261,11 @@ func TestCreateNvmeController(t *testing.T) { Active: true, }, }, NvmeControllerId: testNvmeControllerID}, - out: nil, - expectError: errDeviceEndpoint, - jsonRPC: alwaysSuccessfulJSONRPC, - buses: []string{"pci.opi.0"}, + out: nil, + errCode: status.Convert(errDeviceEndpoint).Code(), + errMsg: status.Convert(errDeviceEndpoint).Message(), + jsonRPC: alwaysSuccessfulJSONRPC, + buses: []string{"pci.opi.0"}, }, "nil pcie endpoint": { in: &pb.CreateNvmeControllerRequest{ @@ -269,9 +279,10 @@ func TestCreateNvmeController(t *testing.T) { Active: true, }, }, NvmeControllerId: testNvmeControllerID}, - out: nil, - expectError: errNoPcieEndpoint, - jsonRPC: alwaysSuccessfulJSONRPC, + out: nil, + errCode: status.Convert(errNoPcieEndpoint).Code(), + errMsg: status.Convert(errNoPcieEndpoint).Message(), + jsonRPC: alwaysSuccessfulJSONRPC, }, } @@ -295,9 +306,17 @@ func TestCreateNvmeController(t *testing.T) { request := server.ProtoClone(test.in) out, err := kvmServer.CreateNvmeController(context.Background(), request) - if !errors.Is(err, test.expectError) { - t.Errorf("Expected error %v, got %v", test.expectError, err) + if er, ok := status.FromError(err); ok { + if er.Code() != test.errCode { + t.Error("error code: expected", test.errCode, "received", er.Code()) + } + if er.Message() != test.errMsg { + t.Error("error message: expected", test.errMsg, "received", er.Message()) + } + } else { + t.Errorf("expected grpc error status") } + gotOut, _ := proto.Marshal(out) wantOut, _ := proto.Marshal(test.out) if !bytes.Equal(gotOut, wantOut) { @@ -323,7 +342,8 @@ func TestDeleteNvmeController(t *testing.T) { ctrlrDirExistsAfterOperation bool nonEmptyCtrlrDirAfterSpdkCall bool noController bool - expectError error + errCode codes.Code + errMsg string mockQmpCalls *mockQmpCalls }{ @@ -332,7 +352,8 @@ func TestDeleteNvmeController(t *testing.T) { ctrlrDirExistsBeforeOperation: true, ctrlrDirExistsAfterOperation: false, nonEmptyCtrlrDirAfterSpdkCall: false, - expectError: nil, + errCode: codes.OK, + errMsg: "", mockQmpCalls: newMockQmpCalls(). ExpectDeleteNvmeController(testNvmeControllerID). ExpectNoDeviceQueryPci(), @@ -342,7 +363,8 @@ func TestDeleteNvmeController(t *testing.T) { ctrlrDirExistsBeforeOperation: true, ctrlrDirExistsAfterOperation: false, nonEmptyCtrlrDirAfterSpdkCall: false, - expectError: errDevicePartiallyDeleted, + errCode: status.Convert(errDevicePartiallyDeleted).Code(), + errMsg: status.Convert(errDevicePartiallyDeleted).Message(), mockQmpCalls: newMockQmpCalls(). ExpectDeleteNvmeController(testNvmeControllerID).WithErrorResponse(), }, @@ -351,7 +373,8 @@ func TestDeleteNvmeController(t *testing.T) { ctrlrDirExistsBeforeOperation: true, ctrlrDirExistsAfterOperation: false, nonEmptyCtrlrDirAfterSpdkCall: false, - expectError: errDevicePartiallyDeleted, + errCode: status.Convert(errDevicePartiallyDeleted).Code(), + errMsg: status.Convert(errDevicePartiallyDeleted).Message(), mockQmpCalls: newMockQmpCalls(). ExpectDeleteNvmeController(testNvmeControllerID). ExpectNoDeviceQueryPci(), @@ -362,14 +385,16 @@ func TestDeleteNvmeController(t *testing.T) { ctrlrDirExistsBeforeOperation: true, ctrlrDirExistsAfterOperation: true, nonEmptyCtrlrDirAfterSpdkCall: false, - expectError: errMonitorCreation, + errCode: status.Convert(errMonitorCreation).Code(), + errMsg: status.Convert(errMonitorCreation).Message(), }, "ctrlr dir is not empty after SPDK call": { jsonRPC: alwaysSuccessfulJSONRPC, ctrlrDirExistsBeforeOperation: true, ctrlrDirExistsAfterOperation: true, nonEmptyCtrlrDirAfterSpdkCall: true, - expectError: errDevicePartiallyDeleted, + errCode: status.Convert(errDevicePartiallyDeleted).Code(), + errMsg: status.Convert(errDevicePartiallyDeleted).Message(), mockQmpCalls: newMockQmpCalls(). ExpectDeleteNvmeController(testNvmeControllerID). ExpectNoDeviceQueryPci(), @@ -379,7 +404,8 @@ func TestDeleteNvmeController(t *testing.T) { ctrlrDirExistsBeforeOperation: false, ctrlrDirExistsAfterOperation: false, nonEmptyCtrlrDirAfterSpdkCall: false, - expectError: nil, + errCode: codes.OK, + errMsg: "", mockQmpCalls: newMockQmpCalls(). ExpectDeleteNvmeController(testNvmeControllerID). ExpectNoDeviceQueryPci(), @@ -389,7 +415,8 @@ func TestDeleteNvmeController(t *testing.T) { ctrlrDirExistsBeforeOperation: true, ctrlrDirExistsAfterOperation: true, nonEmptyCtrlrDirAfterSpdkCall: true, - expectError: errDeviceNotDeleted, + errCode: status.Convert(errDeviceNotDeleted).Code(), + errMsg: status.Convert(errDeviceNotDeleted).Message(), mockQmpCalls: newMockQmpCalls(). ExpectDeleteNvmeController(testNvmeControllerID).WithErrorResponse(), }, @@ -399,7 +426,8 @@ func TestDeleteNvmeController(t *testing.T) { ctrlrDirExistsAfterOperation: true, nonEmptyCtrlrDirAfterSpdkCall: false, noController: true, - expectError: errNoController, + errCode: status.Convert(errNoController).Code(), + errMsg: status.Convert(errNoController).Message(), }, } @@ -435,9 +463,18 @@ func TestDeleteNvmeController(t *testing.T) { request := server.ProtoClone(testDeleteNvmeControllerRequest) _, err := kvmServer.DeleteNvmeController(context.Background(), request) - if !errors.Is(err, test.expectError) { - t.Errorf("Expected error %v, got %v", test.expectError, err) + + if er, ok := status.FromError(err); ok { + if er.Code() != test.errCode { + t.Error("error code: expected", test.errCode, "received", er.Code()) + } + if er.Message() != test.errMsg { + t.Error("error message: expected", test.errMsg, "received", er.Message()) + } + } else { + t.Errorf("expected grpc error status") } + if !qmpServer.WereExpectedCallsPerformed() { t.Errorf("Not all expected calls were performed") }