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
3 changes: 2 additions & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ coverage:
range: 25...100
# Specify files or directories to ignore
ignore:
- "internal/usecase/devices/wsman/*"
- "internal/usecase/devices/wsman/*"
- "internal/mocks/*"
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ require (
github.com/ilyakaznacheev/cleanenv v1.5.0
github.com/jackc/pgx/v5 v5.9.2
github.com/prometheus/client_golang v1.23.2
github.com/rs/zerolog v1.35.1
github.com/rs/zerolog v1.35.0
github.com/stretchr/testify v1.11.1
github.com/zsais/go-gin-prometheus v1.0.3
go.uber.org/mock v0.6.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI=
github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw=
github.com/rs/zerolog v1.35.0 h1:VD0ykx7HMiMJytqINBsKcbLS+BJ4WYjz+05us+LRTdI=
github.com/rs/zerolog v1.35.0/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
Expand Down
43 changes: 43 additions & 0 deletions internal/controller/httpapi/v1/boot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package v1

import (
"net/http"

"github.com/gin-gonic/gin"

"github.com/device-management-toolkit/console/internal/entity/dto/v1"
)

func (r *deviceManagementRoutes) getRemoteEraseCapabilities(c *gin.Context) {
guid := c.Param("guid")

capabilities, err := r.d.GetRemoteEraseCapabilities(c.Request.Context(), guid)
if err != nil {
r.l.Error(err, "http - v1 - getRemoteEraseCapabilities")
ErrorResponse(c, err)

return
}

c.JSON(http.StatusOK, capabilities)
}

func (r *deviceManagementRoutes) setRemoteEraseOptions(c *gin.Context) {
guid := c.Param("guid")

var req dto.RemoteEraseRequest
if err := c.ShouldBindJSON(&req); err != nil {
ErrorResponse(c, err)

return
}

if err := r.d.SetRemoteEraseOptions(c.Request.Context(), guid, req.EraseMask); err != nil {
r.l.Error(err, "http - v1 - setRemoteEraseOptions")
ErrorResponse(c, err)

return
}

c.JSON(http.StatusOK, nil)
}
126 changes: 126 additions & 0 deletions internal/controller/httpapi/v1/boot_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package v1

import (
"bytes"
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/require"

"github.com/device-management-toolkit/console/internal/entity/dto/v1"
"github.com/device-management-toolkit/console/internal/mocks"
)

func TestGetRemoteEraseCapabilities(t *testing.T) {
t.Parallel()

tests := []struct {
name string
mock func(m *mocks.MockDeviceManagementFeature)
expectedCode int
response interface{}
}{
{
name: "getRemoteEraseCapabilities - successful retrieval",
mock: func(m *mocks.MockDeviceManagementFeature) {
m.EXPECT().GetRemoteEraseCapabilities(context.Background(), "valid-guid").
Return(dto.BootCapabilities{PlatformErase: 1}, nil)
},
expectedCode: http.StatusOK,
response: dto.BootCapabilities{PlatformErase: 1},
},
{
name: "getRemoteEraseCapabilities - service failure",
mock: func(m *mocks.MockDeviceManagementFeature) {
m.EXPECT().GetRemoteEraseCapabilities(context.Background(), "valid-guid").
Return(dto.BootCapabilities{}, ErrGeneral)
},
expectedCode: http.StatusInternalServerError,
response: nil,
},
}

for _, tc := range tests {
tc := tc

t.Run(tc.name, func(t *testing.T) {
t.Parallel()

deviceManagement, engine := deviceManagementTest(t)
tc.mock(deviceManagement)

req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/api/v1/amt/boot/remoteErase/valid-guid", http.NoBody)
require.NoError(t, err)

w := httptest.NewRecorder()
engine.ServeHTTP(w, req)

require.Equal(t, tc.expectedCode, w.Code)

if tc.expectedCode == http.StatusOK {
jsonBytes, _ := json.Marshal(tc.response)
require.Equal(t, string(jsonBytes), w.Body.String())
}
})
}
}

func TestSetRemoteEraseOptions(t *testing.T) {
t.Parallel()

tests := []struct {
name string
requestBody interface{}
mock func(m *mocks.MockDeviceManagementFeature)
expectedCode int
}{
{
name: "setRemoteEraseOptions - successful",
requestBody: dto.RemoteEraseRequest{EraseMask: 3},
mock: func(m *mocks.MockDeviceManagementFeature) {
m.EXPECT().SetRemoteEraseOptions(context.Background(), "valid-guid", 3).
Return(nil)
},
expectedCode: http.StatusOK,
},
{
name: "setRemoteEraseOptions - invalid JSON payload",
requestBody: "invalid-json",
mock: func(_ *mocks.MockDeviceManagementFeature) {
},
expectedCode: http.StatusInternalServerError,
},
{
name: "setRemoteEraseOptions - service failure",
requestBody: dto.RemoteEraseRequest{EraseMask: 1},
mock: func(m *mocks.MockDeviceManagementFeature) {
m.EXPECT().SetRemoteEraseOptions(context.Background(), "valid-guid", 1).
Return(ErrGeneral)
},
expectedCode: http.StatusInternalServerError,
},
}

for _, tc := range tests {
tc := tc

t.Run(tc.name, func(t *testing.T) {
t.Parallel()

deviceManagement, engine := deviceManagementTest(t)
tc.mock(deviceManagement)

reqBody, _ := json.Marshal(tc.requestBody)
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, "/api/v1/amt/boot/remoteErase/valid-guid", bytes.NewBuffer(reqBody))
require.NoError(t, err)

w := httptest.NewRecorder()
engine.ServeHTTP(w, req)

require.Equal(t, tc.expectedCode, w.Code)
})
}
}
2 changes: 2 additions & 0 deletions internal/controller/httpapi/v1/devicemanagement.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ func NewAmtRoutes(handler *gin.RouterGroup, d devices.Feature, amt amtexplorer.F
h.POST("alarmOccurrences/:guid", r.createAlarmOccurrences)
h.DELETE("alarmOccurrences/:guid", r.deleteAlarmOccurrences)

h.GET("boot/remoteErase/:guid", r.getRemoteEraseCapabilities)
h.POST("boot/remoteErase/:guid", r.setRemoteEraseOptions)
h.GET("hardwareInfo/:guid", r.getHardwareInfo)
h.GET("diskInfo/:guid", r.getDiskInfo)
h.GET("power/state/:guid", r.getPowerState)
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/httpapi/v1/devicemanagement_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func TestDeviceManagement(t *testing.T) {
OCR: false,
OptInState: 0,
Redirection: false,
RemoteErase: false,
RPE: false,
UserConsent: "",
WinREBootSupported: false,
},
Expand Down
3 changes: 2 additions & 1 deletion internal/controller/httpapi/v1/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ func (r *deviceManagementRoutes) getFeatures(c *gin.Context) {
HTTPSBootSupported: features.HTTPSBootSupported,
WinREBootSupported: features.WinREBootSupported,
LocalPBABootSupported: features.LocalPBABootSupported,
RemoteErase: features.RemoteErase,
RPE: features.RPE,
RPESupported: features.RPESupported,
}

c.JSON(http.StatusOK, v1Features)
Expand Down
11 changes: 11 additions & 0 deletions internal/controller/openapi/devicemanagement.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,13 @@ func (f *FuegoAdapter) registerPowerRoutes() {
fuego.OptionPath("guid", "Device GUID"),
protectedRouteOptions(),
)

fuego.Get(f.server, "/api/v1/admin/amt/boot/capabilities/{guid}", f.getBootCapabilities,
fuego.OptionTags("Device Management"),
fuego.OptionSummary("Get Boot Capabilities"),
fuego.OptionDescription("Read AMT_BootCapabilities.PlatformErase to determine Remote Platform Erase (RPE) support in the BIOS"),
fuego.OptionPath("guid", "Device GUID"),
)
}

