Skip to content
Merged
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
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
ARG BUILD_TAGS=""

# Step 1: Modules caching
FROM golang:1.26.3-alpine@sha256:91eda9776261207ea25fd06b5b7fed8d397dd2c0a283e77f2ab6e91bfa71079d AS modules
FROM golang:1.26-alpine@sha256:f23e8b227fb4493eabe03bede4d5a32d04092da71962f1fb79b5f7d1e6c2a17f AS modules
COPY go.mod go.sum /modules/
WORKDIR /modules
RUN apk add --no-cache git
RUN go mod download

# Step 2: Builder
FROM golang:1.26.3-alpine@sha256:91eda9776261207ea25fd06b5b7fed8d397dd2c0a283e77f2ab6e91bfa71079d AS builder
FROM golang:1.26-alpine@sha256:f23e8b227fb4493eabe03bede4d5a32d04092da71962f1fb79b5f7d1e6c2a17f AS builder
# Build tags control dependencies:
# - Default (no tags): Full build with UI
# - noui: Excludes web UI assets
Expand Down
9 changes: 7 additions & 2 deletions redfish/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,16 @@ func Initialize(_ *gin.Engine, log logger.Interface, _ *db.SQL, usecases *dmtuse
// Check if we should use mock repository (for testing)
useMock := os.Getenv("REDFISH_USE_MOCK") == "true"

var repo redfishusecase.ComputerSystemRepository
var (
repo redfishusecase.ComputerSystemRepository
deviceRepo redfishusecase.DeviceLookupRepository
)

if useMock {
log.Info("Using mock WSMAN repository for Redfish API")

repo = mocks.NewMockComputerSystemRepo()
deviceRepo = redfishusecase.DeviceLookupFromComputerSystemRepo{Repo: repo}
} else {
// Create Redfish-specific repository and use case using DMT's device management
devicesUC, ok := usecases.Devices.(*devices.UseCase)
Expand All @@ -87,9 +91,10 @@ func Initialize(_ *gin.Engine, log logger.Interface, _ *db.SQL, usecases *dmtuse
}

repo = redfishusecase.NewWsmanComputerSystemRepo(devicesUC, log)
deviceRepo = devicesUC
}

computerSystemUC := &redfishusecase.ComputerSystemUseCase{Repo: repo}
computerSystemUC := &redfishusecase.ComputerSystemUseCase{Repo: repo, DeviceRepo: deviceRepo}

// Create session repository and use case
const sessionCleanupInterval = 5 * time.Minute
Expand Down
44 changes: 44 additions & 0 deletions redfish/internal/controller/http/v1/handler/systems_actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ func (s *RedfishServer) PostRedfishV1SystemsComputerSystemIdActionsComputerSyste

log.Infof("Received reset request for ComputerSystem %s with ResetType %s", computerSystemID, *req.ResetType)

if err := s.ComputerSystemUC.EnsureSystemExists(c.Request.Context(), computerSystemID); err != nil {
switch {
case errors.Is(err, usecase.ErrSystemNotFound):
NotFoundError(c, "System", computerSystemID)
default:
InternalServerError(c, err)
}

return
}

if err := s.ComputerSystemUC.SetPowerState(c.Request.Context(), computerSystemID, *req.ResetType); err != nil {
switch {
case errors.Is(err, usecase.ErrSystemNotFound):
Expand Down Expand Up @@ -129,6 +140,17 @@ func (s *RedfishServer) PostRedfishV1SystemsComputerSystemIdActionsOemIntelCompu
return
}

if err := s.ComputerSystemUC.EnsureSystemExists(c.Request.Context(), computerSystemID); err != nil {
switch {
case errors.Is(err, usecase.ErrSystemNotFound):
NotFoundError(c, "System", computerSystemID)
default:
InternalServerError(c, err)
}

return
}

if err := s.ComputerSystemUC.CancelKVMConsent(c.Request.Context(), computerSystemID); err != nil {
var consentErr *usecase.ConsentFailedError

Expand Down Expand Up @@ -215,6 +237,17 @@ func (s *RedfishServer) PostRedfishV1SystemsComputerSystemIdActionsOemIntelCompu
return
}

if err := s.ComputerSystemUC.EnsureSystemExists(c.Request.Context(), computerSystemID); err != nil {
switch {
case errors.Is(err, usecase.ErrSystemNotFound):
NotFoundError(c, "System", computerSystemID)
default:
InternalServerError(c, err)
}

return
}

if err := s.ComputerSystemUC.RequestKVMConsent(c.Request.Context(), computerSystemID); err != nil {
var consentErr *usecase.ConsentFailedError

Expand Down Expand Up @@ -282,6 +315,17 @@ func (s *RedfishServer) PostRedfishV1SystemsComputerSystemIdActionsOemIntelCompu
return
}

if err := s.ComputerSystemUC.EnsureSystemExists(c.Request.Context(), computerSystemID); err != nil {
switch {
case errors.Is(err, usecase.ErrSystemNotFound):
NotFoundError(c, "System", computerSystemID)
default:
InternalServerError(c, err)
}

return
}

if err := s.ComputerSystemUC.SubmitKVMConsentCode(c.Request.Context(), computerSystemID, consentCode); err != nil {
var consentErr *usecase.ConsentFailedError

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,41 @@ import (
"github.com/stretchr/testify/assert"

"github.com/device-management-toolkit/console/config"
devicev1 "github.com/device-management-toolkit/console/internal/entity/dto/v1"
devusecase "github.com/device-management-toolkit/console/internal/usecase/devices"
"github.com/device-management-toolkit/console/redfish/internal/controller/http/v1/generated"
redfishv1 "github.com/device-management-toolkit/console/redfish/internal/entity/v1"
"github.com/device-management-toolkit/console/redfish/internal/usecase"
)

var configTestMu sync.Mutex

type testActionDeviceLookupRepo struct {
repo *TestSystemsComputerSystemRepository
err error
}

func (r testActionDeviceLookupRepo) GetByID(_ context.Context, guid, _ string, _ bool) (*devicev1.Device, error) {
if r.err != nil {
return nil, r.err
}

if _, exists := r.repo.systems[guid]; !exists {
return nil, devusecase.ErrNotFound
}

return &devicev1.Device{GUID: guid}, nil
}

func setupSystemActionsTestServerWithLookup(repo *TestSystemsComputerSystemRepository, lookup testActionDeviceLookupRepo) *RedfishServer {
uc := &usecase.ComputerSystemUseCase{
Repo: repo,
DeviceRepo: lookup,
}

return &RedfishServer{ComputerSystemUC: uc}
}

// Test constants for system actions
const (
testSystemID = "550e8400-e29b-41d4-a716-446655440001"
Expand All @@ -39,7 +67,8 @@ const (
// setupSystemActionsTestServer creates a test server with a mock repository
func setupSystemActionsTestServer(repo *TestSystemsComputerSystemRepository) *RedfishServer {
uc := &usecase.ComputerSystemUseCase{
Repo: repo,
Repo: repo,
DeviceRepo: testActionDeviceLookupRepo{repo: repo},
}

return &RedfishServer{
Expand Down Expand Up @@ -883,6 +912,63 @@ func TestPostRedfishV1SystemsComputerSystemIdActionsOemIntelComputerSystemCancel
assert.Equal(t, http.StatusBadRequest, w.Code)
}

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

repo := NewTestSystemsComputerSystemRepository()
repo.AddSystem(testSystemID, &redfishv1.ComputerSystem{ID: testSystemID, Name: "Test"})

server := setupSystemActionsTestServerWithLookup(repo, testActionDeviceLookupRepo{repo: repo, err: errors.New("lookup failure")})
router := setupSystemActionsTestRouter(server)

body := createResetRequest(generated.ResourceResetTypeOn)
w := executeResetRequest(router, resetActionEndpoint, body)

assert.Equal(t, http.StatusInternalServerError, w.Code)
}

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

repo := NewTestSystemsComputerSystemRepository()
repo.AddSystem(testSystemID, &redfishv1.ComputerSystem{ID: testSystemID, Name: "Test"})

server := setupSystemActionsTestServerWithLookup(repo, testActionDeviceLookupRepo{repo: repo, err: errors.New("lookup failure")})
router := setupSystemActionsTestRouter(server)

w := executeResetRequest(router, requestKVMConsentEndpoint, createEmptyActionRequest())

assert.Equal(t, http.StatusInternalServerError, w.Code)
}

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

repo := NewTestSystemsComputerSystemRepository()
repo.AddSystem(testSystemID, &redfishv1.ComputerSystem{ID: testSystemID, Name: "Test"})

server := setupSystemActionsTestServerWithLookup(repo, testActionDeviceLookupRepo{repo: repo, err: errors.New("lookup failure")})
router := setupSystemActionsTestRouter(server)

w := executeResetRequest(router, submitKVMConsentEndpoint, createSubmitKVMConsentCodeRequest("123456"))

assert.Equal(t, http.StatusInternalServerError, w.Code)
}

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

repo := NewTestSystemsComputerSystemRepository()
repo.AddSystem(testSystemID, &redfishv1.ComputerSystem{ID: testSystemID, Name: "Test"})

server := setupSystemActionsTestServerWithLookup(repo, testActionDeviceLookupRepo{repo: repo, err: errors.New("lookup failure")})
router := setupSystemActionsTestRouter(server)

w := executeResetRequest(router, cancelKVMConsentEndpoint, createEmptyActionRequest())

assert.Equal(t, http.StatusInternalServerError, w.Code)
}

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

Expand Down
55 changes: 54 additions & 1 deletion redfish/internal/usecase/computer_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"github.com/golang-jwt/jwt/v5"

"github.com/device-management-toolkit/console/config"
devicev1 "github.com/device-management-toolkit/console/internal/entity/dto/v1"
devusecase "github.com/device-management-toolkit/console/internal/usecase/devices"
"github.com/device-management-toolkit/console/redfish/internal/controller/http/v1/generated"
redfishv1 "github.com/device-management-toolkit/console/redfish/internal/entity/v1"
)
Expand Down Expand Up @@ -38,6 +40,9 @@ var (

// ErrConsoleConfigNotInitialized is returned when console config is not initialized.
ErrConsoleConfigNotInitialized = errors.New("console config is not initialized")

// ErrDeviceLookupNotConfigured is returned when device lookup dependency is missing.
ErrDeviceLookupNotConfigured = errors.New("device lookup repository is not configured")
)

// OData and schema constants for ComputerSystem.
Expand Down Expand Up @@ -80,9 +85,39 @@ const (

// ComputerSystemUseCase provides business logic for ComputerSystem entities.
type ComputerSystemUseCase struct {
Repo ComputerSystemRepository
DeviceRepo DeviceLookupRepository
}

// DeviceLookupRepository defines the minimal dependency needed to validate
// device existence for token generation without triggering Redfish WSMAN hydration.
type DeviceLookupRepository interface {
GetByID(ctx context.Context, guid, tenantID string, includeSecrets bool) (*devicev1.Device, error)
}

// DeviceLookupFromComputerSystemRepo adapts a ComputerSystemRepository for
// mock/test flows that do not have direct access to the devices use case.
type DeviceLookupFromComputerSystemRepo struct {
Repo ComputerSystemRepository
}

// GetByID validates existence via the wrapped ComputerSystemRepository.
func (a DeviceLookupFromComputerSystemRepo) GetByID(ctx context.Context, guid, _ string, _ bool) (*devicev1.Device, error) {
if a.Repo == nil {
return nil, ErrDeviceLookupNotConfigured
}

if _, err := a.Repo.GetByID(ctx, guid); err != nil {
if errors.Is(err, ErrSystemNotFound) {
return nil, devusecase.ErrNotFound
}

return nil, err
}

return &devicev1.Device{GUID: guid}, nil
}

// GetAll retrieves all ComputerSystem IDs from the repository.
func (uc *ComputerSystemUseCase) GetAll(ctx context.Context) ([]string, error) {
return uc.Repo.GetAll(ctx)
Expand Down Expand Up @@ -198,6 +233,24 @@ func (uc *ComputerSystemUseCase) GetComputerSystem(ctx context.Context, systemID
return &result, nil
}

// EnsureSystemExists validates that the requested system exists via the configured
// device lookup repository.
func (uc *ComputerSystemUseCase) EnsureSystemExists(ctx context.Context, systemID string) error {
if uc.DeviceRepo == nil {
return ErrDeviceLookupNotConfigured
}

if _, err := uc.DeviceRepo.GetByID(ctx, systemID, "", false); err != nil {
if errors.Is(err, devusecase.ErrNotFound) || errors.Is(err, ErrSystemNotFound) {
return ErrSystemNotFound
}

return err
}

return nil
}
Comment thread
sudhir-intc marked this conversation as resolved.

// SetPowerState validates and sets the power state for a ComputerSystem.
func (uc *ComputerSystemUseCase) SetPowerState(ctx context.Context, id string, resetType generated.ResourceResetType) error {
// Validate the reset type
Expand Down Expand Up @@ -522,7 +575,7 @@ func (uc *ComputerSystemUseCase) CancelKVMConsent(ctx context.Context, systemID

// GenerateRedirectionToken validates that the target system exists and returns a short-lived redirection token.
func (uc *ComputerSystemUseCase) GenerateRedirectionToken(ctx context.Context, systemID string) (*generated.ComputerSystemOemIntelAmtGenerateRedirectionTokenResponse, error) {
if _, err := uc.Repo.GetByID(ctx, systemID); err != nil {
if err := uc.EnsureSystemExists(ctx, systemID); err != nil {
return nil, err
}

Expand Down
Loading
Loading