Skip to content
Open
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
23 changes: 0 additions & 23 deletions api/v1/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -267,29 +267,6 @@ paths:
description: Inspector already running
'500':
description: Internal server error
patch:
summary: Add more VMs to inspection queue
operationId: addVMsToInspection
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/VMIdArray'
example: ["vm-1236", "vm-1237"]
responses:
'202':
description: VMs added to inspection queue
content:
application/json:
schema:
$ref: '#/components/schemas/InspectorStatus'
'400':
description: Invalid request
'404':
description: Inspector not running
'500':
description: Internal server error
delete:
summary: Stop inspector entirely
operationId: stopInspection
Expand Down
17 changes: 0 additions & 17 deletions api/v1/spec.gen.go

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

3 changes: 0 additions & 3 deletions api/v1/types.gen.go

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

7 changes: 0 additions & 7 deletions docs/filter-by-expression.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,13 +263,6 @@ Identifiers are **case-insensitive**. Dotted names refer to joined tables (e.g.
| `concern.category` | string | Category |
| `concern.assessment` | string | Assessment |

### vm_inspection_status (inspection.*)

| Identifier | Type | Description (backing column) |
|----------------------|--------|-----------------------------|
| `inspection.status` | string | Inspection status |
| `inspection.error` | string | Inspection error |

### vcpu (cpu.*) — CPU attributes

| Identifier | Type | Description (backing column) |
Expand Down
2 changes: 1 addition & 1 deletion internal/handlers/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@
//
// The byExpression parameter accepts a filter DSL expression that can reference
// any column across all joined tables (vinfo, vdisk, concerns, vcpu, vmemory,
// vnetwork, vdatastore, vm_inspection_status). See pkg/filter for the grammar
// vnetwork, vdatastore). See pkg/filter for the grammar
// and docs/filter-by-expression.md for field mappings and examples.
//
// Valid Sort Fields:
Expand Down
6 changes: 3 additions & 3 deletions internal/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ type VMService interface {
// InspectorService defines the interface for deep inspector operations.
type InspectorService interface {
Start(ctx context.Context, vmIDs []string, cred *models.Credentials) error
Add(ctx context.Context, vmIDs []string) error
Add(id string) error
GetStatus() models.InspectorStatus
GetVmStatus(ctx context.Context, id string) (models.InspectionStatus, error)
GetVmStatus(id string) (models.InspectionStatus, error)
IsBusy() bool
CancelVmsInspection(ctx context.Context, vmIDs ...string) error
Cancel(id string) error
Stop(ctx context.Context) error
}

Expand Down
6 changes: 3 additions & 3 deletions internal/handlers/handlers_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func (m *MockInspectorService) Start(ctx context.Context, vmIDs []string, cred *
return m.StartError
}

func (m *MockInspectorService) Add(ctx context.Context, vmIDs []string) error {
func (m *MockInspectorService) Add(id string) error {
m.AddCallCount++
return m.AddError
}
Expand All @@ -131,12 +131,12 @@ func (m *MockInspectorService) GetStatus() models.InspectorStatus {
return m.GetStatusResult
}

func (m *MockInspectorService) GetVmStatus(ctx context.Context, id string) (models.InspectionStatus, error) {
func (m *MockInspectorService) GetVmStatus(id string) (models.InspectionStatus, error) {
m.GetVmStatusCallCount++
return m.GetVmStatusResult, m.GetVmStatusError
}

func (m *MockInspectorService) CancelVmsInspection(ctx context.Context, vmIDs ...string) error {
func (m *MockInspectorService) Cancel(id string) error {
m.CancelVmsInspectionCallCount++
return m.CancelVmsInspectionError
}
Expand Down
39 changes: 9 additions & 30 deletions internal/handlers/vms.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ func (h *Handler) GetVMs(c *gin.Context, params v1.GetVMsParams) {
// Map to API response
apiVMs := make([]v1.VirtualMachine, 0, len(vms))
for _, vm := range vms {
s, err := h.inspectorSrv.GetVmStatus(vm.ID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to get VM status: %v", err)})
return
}
vm.Status = s
apiVMs = append(apiVMs, v1.NewVirtualMachineFromSummary(vm))
}

Expand Down Expand Up @@ -124,7 +130,7 @@ func (h *Handler) GetVM(c *gin.Context, id string) {
// GetVMInspectionStatus returns the inspection status for a specific VM
// (GET /vms/{id}/inspector)
func (h *Handler) GetVMInspectionStatus(c *gin.Context, id string) {
s, err := h.inspectorSrv.GetVmStatus(c.Request.Context(), id)
s, err := h.inspectorSrv.GetVmStatus(id)
if err != nil {
if srvErrors.IsResourceNotFoundError(err) {
c.JSON(http.StatusNotFound, v1.VmInspectionStatus{State: v1.VmInspectionStatusStateNotFound})
Expand All @@ -140,7 +146,7 @@ func (h *Handler) GetVMInspectionStatus(c *gin.Context, id string) {
// RemoveVMFromInspection removes VM from inspection queue
// (DELETE /vms/{id}/inspector)
func (h *Handler) RemoveVMFromInspection(c *gin.Context, id string) {
if err := h.inspectorSrv.CancelVmsInspection(c.Request.Context(), id); err != nil {
if err := h.inspectorSrv.Cancel(id); err != nil {
if srvErrors.IsInspectorNotRunningError(err) {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
Expand All @@ -149,7 +155,7 @@ func (h *Handler) RemoveVMFromInspection(c *gin.Context, id string) {
return
}

s, err := h.inspectorSrv.GetVmStatus(c.Request.Context(), id)
s, err := h.inspectorSrv.GetVmStatus(id)
if err != nil {
if srvErrors.IsResourceNotFoundError(err) {
c.JSON(http.StatusNotFound, v1.VmInspectionStatus{State: v1.VmInspectionStatusStateNotFound})
Expand Down Expand Up @@ -196,33 +202,6 @@ func (h *Handler) StartInspection(c *gin.Context) {
c.JSON(http.StatusAccepted, v1.InspectorStatus{State: v1.InspectorStatusStateInitiating})
}

// AddVMsToInspection adds more VMs to inspection queue
// (PATCH /vms/inspector)
func (h *Handler) AddVMsToInspection(c *gin.Context) {
var vmsMoid v1.VMIdArray
if err := c.ShouldBindJSON(&vmsMoid); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

if len(vmsMoid) == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "no vms provided"})
return
}

if err := h.inspectorSrv.Add(c.Request.Context(), vmsMoid); err != nil {
if srvErrors.IsInspectorNotRunningError(err) {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

c.JSON(http.StatusAccepted, v1.NewInspectorStatus(h.inspectorSrv.GetStatus()))

}

// StopInspection stops inspector entirely
// (DELETE /vms/inspector)
func (h *Handler) StopInspection(c *gin.Context) {
Expand Down
115 changes: 14 additions & 101 deletions internal/handlers/vms_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ var _ = Describe("VMs Handlers", func() {
})
router.GET("/vms/inspector", handler.GetInspectorStatus)
router.POST("/vms/inspector", handler.StartInspection)
router.PATCH("/vms/inspector", handler.AddVMsToInspection)
router.DELETE("/vms/inspector", handler.StopInspection)
router.GET("/vms/:id/inspector", func(c *gin.Context) {
handler.GetVMInspectionStatus(c, c.Param("id"))
Expand Down Expand Up @@ -445,65 +444,6 @@ var _ = Describe("VMs Handlers", func() {
Expect(response.State).To(Equal(v1.InspectorStatusStateInitiating))
})

// Given an invalid JSON request body
// When we try to add VMs to inspection
// Then it should return 400 Bad Request
It("AddVMsToInspection should return 400 for invalid JSON", func() {
// Arrange
req := httptest.NewRequest(http.MethodPatch, "/vms/inspector", strings.NewReader("invalid json"))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()

// Act
router.ServeHTTP(w, req)

// Assert
Expect(w.Code).To(Equal(http.StatusBadRequest))
var body map[string]any
Expect(json.Unmarshal(w.Body.Bytes(), &body)).To(Succeed())
Expect(body["error"]).NotTo(BeEmpty())
})

// Given an empty VM list in the request
// When we try to add VMs to inspection
// Then it should return 400 Bad Request
It("AddVMsToInspection should return 400 for empty VM list", func() {
// Arrange
reqBody := `[]`
req := httptest.NewRequest(http.MethodPatch, "/vms/inspector", strings.NewReader(reqBody))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()

// Act
router.ServeHTTP(w, req)

// Assert
Expect(w.Code).To(Equal(http.StatusBadRequest))
var body map[string]any
Expect(json.Unmarshal(w.Body.Bytes(), &body)).To(Succeed())
Expect(body["error"]).To(Equal("no vms provided"))
})

// Given a running inspector and a valid VM list
// When we add VMs to inspection
// Then it should return 202 Accepted
It("AddVMsToInspection should add VMs successfully", func() {
// Arrange
mockInspector.GetStatusResult = models.InspectorStatus{
State: models.InspectorStateRunning,
}
body := `["vm-1","vm-2"]`
req := httptest.NewRequest(http.MethodPatch, "/vms/inspector", strings.NewReader(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()

// Act
router.ServeHTTP(w, req)

// Assert
Expect(w.Code).To(Equal(http.StatusAccepted))
})

// Given a running inspector
// When we stop the inspection
// Then it should return 202 Accepted
Expand Down Expand Up @@ -611,7 +551,7 @@ var _ = Describe("VMs Handlers", func() {
Expect(response.State).To(Equal(v1.VmInspectionStatusStateCanceled))
})

// Given CancelVmsInspection returns an error
// Given Cancel returns an error
// When we remove a VM from inspection
// Then it should return 500 Internal Server Error
It("RemoveVMFromInspection should return 500 when cancel fails", func() {
Expand Down Expand Up @@ -700,24 +640,6 @@ var _ = Describe("VMs Handlers", func() {
Expect(body["error"]).To(Equal("failed to start inspector: start failed"))
})

It("AddVMsToInspection should return 400 when add fails", func() {
// Arrange
mockInspector.AddError = errors.New("inspector not running")
reqBody := `["vm-1","vm-2"]`
req := httptest.NewRequest(http.MethodPatch, "/vms/inspector", strings.NewReader(reqBody))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()

// Act
router.ServeHTTP(w, req)

// Assert
Expect(w.Code).To(Equal(http.StatusBadRequest))
var body map[string]any
Expect(json.Unmarshal(w.Body.Bytes(), &body)).To(Succeed())
Expect(body["error"]).To(Equal("inspector not running"))
})

It("RemoveVMFromInspection should return 404 when inspector not running", func() {
mockInspector.CancelVmsInspectionError = srvErrors.NewInspectorNotRunningError()

Expand All @@ -732,21 +654,6 @@ var _ = Describe("VMs Handlers", func() {
Expect(body["error"]).To(Equal("inspector not running"))
})

It("AddVMsToInspection should return 404 when inspector not running", func() {
mockInspector.AddError = srvErrors.NewInspectorNotRunningError()
body := `["vm-1","vm-2"]`
req := httptest.NewRequest(http.MethodPatch, "/vms/inspector", strings.NewReader(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()

router.ServeHTTP(w, req)

Expect(w.Code).To(Equal(http.StatusNotFound))
var respBody map[string]any
Expect(json.Unmarshal(w.Body.Bytes(), &respBody)).To(Succeed())
Expect(respBody["error"]).To(Equal("inspector not running"))
})

It("StopInspection should return 404 when inspector not running", func() {
mockInspector.StopError = srvErrors.NewInspectorNotRunningError()

Expand Down Expand Up @@ -804,12 +711,13 @@ var _ = Describe("Version Handler", func() {

var _ = Describe("VMs Handlers Integration", func() {
var (
ctx context.Context
db *sql.DB
st *store.Store
vmSrv *services.VMService
handler *handlers.Handler
router *gin.Engine
ctx context.Context
db *sql.DB
st *store.Store
vmSrv *services.VMService
mockInspector *MockInspectorService
handler *handlers.Handler
router *gin.Engine
)

BeforeEach(func() {
Expand All @@ -831,7 +739,12 @@ var _ = Describe("VMs Handlers Integration", func() {
Expect(err).NotTo(HaveOccurred())

vmSrv = services.NewVMService(st)
handler = handlers.NewHandler(config.Configuration{}).WithVMService(vmSrv)
mockInspector = &MockInspectorService{
GetVmStatusResult: models.InspectionStatus{State: models.InspectionStateNotFound},
}
handler = handlers.NewHandler(config.Configuration{}).
WithVMService(vmSrv).
WithInspectorService(mockInspector)
router = gin.New()
router.GET("/vms", func(c *gin.Context) {
var params v1.GetVMsParams
Expand Down
4 changes: 4 additions & 0 deletions internal/models/inspection.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ type InspectionStatus struct {
State InspectionState
Error error
}

// InspectionResult is the shared result struct threaded through inspection work units.
// InspectionResult Todo: pass here data between inspection phase to saving step
type InspectionResult struct{}
Loading
Loading