graphvent/event.go

203 lines
4.8 KiB
Go

package graphvent
import (
"time"
"fmt"
)
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) {
event_ext, err := GetExt[*EventExt](node, EventExtType)
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{
SerializedType(EventControlSignalType): nil,
})
type EventExt struct {
Name string `gv:"name"`
State string `gv:"state"`
Parent NodeID `gv:"parent"`
}
func NewEventExt(parent NodeID, name string) *EventExt {
return &EventExt{
Name: name,
State: "init",
Parent: parent,
}
}
type EventStateSignal struct {
SignalHeader
Source NodeID
State string
Time time.Time
}
func (signal EventStateSignal) Permission() Tree {
return Tree{
SerializedType(StatusType): nil,
}
}
func (signal EventStateSignal) String() string {
return fmt.Sprintf("EventStateSignal(%s, %s, %s, %+v)", signal.SignalHeader, signal.Source, signal.State, signal.Time)
}
func NewEventStateSignal(source NodeID, state string, t time.Time) *EventStateSignal {
return &EventStateSignal{
SignalHeader: NewSignalHeader(Up),
Source: source,
State: state,
Time: t,
}
}
type EventControlSignal struct {
SignalHeader
Command string
}
func (signal EventControlSignal) String() string {
return fmt.Sprintf("EventControlSignal(%s, %s)", signal.SignalHeader, signal.Command)
}
func NewEventControlSignal(command string) *EventControlSignal {
return &EventControlSignal{
NewSignalHeader(Direct),
command,
}
}
func (signal EventControlSignal) Permission() Tree {
return Tree{
SerializedType(EventControlSignalType): {
Hash("command", signal.Command): nil,
},
}
}
func (ext *EventExt) UpdateState(node *Node, state string) {
if ext.State != state {
ext.State = state
node.QueueSignal(time.Now(), NewEventStateSignal(node.ID, ext.State, time.Now()))
}
}
func (ext *EventExt) Process(ctx *Context, node *Node, source NodeID, signal Signal) (Messages, Changes) {
var messages Messages = nil
var changes Changes = nil
if signal.Direction() == Up && ext.Parent != node.ID {
messages = messages.Add(ctx, ext.Parent, node, nil, signal)
}
return messages, changes
}
type TestEventExt struct {
Length time.Duration
}
var test_event_transitions = map[string]struct{
from_state string
to_state string
}{
"ready?": {
"init",
"ready",
},
"start": {
"ready",
"running",
},
"abort": {
"ready",
"init",
},
"stop": {
"running",
"stopped",
},
"finish": {
"running",
"done",
},
}
func (ext *TestEventExt) Process(ctx *Context, node *Node, source NodeID, signal Signal) (Messages, Changes) {
var messages Messages = nil
var changes Changes = nil
switch sig := signal.(type) {
case *EventControlSignal:
event_ext, err := GetExt[*EventExt](node, EventExtType)
if err != nil {
messages = messages.Add(ctx, source, node, nil, NewErrorSignal(sig.Id, "not_event"))
} else {
info, exists := test_event_transitions[sig.Command]
if exists == true {
if event_ext.State == info.from_state {
ctx.Log.Logf("event", "%s %s->%s", node.ID, info.from_state, info.to_state)
messages = messages.Add(ctx, source, node, nil, NewSuccessSignal(sig.Id))
event_ext.UpdateState(node, info.to_state)
if event_ext.State == "running" {
node.QueueSignal(time.Now().Add(ext.Length), NewEventControlSignal("finish"))
}
} else {
messages = messages.Add(ctx, source, node, nil, NewErrorSignal(sig.Id, "bad_state"))
}
} else {
messages = messages.Add(ctx, source, node, nil, NewErrorSignal(sig.Id, "bad_command"))
}
}
}
return messages, changes
}
type TransitionValidation struct {
ToState string
}
func(ext *EventExt) ValidateEventCommand(signal *EventControlSignal, commands map[string]map[string]string) (string, *ErrorSignal) {
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
}
}
}