model done

This commit is contained in:
2025-01-31 17:42:06 +03:00
parent bb69ee0be9
commit 99ecf1b0d8
12 changed files with 185 additions and 194 deletions

View File

@@ -5,10 +5,10 @@ import freeflowuniverse.herolib.data.ourtime
// Agent represents a service provider that can execute jobs
pub struct Agent {
pub mut:
pubkey string // pubkey using ed25519
address string // where we can find the agent
port int // default 9999
description string // optional
pubkey string // pubkey using ed25519
address string // where we can find the agent
port int // default 9999
description string // optional
status AgentStatus
services []AgentService // these are the public services
signature string // signature as done by private key of $address+$port+$description+$status
@@ -17,30 +17,30 @@ pub mut:
// AgentStatus represents the current state of an agent
pub struct AgentStatus {
pub mut:
guid string // unique id for the job
timestamp_first ourtime.Time // when agent came online
timestamp_last ourtime.Time // last time agent let us know that he is working
status AgentState // current state of the agent
guid string // unique id for the job
timestamp_first ourtime.OurTime // when agent came online
timestamp_last ourtime.OurTime // last time agent let us know that he is working
status AgentState // current state of the agent
}
// AgentService represents a service provided by an agent
pub struct AgentService {
pub mut:
actor string // name of the actor providing the service
actions []AgentServiceAction // available actions for this service
description string // optional description
actor string // name of the actor providing the service
actions []AgentServiceAction // available actions for this service
description string // optional description
status AgentServiceState // current state of the service
}
// AgentServiceAction represents an action that can be performed by a service
pub struct AgentServiceAction {
pub mut:
action string // which action
description string // optional description
params map[string]string // e.g. name:'name of the vm' ...
params_example map[string]string // e.g. name:'myvm'
status AgentServiceState // current state of the action
public bool // if everyone can use then true, if restricted means only certain people can use
action string // which action
description string // optional description
params map[string]string // e.g. name:'name of the vm' ...
params_example map[string]string // e.g. name:'myvm'
status AgentServiceState // current state of the action
public bool // if everyone can use then true, if restricted means only certain people can use
}
// AgentState represents the possible states of an agent

View File

@@ -4,9 +4,7 @@ import freeflowuniverse.herolib.core.redisclient
import freeflowuniverse.herolib.data.ourtime
import json
const (
agents_key = 'herorunner:agents' // Redis key for storing agents
)
const agents_key = 'herorunner:agents' // Redis key for storing agents
// AgentManager handles all agent-related operations
pub struct AgentManager {
@@ -17,13 +15,13 @@ mut:
// new creates a new Agent instance
pub fn (mut m AgentManager) new() Agent {
return Agent{
pubkey: '' // Empty pubkey to be filled by caller
port: 9999 // Default port
status: AgentStatus{
guid: ''
timestamp_first: ourtime.Time{}
timestamp_last: ourtime.Time{}
status: .ok
pubkey: '' // Empty pubkey to be filled by caller
port: 9999 // Default port
status: AgentStatus{
guid: ''
timestamp_first: ourtime.now()
timestamp_last: ourtime.OurTime{}
status: .ok
}
services: []AgentService{}
}
@@ -67,7 +65,7 @@ pub fn (mut m AgentManager) delete(pubkey string) ! {
pub fn (mut m AgentManager) update_status(pubkey string, status AgentState) ! {
mut agent := m.get(pubkey)!
agent.status.status = status
m.update(agent)!
m.set(agent)!
}
// get_by_service returns all agents that provide a specific service

View File

@@ -4,8 +4,7 @@ import freeflowuniverse.herolib.core.redisclient
import freeflowuniverse.herolib.data.ourtime
fn test_agents_model() {
mut runner:=new()!
mut runner := new()!
// Create a new agent using the manager
mut agent := runner.agents.new()
@@ -15,24 +14,24 @@ fn test_agents_model() {
// Create a service action
mut action := AgentServiceAction{
action: 'start'
description: 'Start a VM'
params: {
action: 'start'
description: 'Start a VM'
params: {
'name': 'string'
}
params_example: {
'name': 'myvm'
}
status: .ok
public: true
status: .ok
public: true
}
// Create a service
mut service := AgentService{
actor: 'vm_manager'
actions: [action]
actor: 'vm_manager'
actions: [action]
description: 'VM Management Service'
status: .ok
status: .ok
}
agent.services = [service]

View File

@@ -7,10 +7,10 @@ pub struct HeroRunner {
mut:
redis &redisclient.Redis
pub mut:
jobs &JobManager
agents &AgentManager
jobs &JobManager
agents &AgentManager
services &ServiceManager
groups &GroupManager
groups &GroupManager
}
// new creates a new HeroRunner instance
@@ -18,17 +18,17 @@ pub fn new() !&HeroRunner {
mut redis := redisclient.core_get()!
mut hr := &HeroRunner{
redis: redis
jobs: &JobManager{
redis: redis
jobs: &JobManager{
redis: redis
}
agents: &AgentManager{
agents: &AgentManager{
redis: redis
}
services: &ServiceManager{
redis: redis
}
groups: &GroupManager{
groups: &GroupManager{
redis: redis
}
}

View File

@@ -3,9 +3,7 @@ module model
import freeflowuniverse.herolib.core.redisclient
import json
const (
groups_key = 'herorunner:groups' // Redis key for storing groups
)
const groups_key = 'herorunner:groups' // Redis key for storing groups
// GroupManager handles all group-related operations
pub struct GroupManager {
@@ -16,7 +14,7 @@ mut:
// new creates a new Group instance
pub fn (mut m GroupManager) new() Group {
return Group{
guid: '' // Empty GUID to be filled by caller
guid: '' // Empty GUID to be filled by caller
members: []string{}
}
}
@@ -49,6 +47,7 @@ pub fn (mut m GroupManager) list() ![]Group {
return groups
}
// delete removes a group by its GUID
pub fn (mut m GroupManager) delete(guid string) ! {
m.redis.hdel(groups_key, guid)!
@@ -59,7 +58,7 @@ pub fn (mut m GroupManager) add_member(guid string, member string) ! {
mut group := m.get(guid)!
if member !in group.members {
group.members << member
m.update(group)!
m.set(group)!
}
}
@@ -67,38 +66,34 @@ pub fn (mut m GroupManager) add_member(guid string, member string) ! {
pub fn (mut m GroupManager) remove_member(guid string, member string) ! {
mut group := m.get(guid)!
group.members = group.members.filter(it != member)
m.update(group)!
m.set(group)!
}
// get_user_groups returns all groups that a user is a member of (directly or indirectly)
pub fn (mut m GroupManager) get_user_groups(user_pubkey string) ![]Group {
mut user_groups := []Group{}
mut checked_groups := map[string]bool{}
groups := m.list()!
// Recursive function to check group membership
fn check_group_membership(group Group, user string, groups []Group, mut checked map[string]bool, mut result []Group) {
if group.guid in checked {
return
}
checked[group.guid] = true
if user in group.members {
result << group
// Check parent groups
for parent_group in groups {
if group.guid in parent_group.members {
check_group_membership(parent_group, user, groups, mut checked, mut result)
}
}
}
}
// Check each group
for group in groups {
check_group_membership(group, user_pubkey, groups, mut checked_groups, mut user_groups)
}
return user_groups
}
// Recursive function to check group membership
fn check_group_membership(group Group, user string, groups []Group, mut checked map[string]bool, mut result []Group) {
if group.guid in checked {
return
}
checked[group.guid] = true
if user in group.members {
result << group
// Check parent groups
for parent_group in groups {
if group.guid in parent_group.members {
check_group_membership(parent_group, user, groups, mut checked, mut result)
}
}
}
}

View File

@@ -3,7 +3,7 @@ module model
import freeflowuniverse.herolib.core.redisclient
fn test_groups() {
mut runner:=new()!
mut runner := new()!
// Create a new group using the manager
mut group := runner.groups.new()
@@ -20,7 +20,7 @@ fn test_groups() {
subgroup.name = 'VM Administrators'
subgroup.description = 'VM management administrators'
runner.groups.add(subgroup)!
runner.groups.set(subgroup)!
// Add subgroup to main group
runner.groups.add_member(group.guid, subgroup.guid)!

View File

@@ -5,33 +5,33 @@ import freeflowuniverse.herolib.data.ourtime
// Job represents a task to be executed by an agent
pub struct Job {
pub mut:
guid string // unique id for the job
agents []string // the pub key of the agent(s) which will execute the command, only 1 will execute
source string // pubkey from the agent who asked for the job
guid string // unique id for the job
agents []string // the pub key of the agent(s) which will execute the command, only 1 will execute
source string // pubkey from the agent who asked for the job
circle string = 'default' // our digital life is organized in circles
context string = 'default' // is the high level context in which actors will execute the work inside a circle
actor string // e.g. vm_manager
action string // e.g. start
params map[string]string // e.g. id:10
timeout_schedule u16 = 60 // timeout before its picked up
timeout u16 = 3600 // timeout in sec
timeout_schedule u16 = 60 // timeout before its picked up
timeout u16 = 3600 // timeout in sec
log bool = true
ignore_error bool // means if error will just exit and not raise, there will be no error reporting
ignore_error_codes []int // of we want to ignore certain error codes
debug bool // if debug will get more context
retry int // default there is no debug
ignore_error bool // means if error will just exit and not raise, there will be no error reporting
ignore_error_codes []int // of we want to ignore certain error codes
debug bool // if debug will get more context
retry int // default there is no debug
status JobStatus
dependencies []JobDependency // will not execute until other jobs are done
dependencies []JobDependency // will not execute until other jobs are done
}
// JobStatus represents the current state of a job
pub struct JobStatus {
pub mut:
guid string // unique id for the job
created ourtime.Time // when we created the job
start ourtime.Time // when the job needs to start
end ourtime.Time // when the job ended, can be in error
status Status // current status of the job
guid string // unique id for the job
created ourtime.OurTime // when we created the job
start ourtime.OurTime // when the job needs to start
end ourtime.OurTime // when the job ended, can be in error
status Status // current status of the job
}
// JobDependency represents a dependency on another job

View File

@@ -4,9 +4,7 @@ import freeflowuniverse.herolib.core.redisclient
import freeflowuniverse.herolib.data.ourtime
import json
const (
jobs_key = 'herorunner:jobs' // Redis key for storing jobs
)
const jobs_key = 'herorunner:jobs' // Redis key for storing jobs
// JobManager handles all job-related operations
pub struct JobManager {
@@ -17,13 +15,13 @@ mut:
// new creates a new Job instance
pub fn (mut m JobManager) new() Job {
return Job{
guid: '' // Empty GUID to be filled by caller
guid: '' // Empty GUID to be filled by caller
status: JobStatus{
guid: ''
created: ourtime.Time{}
start: ourtime.Time{}
end: ourtime.Time{}
status: .created
guid: ''
created: ourtime.now()
start: ourtime.OurTime{}
end: ourtime.OurTime{}
status: .created
}
}
}
@@ -66,5 +64,5 @@ pub fn (mut m JobManager) delete(guid string) ! {
pub fn (mut m JobManager) update_status(guid string, status Status) ! {
mut job := m.get(guid)!
job.status.status = status
m.update(job)!
m.set(job)!
}

View File

@@ -4,7 +4,7 @@ import freeflowuniverse.herolib.core.redisclient
import freeflowuniverse.herolib.data.ourtime
fn test_jobs() {
mut runner:=new()!
mut runner := new()!
// Create a new job using the manager
mut job := runner.jobs.new()

View File

@@ -3,21 +3,21 @@ module model
// Service represents a service that can be provided by agents
pub struct Service {
pub mut:
actor string // name of the actor providing the service
actions []ServiceAction // available actions for this service
description string // optional description
status ServiceState // current state of the service
actor string // name of the actor providing the service
actions []ServiceAction // available actions for this service
description string // optional description
status ServiceState // current state of the service
acl ?ACL // access control list for the service
}
// ServiceAction represents an action that can be performed by a service
pub struct ServiceAction {
pub mut:
action string // which action
description string // optional description
params map[string]string // e.g. name:'name of the vm' ...
params_example map[string]string // e.g. name:'myvm'
acl ?ACL // if not used then everyone can use
action string // which action
description string // optional description
params map[string]string // e.g. name:'name of the vm' ...
params_example map[string]string // e.g. name:'myvm'
acl ?ACL // if not used then everyone can use
}
// ACL represents an access control list

View File

@@ -3,9 +3,7 @@ module model
import freeflowuniverse.herolib.core.redisclient
import json
const (
services_key = 'herorunner:services' // Redis key for storing services
)
const services_key = 'herorunner:services' // Redis key for storing services
// ServiceManager handles all service-related operations
pub struct ServiceManager {
@@ -16,9 +14,9 @@ mut:
// new creates a new Service instance
pub fn (mut m ServiceManager) new() Service {
return Service{
actor: '' // Empty actor name to be filled by caller
actor: '' // Empty actor name to be filled by caller
actions: []ServiceAction{}
status: .ok
status: .ok
}
}
@@ -60,7 +58,7 @@ pub fn (mut m ServiceManager) delete(actor string) ! {
pub fn (mut m ServiceManager) update_status(actor string, status ServiceState) ! {
mut service := m.get(actor)!
service.status = status
m.update(service)!
m.set(service)!
}
// get_by_action returns all services that provide a specific action

View File

@@ -3,7 +3,7 @@ module model
import freeflowuniverse.herolib.core.redisclient
fn test_services() {
mut runner:=new()!
mut runner := new()!
// Create a new service using the manager
mut service := runner.services.new()
@@ -13,26 +13,26 @@ fn test_services() {
// Create an ACL
mut ace := ACE{
groups: ['admin-group']
users: ['user-1-pubkey']
right: 'write'
users: ['user-1-pubkey']
right: 'write'
}
mut acl := ACL{
name: 'vm-acl'
ace: [ace]
ace: [ace]
}
// Create a service action
mut action := ServiceAction{
action: 'start'
description: 'Start a VM'
params: {
action: 'start'
description: 'Start a VM'
params: {
'name': 'string'
}
params_example: {
'name': 'myvm'
}
acl: acl
acl: acl
}
service.actions = [action]
@@ -59,13 +59,16 @@ fn test_services() {
assert services[0].actor == service.actor
// Test access control
has_access := runner.services.check_access(service.actor, 'start', 'user-1-pubkey', [])!
has_access := runner.services.check_access(service.actor, 'start', 'user-1-pubkey',
[])!
assert has_access == true
has_group_access := runner.services.check_access(service.actor, 'start', 'user-2-pubkey', ['admin-group'])!
has_group_access := runner.services.check_access(service.actor, 'start', 'user-2-pubkey',
['admin-group'])!
assert has_group_access == true
no_access := runner.services.check_access(service.actor, 'start', 'user-3-pubkey', [])!
no_access := runner.services.check_access(service.actor, 'start', 'user-3-pubkey',
[])!
assert no_access == false
// List all services