mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-10 00:14:38 +00:00
124 lines
3.8 KiB
Go
124 lines
3.8 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/agentexec"
|
|
unified "github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources"
|
|
)
|
|
|
|
type actionAgentCommander interface {
|
|
ExecuteCommand(ctx context.Context, agentID string, cmd agentexec.ExecuteCommandPayload) (*agentexec.CommandResultPayload, error)
|
|
GetAgentForHost(hostname string) (string, bool)
|
|
IsAgentConnected(agentID string) bool
|
|
}
|
|
|
|
type actionHandlerProvider interface {
|
|
ActionHandlerNames() []string
|
|
}
|
|
|
|
type routedActionExecutor struct {
|
|
resources *ResourceHandlers
|
|
byHandler map[string]ActionExecutor
|
|
}
|
|
|
|
func newRoutedActionExecutor(resources *ResourceHandlers, executors ...ActionExecutor) ActionExecutor {
|
|
if resources == nil {
|
|
return nil
|
|
}
|
|
routed := routedActionExecutor{
|
|
resources: resources,
|
|
byHandler: map[string]ActionExecutor{},
|
|
}
|
|
for _, executor := range executors {
|
|
if executor == nil {
|
|
continue
|
|
}
|
|
provider, ok := executor.(actionHandlerProvider)
|
|
if !ok {
|
|
continue
|
|
}
|
|
for _, handler := range provider.ActionHandlerNames() {
|
|
handler = strings.TrimSpace(handler)
|
|
if handler != "" {
|
|
routed.byHandler[handler] = executor
|
|
}
|
|
}
|
|
}
|
|
if len(routed.byHandler) == 0 {
|
|
return nil
|
|
}
|
|
return routed
|
|
}
|
|
|
|
func (e routedActionExecutor) ExecuteAction(ctx context.Context, record unified.ActionAuditRecord) (*unified.ExecutionResult, error) {
|
|
normalized, err := unified.NormalizeActionAuditRecord(record)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
executor, err := e.executorForAction(ctx, normalized.Request)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return executor.ExecuteAction(ctx, normalized)
|
|
}
|
|
|
|
func (e routedActionExecutor) CheckActionAvailable(ctx context.Context, req unified.ActionRequest, resource unified.Resource) unified.ResourceActionReadiness {
|
|
capability, ok := resourceCapabilityByName(resource.Capabilities, req.CapabilityName)
|
|
if !ok || strings.TrimSpace(capability.InternalHandler) == "" {
|
|
return unified.ResourceActionReadiness{}
|
|
}
|
|
executor := e.byHandler[strings.TrimSpace(capability.InternalHandler)]
|
|
if executor == nil {
|
|
return unified.ResourceActionReadiness{
|
|
Name: strings.TrimSpace(req.CapabilityName),
|
|
Available: false,
|
|
ReasonCode: "unsupported_handler",
|
|
Reason: "This action is not routed through a supported executor.",
|
|
}
|
|
}
|
|
checker, ok := executor.(ActionAvailabilityChecker)
|
|
if !ok {
|
|
return unified.ResourceActionReadiness{}
|
|
}
|
|
return checker.CheckActionAvailable(ctx, req, resource)
|
|
}
|
|
|
|
func (e routedActionExecutor) executorForAction(ctx context.Context, req unified.ActionRequest) (ActionExecutor, error) {
|
|
if e.resources == nil {
|
|
return nil, fmt.Errorf("resource handler unavailable")
|
|
}
|
|
registry, err := e.resources.buildRegistry(GetOrgID(ctx))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resource, ok := registry.Get(req.ResourceID)
|
|
if !ok || resource == nil {
|
|
return nil, fmt.Errorf("resource %q is no longer present", req.ResourceID)
|
|
}
|
|
capability, ok := resourceCapabilityByName(resource.Capabilities, req.CapabilityName)
|
|
if !ok {
|
|
return nil, fmt.Errorf("resource %q does not currently advertise %s capability", req.ResourceID, req.CapabilityName)
|
|
}
|
|
handler := strings.TrimSpace(capability.InternalHandler)
|
|
if handler == "" {
|
|
return nil, fmt.Errorf("resource %q capability %s has no executor handler", req.ResourceID, req.CapabilityName)
|
|
}
|
|
executor := e.byHandler[handler]
|
|
if executor == nil {
|
|
return nil, fmt.Errorf("resource %q capability %s is routed through unsupported handler %q", req.ResourceID, req.CapabilityName, handler)
|
|
}
|
|
return executor, nil
|
|
}
|
|
|
|
func resourceCapabilityByName(capabilities []unified.ResourceCapability, name string) (unified.ResourceCapability, bool) {
|
|
name = strings.TrimSpace(name)
|
|
for _, capability := range capabilities {
|
|
if strings.TrimSpace(capability.Name) == name {
|
|
return capability, true
|
|
}
|
|
}
|
|
return unified.ResourceCapability{}, false
|
|
}
|