2023-10-29 18:26:14 -06:00
|
|
|
package graphvent
|
|
|
|
|
|
|
|
import (
|
|
|
|
"time"
|
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
|
2023-11-12 13:36:11 -07:00
|
|
|
type EventCommand string
|
|
|
|
type EventState string
|
|
|
|
|
2023-11-05 21:18:14 -07:00
|
|
|
type ParentOfPolicy struct {
|
|
|
|
PolicyHeader
|
|
|
|
Policy Tree
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewParentOfPolicy(policy Tree) *ParentOfPolicy {
|
|
|
|
return &ParentOfPolicy{
|
|
|
|
PolicyHeader: NewPolicyHeader(),
|
|
|
|
Policy: policy,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (policy ParentOfPolicy) Allows(ctx *Context, principal_id NodeID, action Tree, node *Node)(Messages, RuleResult) {
|
2024-03-03 15:45:45 -07:00
|
|
|
event_ext, err := GetExt[EventExt](node)
|
2023-11-05 21:18:14 -07:00
|
|
|
if err != nil {
|
|
|
|
ctx.Log.Logf("event", "ParentOfPolicy, node not event %s", node.ID)
|
|
|
|
return nil, Deny
|
|
|
|
}
|
|
|
|
|
|
|
|
if event_ext.Parent == principal_id {
|
|
|
|
return nil, policy.Policy.Allows(action)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, Deny
|
|
|
|
}
|
|
|
|
|
|
|
|
func (policy ParentOfPolicy) ContinueAllows(ctx *Context, current PendingACL, signal Signal) RuleResult {
|
|
|
|
return Deny
|
|
|
|
}
|
|
|
|
|
|
|
|
var DefaultEventPolicy = NewParentOfPolicy(Tree{
|
2024-03-03 15:45:45 -07:00
|
|
|
SerializedType(SignalTypeFor[EventControlSignal]()): nil,
|
2023-11-05 21:18:14 -07:00
|
|
|
})
|
|
|
|
|
2023-10-29 18:26:14 -06:00
|
|
|
type EventExt struct {
|
2023-10-30 01:25:18 -06:00
|
|
|
Name string `gv:"name"`
|
2023-11-12 13:36:11 -07:00
|
|
|
State EventState `gv:"state"`
|
2023-11-13 17:29:53 -07:00
|
|
|
StateStart time.Time `gv:"state_start"`
|
2023-11-04 18:56:35 -06:00
|
|
|
Parent NodeID `gv:"parent"`
|
2023-10-29 18:26:14 -06:00
|
|
|
}
|
|
|
|
|
2024-03-03 16:37:03 -07:00
|
|
|
func (ext *EventExt) Load(ctx *Context, node *Node) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ext *EventExt) Unload(ctx *Context, node *Node) {
|
|
|
|
}
|
|
|
|
|
2023-11-04 18:56:35 -06:00
|
|
|
func NewEventExt(parent NodeID, name string) *EventExt {
|
2023-10-29 18:26:14 -06:00
|
|
|
return &EventExt{
|
|
|
|
Name: name,
|
|
|
|
State: "init",
|
|
|
|
Parent: parent,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type EventStateSignal struct {
|
|
|
|
SignalHeader
|
2023-11-12 13:36:11 -07:00
|
|
|
Source NodeID `gv:"source"`
|
|
|
|
State EventState `gv:"state"`
|
|
|
|
Time time.Time `gv:"time"`
|
2023-10-29 18:26:14 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (signal EventStateSignal) Permission() Tree {
|
|
|
|
return Tree{
|
2024-03-03 15:45:45 -07:00
|
|
|
SerializedType(SignalTypeFor[StatusSignal]()): nil,
|
2023-10-29 18:26:14 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (signal EventStateSignal) String() string {
|
|
|
|
return fmt.Sprintf("EventStateSignal(%s, %s, %s, %+v)", signal.SignalHeader, signal.Source, signal.State, signal.Time)
|
|
|
|
}
|
|
|
|
|
2023-11-12 13:36:11 -07:00
|
|
|
func NewEventStateSignal(source NodeID, state EventState, t time.Time) *EventStateSignal {
|
2023-10-29 18:26:14 -06:00
|
|
|
return &EventStateSignal{
|
|
|
|
SignalHeader: NewSignalHeader(Up),
|
|
|
|
Source: source,
|
|
|
|
State: state,
|
|
|
|
Time: t,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type EventControlSignal struct {
|
|
|
|
SignalHeader
|
2023-11-12 13:36:11 -07:00
|
|
|
Command EventCommand `gv:"command"`
|
2023-10-29 18:26:14 -06:00
|
|
|
}
|
|
|
|
|
2023-11-02 20:58:38 -06:00
|
|
|
func (signal EventControlSignal) String() string {
|
|
|
|
return fmt.Sprintf("EventControlSignal(%s, %s)", signal.SignalHeader, signal.Command)
|
|
|
|
}
|
|
|
|
|
2023-11-12 13:36:11 -07:00
|
|
|
func NewEventControlSignal(command EventCommand) *EventControlSignal {
|
2023-10-29 18:26:14 -06:00
|
|
|
return &EventControlSignal{
|
|
|
|
NewSignalHeader(Direct),
|
|
|
|
command,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (signal EventControlSignal) Permission() Tree {
|
|
|
|
return Tree{
|
2024-03-03 15:45:45 -07:00
|
|
|
SerializedType(SignalTypeFor[EventControlSignal]()): {
|
2023-11-12 13:36:11 -07:00
|
|
|
Hash("command", string(signal.Command)): nil,
|
2023-10-29 18:26:14 -06:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-13 17:29:53 -07:00
|
|
|
func (ext *EventExt) UpdateState(node *Node, changes Changes, state EventState, state_start time.Time) {
|
2023-11-03 21:41:06 -06:00
|
|
|
if ext.State != state {
|
2023-11-13 17:29:53 -07:00
|
|
|
ext.StateStart = state_start
|
2024-03-03 16:37:03 -07:00
|
|
|
changes.Add("state")
|
2023-11-03 21:41:06 -06:00
|
|
|
ext.State = state
|
|
|
|
node.QueueSignal(time.Now(), NewEventStateSignal(node.ID, ext.State, time.Now()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-30 19:40:30 -06:00
|
|
|
func (ext *EventExt) Process(ctx *Context, node *Node, source NodeID, signal Signal) (Messages, Changes) {
|
|
|
|
var messages Messages = nil
|
2023-11-11 14:52:08 -07:00
|
|
|
var changes = Changes{}
|
2023-10-30 19:40:30 -06:00
|
|
|
|
2023-11-04 18:56:35 -06:00
|
|
|
if signal.Direction() == Up && ext.Parent != node.ID {
|
|
|
|
messages = messages.Add(ctx, ext.Parent, node, nil, signal)
|
2023-10-30 19:40:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return messages, changes
|
|
|
|
}
|
|
|
|
|
2023-11-02 20:58:38 -06:00
|
|
|
type TestEventExt struct {
|
|
|
|
Length time.Duration
|
|
|
|
}
|
2023-10-30 19:40:30 -06:00
|
|
|
|
2024-03-03 16:37:03 -07:00
|
|
|
func (ext *TestEventExt) Load(ctx *Context, node *Node) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ext *TestEventExt) Unload(ctx *Context, node *Node) {
|
|
|
|
}
|
|
|
|
|
2023-11-12 13:36:11 -07:00
|
|
|
type EventCommandMap map[EventCommand]map[EventState]EventState
|
|
|
|
var test_event_commands = EventCommandMap{
|
2023-11-02 20:58:38 -06:00
|
|
|
"ready?": {
|
2023-11-05 23:32:13 -07:00
|
|
|
"init": "ready",
|
2023-11-02 20:58:38 -06:00
|
|
|
},
|
|
|
|
"start": {
|
2023-11-05 23:32:13 -07:00
|
|
|
"ready": "running",
|
2023-10-29 18:26:14 -06:00
|
|
|
},
|
2023-11-02 20:58:38 -06:00
|
|
|
"abort": {
|
2023-11-05 23:32:13 -07:00
|
|
|
"ready": "init",
|
2023-11-02 20:58:38 -06:00
|
|
|
},
|
2023-10-29 18:26:14 -06:00
|
|
|
"stop": {
|
2023-11-05 23:32:13 -07:00
|
|
|
"running": "stopped",
|
2023-10-29 18:26:14 -06:00
|
|
|
},
|
|
|
|
"finish": {
|
2023-11-05 23:32:13 -07:00
|
|
|
"running": "done",
|
2023-10-29 18:26:14 -06:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2023-10-30 01:25:18 -06:00
|
|
|
|
|
|
|
func (ext *TestEventExt) Process(ctx *Context, node *Node, source NodeID, signal Signal) (Messages, Changes) {
|
|
|
|
var messages Messages = nil
|
2023-11-11 14:52:08 -07:00
|
|
|
var changes = Changes{}
|
2023-10-30 01:25:18 -06:00
|
|
|
|
2023-10-29 18:26:14 -06:00
|
|
|
switch sig := signal.(type) {
|
|
|
|
case *EventControlSignal:
|
2024-03-03 15:45:45 -07:00
|
|
|
event_ext, err := GetExt[EventExt](node)
|
2023-10-30 01:25:18 -06:00
|
|
|
if err != nil {
|
|
|
|
messages = messages.Add(ctx, source, node, nil, NewErrorSignal(sig.Id, "not_event"))
|
|
|
|
} else {
|
2023-11-05 23:32:13 -07:00
|
|
|
ctx.Log.Logf("event", "%s got %s EventControlSignal while in %s", node.ID, sig.Command, event_ext.State)
|
|
|
|
new_state, error_signal := event_ext.ValidateEventCommand(sig, test_event_commands)
|
|
|
|
if error_signal != nil {
|
|
|
|
messages = messages.Add(ctx, source, node, nil, error_signal)
|
2023-10-29 18:26:14 -06:00
|
|
|
} else {
|
2023-11-05 23:32:13 -07:00
|
|
|
switch sig.Command {
|
|
|
|
case "start":
|
|
|
|
node.QueueSignal(time.Now().Add(ext.Length), NewEventControlSignal("finish"))
|
|
|
|
}
|
2023-11-13 17:29:53 -07:00
|
|
|
event_ext.UpdateState(node, changes, new_state, time.Now())
|
2023-11-05 23:32:13 -07:00
|
|
|
messages = messages.Add(ctx, source, node, nil, NewSuccessSignal(sig.Id))
|
2023-10-29 18:26:14 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return messages, changes
|
|
|
|
}
|
2023-11-05 21:18:14 -07:00
|
|
|
|
2023-11-12 13:36:11 -07:00
|
|
|
func(ext *EventExt) ValidateEventCommand(signal *EventControlSignal, commands EventCommandMap) (EventState, *ErrorSignal) {
|
2023-11-05 21:18:14 -07:00
|
|
|
transitions, command_mapped := commands[signal.Command]
|
|
|
|
if command_mapped == false {
|
|
|
|
return "", NewErrorSignal(signal.Id, "unknown command %s", signal.Command)
|
|
|
|
} else {
|
|
|
|
new_state, valid_transition := transitions[ext.State]
|
|
|
|
if valid_transition == false {
|
|
|
|
return "", NewErrorSignal(signal.Id, "invalid command state %s(%s)", signal.Command, ext.State)
|
|
|
|
} else {
|
|
|
|
return new_state, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|