func (f *FuegoAdapter) registerLogsAndAlarmRoutes() {
Expand Down Expand Up @@ -454,6 +461,10 @@ func (f *FuegoAdapter) getPowerCapabilities(_ fuego.ContextNoBody) (dto.PowerCap
return dto.PowerCapabilities{}, nil
}

func (f *FuegoAdapter) getBootCapabilities(_ fuego.ContextNoBody) (dto.BootCapabilities, error) {
return dto.BootCapabilities{}, nil
}

func (f *FuegoAdapter) getAlarmOccurrences(_ fuego.ContextNoBody) ([]dto.AlarmClockOccurrence, error) {
return []dto.AlarmClockOccurrence{}, nil
}
Expand Down
48 changes: 48 additions & 0 deletions internal/controller/openapi/devicemanagement_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package openapi

import (
"encoding/json"
"testing"

"github.com/stretchr/testify/require"

dto "github.com/device-management-toolkit/console/internal/entity/dto/v1"
"github.com/device-management-toolkit/console/internal/usecase"
"github.com/device-management-toolkit/console/pkg/logger"
)

func newTestAdapter() *FuegoAdapter {
log := logger.New("error")

return NewFuegoAdapter(usecase.Usecases{}, log)
}

func TestGetBootCapabilities(t *testing.T) {
t.Parallel()

f := newTestAdapter()

result, err := f.getBootCapabilities(nil)

require.NoError(t, err)
require.Equal(t, dto.BootCapabilities{}, result)
}

func TestRegisterPowerRoutes_IncludesBootEndpoints(t *testing.T) {
t.Parallel()

f := newTestAdapter()
f.RegisterDeviceManagementRoutes()

specBytes, err := f.GetOpenAPISpec()
require.NoError(t, err)

var spec map[string]interface{}
require.NoError(t, json.Unmarshal(specBytes, &spec))

paths, ok := spec["paths"].(map[string]interface{})
require.True(t, ok)

require.Contains(t, paths, "/api/v1/admin/amt/boot/capabilities/{guid}", "boot capabilities route should be registered")
require.Contains(t, paths, "/api/v1/admin/amt/boot/rpe/{guid}", "set RPE enabled route should be registered")
}
1 change: 1 addition & 0 deletions internal/controller/ws/v1/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ type Feature interface {
GetDeviceCertificate(c context.Context, guid string) (dto.Certificate, error)
AddCertificate(c context.Context, guid string, certInfo dto.CertInfo) (string, error)
DeleteCertificate(c context.Context, guid, instanceID string) error
GetBootCapabilities(ctx context.Context, guid string) (dto.BootCapabilities, error)
GetBootSourceSetting(ctx context.Context, guid string) ([]dto.BootSources, error)
GetKVMScreenSettings(c context.Context, guid string) (dto.KVMScreenSettings, error)
SetKVMScreenSettings(c context.Context, guid string, req dto.KVMScreenSettingsRequest) (dto.KVMScreenSettings, error)
Expand Down
13 changes: 13 additions & 0 deletions internal/entity/dto/v1/bootcapabilities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dto

type RPERequest struct {
Enabled bool `json:"enabled"`
}

type RemoteEraseRequest struct {
EraseMask int `json:"eraseMask"`
}

type BootCapabilities struct {
PlatformErase int `json:"PlatformErase,omitempty"`
}
5 changes: 3 additions & 2 deletions internal/entity/dto/v1/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ type Features struct {
HTTPSBootSupported bool `json:"httpsBootSupported" example:"true"`
WinREBootSupported bool `json:"winREBootSupported" example:"true"`
LocalPBABootSupported bool `json:"localPBABootSupported" example:"true"`
RemoteErase bool `json:"remoteErase" example:"true"`
RPE bool `json:"rpe" example:"true"`
RPESupported bool `json:"rpeSupported" example:"true"`
}

type FeaturesRequest struct {
Expand All @@ -32,5 +33,5 @@ type FeaturesRequest struct {
EnableIDER bool `json:"enableIDER" example:"true"`
EnableKVM bool `json:"enableKVM" example:"true"`
OCR bool `json:"ocr" example:"true"`
RemoteErase bool `json:"remoteErase" example:"true"`
RPE bool `json:"rpe" example:"true"`
}
3 changes: 2 additions & 1 deletion internal/entity/dto/v1/getfeatures.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ type GetFeaturesResponse struct {
HTTPSBootSupported bool `json:"httpsBootSupported" binding:"required" example:"false"`
WinREBootSupported bool `json:"winREBootSupported" binding:"required" example:"false"`
LocalPBABootSupported bool `json:"localPBABootSupported" binding:"required" example:"false"`
RemoteErase bool `json:"remoteErase" binding:"required" example:"false"`
RPE bool `json:"rpe" binding:"required" example:"false"`
RPESupported bool `json:"rpeSupported" example:"false"`
}
5 changes: 3 additions & 2 deletions internal/entity/dto/v2/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ type Features struct {
Redirection bool `json:"redirection" example:"true"`
OptInState int `json:"optInState" example:"0"`
KVMAvailable bool `json:"kvmAvailable" example:"true"`
OCR bool `json:"httpBoot" example:"true"`
OCR bool `json:"ocr" example:"true"`
HTTPSBootSupported bool `json:"httpBootSupported,omitempty" example:"true"`
WinREBootSupported bool `json:"winREBootSupported,omitempty" example:"true"`
LocalPBABootSupported bool `json:"localPBABootSupported,omitempty" example:"true"`
RemoteErase bool `json:"remoteErase" example:"true"`
RPE bool `json:"rpe" example:"true"`
RPESupported bool `json:"rpeSupported" example:"true"`
}
Loading