Skip to content

Commit

Permalink
Feature/101 base reporter (#117)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucamrgs authored Apr 24, 2024
1 parent 71ae2df commit ddc95bf
Show file tree
Hide file tree
Showing 15 changed files with 364 additions and 41 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ DB_USERNAME: "root"
DB_PASSWORD: "rootpassword"
PLAYBOOK_API_LOG_LEVEL: trace
DATABASE: "false"
MAX_REPORTERS: "5"

LOG_GLOBAL_LEVEL: "info"
LOG_MODE: "development"
Expand Down
49 changes: 28 additions & 21 deletions docs/content/en/docs/core-components/reporting.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,49 +30,56 @@ The schema below represents the architecture concept.
@startuml
set separator ::
interface IStepReporter{
ReportStep() error
}
interface IWorkflowReporter{
ReportWorkflow(cacao.workflow)
ReportWorkflow() error
}
interface IStepReporter{
ReportStep(cacao.workflow.Step, cacao.Variables, error)
interface IDownStreamReporter {
ReportWorkflow() error
ReportStep() error
}
class Reporter {
stepReporters []IStepReporter
workflowReporters []IWorkflowReporter
reporters []IDownStreamReporter
RegisterStepReporter()
RegisterWorkflowReporter()
RegisterReporters() error
ReportWorkflow()
ReportStep()
}
class Database as DB
class Database
class Cache
class 3PTool
class Decomposer
class Executor
Decomposer -up-> Reporter
Executor -up-> Reporter
Decomposer -right-> IWorkflowReporter
Executor -left-> IStepReporter
Reporter -up-> IWorkflowReporter
Reporter .up.|> IWorkflowReporter
Reporter .up.|> IStepReporter
Reporter -up-> IStepReporter
Reporter .up.|> IWorkflowReporter
Reporter -right-> IDownStreamReporter
DB .up.|> IWorkflowReporter
DB .up.|> IStepReporter
3PTool .up.|> IWorkflowReporter
3PTool .up.|> IStepReporter
Reporter --left--> DB
Reporter --right--> 3PTool
Database .up.|> IDownStreamReporter
Cache .up.|> IDownStreamReporter
3PTool .up.|> IDownStreamReporter
```

### Interfaces

The logic and extensibility is implemented in the SOARCA architecture by means of reporting interfaces. At this stage, we implement an *IWorkflowReporter* to push information about the entire workflow to be executed, and an *IStepReporter* to push step-specific information as the steps of the workflow are executed.
The reporting logic and extensibility is implemented in the SOARCA architecture by means of reporting interfaces. At this stage, we implement an *IWorkflowReporter* to push information about the entire workflow to be executed, and an *IStepReporter* to push step-specific information as the steps of the workflow are executed.

A high level *Reporter* component will implement both interfaces, and maintain the list of *DownStreamRepporter*s activated for the SOARCA instance. The *Reporter* class will invoke all reporting functions for each active reporter. The *Executer* and *Decomposer* components will be injected each with the Reporter though, as interface of respectively workflow reporter, and step reporter, to keep the reporting scope separated.

A high level *Reporter* component will implement both interfaces, and maintain the list of decomposer and executor reporters activated for the SOARCA instance. The *Reporter* class will invoke all reporting functions for each active reporter.
The *DownStream* reporters will implement push-based reporting functions specific for the reporting target, as shown in the *IDownStreamReporter* interface. Internal components to SOARCA, and third-party tool reporters, will thus implement the *IDownStreamReporter* interface.

## Future plans

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module soarca

go 1.22
go 1.22.0

require (
github.com/eclipse/paho.mqtt.golang v1.4.3
Expand Down
11 changes: 8 additions & 3 deletions internal/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ import (
"soarca/internal/executors/playbook_action"
"soarca/internal/fin/protocol"
"soarca/internal/guid"
"soarca/internal/reporter"
"soarca/logger"
"soarca/utils"
httpUtil "soarca/utils/http"

downstreamReporter "soarca/internal/reporter/downstream_reporter"

"github.com/gin-gonic/gin"

mongo "soarca/database/mongodb"
Expand Down Expand Up @@ -67,10 +70,12 @@ func (controller *Controller) NewDecomposer() decomposer.IDecomposer {
}
}

actionExecutor := action.New(capabilities)
playbookActionExecutor := playbook_action.New(controller, controller)
reporter := reporter.New([]downstreamReporter.IDownStreamReporter{})

actionExecutor := action.New(capabilities, reporter)
playbookActionExecutor := playbook_action.New(controller, controller, reporter)
guid := new(guid.Guid)
decompose := decomposer.New(actionExecutor, playbookActionExecutor, guid)
decompose := decomposer.New(actionExecutor, playbookActionExecutor, guid, reporter)
return decompose
}

Expand Down
10 changes: 8 additions & 2 deletions internal/decomposer/decomposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"soarca/internal/executors"
"soarca/internal/executors/action"
"soarca/internal/guid"
"soarca/internal/reporter"
"soarca/logger"
"soarca/models/cacao"
"soarca/models/execution"
Expand Down Expand Up @@ -38,11 +39,12 @@ func init() {

func New(actionExecutor action.IExecuter,
playbookActionExecutor executors.IPlaybookExecuter,
guid guid.IGuid) *Decomposer {
guid guid.IGuid, reporter reporter.IWorkflowReporter) *Decomposer {

return &Decomposer{actionExecutor: actionExecutor,
playbookActionExecutor: playbookActionExecutor,
guid: guid}
guid: guid,
reporter: reporter}
}

type Decomposer struct {
Expand All @@ -51,6 +53,7 @@ type Decomposer struct {
actionExecutor action.IExecuter
playbookActionExecutor executors.IPlaybookExecuter
guid guid.IGuid
reporter reporter.IWorkflowReporter
}

// Execute a Playbook
Expand All @@ -65,6 +68,9 @@ func (decomposer *Decomposer) Execute(playbook cacao.Playbook) (*ExecutionDetail
variables := cacao.NewVariables()
variables.Merge(playbook.PlaybookVariables)

// Reporting workflow instantiation
decomposer.reporter.ReportWorkflow(decomposer.details.ExecutionId, playbook)

outputVariables, err := decomposer.ExecuteBranch(stepId, variables)

decomposer.details.Variables = outputVariables
Expand Down
7 changes: 6 additions & 1 deletion internal/executors/action/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"reflect"
"soarca/internal/capability"
"soarca/internal/reporter"
"soarca/logger"
"soarca/models/cacao"
"soarca/models/execution"
Expand All @@ -17,9 +18,10 @@ func init() {
log = logger.Logger(component, logger.Info, "", logger.Json)
}

func New(capabilities map[string]capability.ICapability) *Executor {
func New(capabilities map[string]capability.ICapability, reporter reporter.IStepReporter) *Executor {
var instance = Executor{}
instance.capabilities = capabilities
instance.reporter = reporter
return &instance
}

Expand All @@ -38,6 +40,7 @@ type IExecuter interface {

type Executor struct {
capabilities map[string]capability.ICapability
reporter reporter.IStepReporter
}

func (executor *Executor) Execute(meta execution.Metadata, metadata PlaybookStepMetadata) (cacao.Variables, error) {
Expand Down Expand Up @@ -73,12 +76,14 @@ func (executor *Executor) Execute(meta execution.Metadata, metadata PlaybookStep

if err != nil {
log.Error("Error executing Command ", err)
executor.reporter.ReportStep(meta.ExecutionId, metadata.Step, returnVariables, err)
return cacao.NewVariables(), err
} else {
log.Debug("Command executed")
}
}
}
executor.reporter.ReportStep(meta.ExecutionId, metadata.Step, returnVariables, nil)
return returnVariables, nil
}

Expand Down
8 changes: 6 additions & 2 deletions internal/executors/playbook_action/playbook_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"reflect"
"soarca/internal/controller/database"
"soarca/internal/controller/decomposer_controller"
"soarca/internal/reporter"
"soarca/logger"
"soarca/models/cacao"
"soarca/models/execution"
Expand All @@ -14,6 +15,7 @@ import (
type PlaybookAction struct {
decomposerController decomposer_controller.IController
databaseController database.IController
reporter reporter.IStepReporter
}

var component = reflect.TypeOf(PlaybookAction{}).PkgPath()
Expand All @@ -24,8 +26,8 @@ func init() {
}

func New(controller decomposer_controller.IController,
database database.IController) *PlaybookAction {
return &PlaybookAction{decomposerController: controller, databaseController: database}
database database.IController, reporter reporter.IStepReporter) *PlaybookAction {
return &PlaybookAction{decomposerController: controller, databaseController: database, reporter: reporter}
}

func (playbookAction *PlaybookAction) Execute(metadata execution.Metadata,
Expand Down Expand Up @@ -54,8 +56,10 @@ func (playbookAction *PlaybookAction) Execute(metadata execution.Metadata,
if err != nil {
err = errors.New(fmt.Sprint("execution of playbook failed with error: ", err))
log.Error(err)
playbookAction.reporter.ReportStep(metadata.ExecutionId, step, playbook.PlaybookVariables, err)
return cacao.NewVariables(), err
}
playbookAction.reporter.ReportStep(metadata.ExecutionId, step, playbook.PlaybookVariables, nil)
return details.Variables, nil

}
12 changes: 12 additions & 0 deletions internal/reporter/downstream_reporter/downstream_reporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package downstream_reporter

import (
"soarca/models/cacao"

"github.com/google/uuid"
)

type IDownStreamReporter interface {
ReportWorkflow(executionId uuid.UUID, playbook cacao.Playbook) error
ReportStep(executionId uuid.UUID, step cacao.Step, stepResults cacao.Variables, err error) error
}
79 changes: 79 additions & 0 deletions internal/reporter/reporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package reporter

import (
"errors"
"reflect"
"strconv"

downstreamReporter "soarca/internal/reporter/downstream_reporter"
"soarca/logger"
"soarca/models/cacao"
"soarca/utils"

"github.com/google/uuid"
)

type Empty struct{}

var component = reflect.TypeOf(Empty{}).PkgPath()
var log *logger.Log

func init() {
log = logger.Logger(component, logger.Info, "", logger.Json)
}

// Reporter interfaces
type IWorkflowReporter interface {
// -> Give info to downstream reporters
ReportWorkflow(executionId uuid.UUID, playbook cacao.Playbook)
}
type IStepReporter interface {
// -> Give info to downstream reporters
ReportStep(executionId uuid.UUID, step cacao.Step, returnVars cacao.Variables, err error)
}

const MaxReporters int = 10

// High-level reporter class with injection of specific reporters
type Reporter struct {
reporters []downstreamReporter.IDownStreamReporter
maxReporters int
}

func New(reporters []downstreamReporter.IDownStreamReporter) *Reporter {
maxReporters, _ := strconv.Atoi(utils.GetEnv("MAX_REPORTERS", strconv.Itoa(MaxReporters)))
instance := Reporter{
reporters: reporters,
maxReporters: maxReporters,
}
return &instance
}

func (reporter *Reporter) RegisterReporters(reporters []downstreamReporter.IDownStreamReporter) error {
if (len(reporter.reporters) + len(reporters)) > reporter.maxReporters {
log.Warning("reporter not registered, too many reporters")
return errors.New("attempting to register too many reporters")
}
reporter.reporters = append(reporter.reporters, reporters...)
return nil
}

func (reporter *Reporter) ReportWorkflow(executionId uuid.UUID, playbook cacao.Playbook) {
log.Trace("reporting workflow")
for _, rep := range reporter.reporters {
err := rep.ReportWorkflow(executionId, playbook)
if err != nil {
log.Warning(err)
}
}
}

func (reporter *Reporter) ReportStep(executionId uuid.UUID, step cacao.Step, returnVars cacao.Variables, err error) {
log.Trace("reporting step data")
for _, rep := range reporter.reporters {
err := rep.ReportStep(executionId, step, returnVars, err)
if err != nil {
log.Warning(err)
}
}
}
Loading

0 comments on commit ddc95bf

Please sign in to comment.