Merge branch 'development' of github.com:freeflowuniverse/herolib into development

This commit is contained in:
2025-01-05 14:02:14 +01:00
13 changed files with 209 additions and 269 deletions

View File

@@ -2,6 +2,7 @@ module ipaddress
import os
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.ui.console
pub struct IPNetwork {
@@ -124,7 +125,7 @@ pub mut:
// PingArgs: retry & timeout
// retry default 1
// timeout default 1000 (msec)
pub fn (mut ipaddr IPAddress) ping(args_ PingArgs) bool {
pub fn (mut ipaddr IPAddress) ping(args_ PingArgs) !bool {
mut args := args_
if args.retry == 0 {
args.retry = 1

View File

@@ -58,24 +58,28 @@ pub fn (mut gs GitStructure) gitlocation_from_url(url string) !GitLocation {
mut path := ''
mut branch_or_tag := ''
// Deal with path and anchor
if parts.len > 4 {
path = parts[4..].join('/')
if path.contains('#') {
parts2 := path.split('#')
if parts2.len == 2 {
path = parts2[0]
anchor = parts2[1]
} else {
return error("git: url badly formatted, more than 1 '#' in ${url}")
// Extract branch if available
for i := 0; i < parts.len; i++ {
if parts[i] == 'refs' || parts[i] == 'tree' {
if i + 1 < parts.len {
branch_or_tag = parts[i + 1]
}
if i + 2 < parts.len {
path = parts[(i + 2)..].join('/')
}
break
}
}
// Extract branch if available
if parts.len > 3 {
branch_or_tag = parts[3]
parts[2] = parts[2].replace('.git', '')
// Deal with path and anchor
if path.contains('#') {
parts2 := path.split('#')
if parts2.len == 2 {
path = parts2[0]
anchor = parts2[1]
} else {
return error("git: url badly formatted, more than 1 '#' in ${url}")
}
}
// Validate parts
@@ -98,7 +102,7 @@ pub fn (mut gs GitStructure) gitlocation_from_url(url string) !GitLocation {
}
}
// Return a herolib path object on the filesystem pointing to the locator
// Return a crystallib path object on the filesystem pointing to the locator
pub fn (mut l GitLocation) patho() !pathlib.Path {
mut addrpath := pathlib.get_dir(path: '${l.provider}/${l.account}/${l.name}', create: false)!
if l.path.len > 0 {

View File

@@ -3,7 +3,6 @@ module gittools
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.osal
import os
import time
// GitRepo holds information about a single Git repository.
@[heap]
@@ -146,7 +145,7 @@ pub fn (mut repo GitRepo) checkout() ! {
repo.exec('git checkout tags/${repo.status_wanted.tag}')!
}
if repo.status_wanted.branch.len > 0 {
repo.exec('git checkout ${repo.status_wanted.tag}')!
repo.exec('git checkout ${repo.status_wanted.branch}')!
}
}

View File

@@ -16,11 +16,13 @@ pub fn (mut gitstructure GitStructure) clone(args GitCloneArgs) !&GitRepo {
if args.url.len == 0 {
return error('url needs to be specified when doing a clone.')
}
console.print_header('Git clone from the URL: ${args.url}.')
git_location := gitstructure.gitlocation_from_url(args.url)!
mut repo := gitstructure.repo_new_from_gitlocation(git_location)!
repo.status_wanted.url = args.url
repo.status_wanted.branch = git_location.branch_or_tag
if args.sshkey.len > 0 {
repo.set_sshkey(args.sshkey)!
@@ -33,13 +35,16 @@ pub fn (mut gitstructure GitStructure) clone(args GitCloneArgs) !&GitRepo {
extra = '--depth 1 --no-single-branch '
}
cmd := 'cd ${parent_dir} && git clone ${extra} ${repo.get_repo_url()!} ${repo.name}'
cmd := 'cd ${parent_dir} && git clone ${extra} ${repo.get_http_url()!} ${repo.name}'
result := os.execute(cmd)
if result.exit_code != 0 {
return error('Cannot clone the repository due to: \n${result.output}')
}
repo.load()!
if repo.need_checkout() {
repo.checkout()!
}
console.print_green("The repository '${repo.name}' cloned into ${parent_dir}.")

View File

@@ -9,8 +9,8 @@ import json
import os
// checks if a certain version or above is installed
fn installed_() !bool {
res := os.execute('${osal.profile_path_source_and()!} livekit-server -v')
fn installed() !bool {
res := os.execute('${osal.profile_path_source_and()} livekit-server -v')
if res.exit_code != 0 {
return false
}
@@ -25,7 +25,7 @@ fn installed_() !bool {
return true
}
fn install_() ! {
fn install() ! {
console.print_header('install livekit')
mut installer := get()!
osal.execute_silent('
@@ -45,7 +45,7 @@ fn startupcmd() ![]zinit.ZProcessNewArgs {
return res
}
fn running_() !bool {
fn running() !bool {
mut installer := get()!
myport := installer.nr * 2 + 7880
@@ -90,7 +90,7 @@ fn stop_post() ! {
// Post-stop cleanup if needed
}
fn destroy_() ! {
fn destroy() ! {
mut installer := get()!
os.rm(installer.configpath) or {}
os.rm('livekit-server') or {}

View File

@@ -2,8 +2,6 @@ module livekit
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.data.encoderhero
import freeflowuniverse.herolib.sysadmin.startupmanager
import freeflowuniverse.herolib.osal.zinit
import time
@@ -18,127 +16,89 @@ __global (
@[params]
pub struct ArgsGet {
pub mut:
name string
name string = 'default'
}
fn args_get(args_ ArgsGet) ArgsGet {
mut model := args_
if model.name == '' {
model.name = livekit_default
mut args := args_
if args.name == '' {
args.name = livekit_default
}
if model.name == '' {
model.name = 'default'
if args.name == '' {
args.name = 'default'
}
return model
return args
}
pub fn get(args_ ArgsGet) !&LivekitServer {
mut args := args_get(args_)
if args.name !in livekit_global {
if args.name == 'default' {
if !config_exists(args) {
if default {
mut context := base.context() or { panic('bug') }
context.hero_config_set('livekit', model.name, heroscript_default()!)!
}
if !config_exists() {
if default {
config_save()!
}
load(args)!
}
config_load()!
}
return livekit_global[args.name] or {
println(livekit_global)
panic('could not get config for ${args.name} with name:${model.name}')
panic('bug in get from factory: ')
}
}
// set the model in mem and the config on the filesystem
pub fn set(o LivekitServer) ! {
mut o2 := obj_init(o)!
livekit_global[o.name] = &o2
livekit_default = o.name
}
// check we find the config on the filesystem
pub fn exists(args_ ArgsGet) bool {
mut model := args_get(args_)
fn config_exists(args_ ArgsGet) bool {
mut args := args_get(args_)
mut context := base.context() or { panic('bug') }
return context.hero_config_exists('livekit', model.name)
return context.hero_config_exists('livekit', args.name)
}
// load the config error if it doesn't exist
pub fn load(args_ ArgsGet) ! {
mut model := args_get(args_)
fn config_load(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()!
mut heroscript := context.hero_config_get('livekit', model.name)!
mut heroscript := context.hero_config_get('livekit', args.name)!
play(heroscript: heroscript)!
}
// save the config to the filesystem in the context
pub fn save(o LivekitServer) ! {
fn config_save(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()!
heroscript := encoderhero.encode[LivekitServer](o)!
context.hero_config_set('livekit', model.name, heroscript)!
context.hero_config_set('livekit', args.name, heroscript_default()!)!
}
fn set(o LivekitServer) ! {
mut o2 := obj_init(o)!
livekit_global['default'] = &o2
}
@[params]
pub struct PlayArgs {
pub mut:
name string = 'default'
heroscript string // if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
start bool
stop bool
restart bool
delete bool
configure bool // make sure there is at least one installed
}
pub fn play(args_ PlayArgs) ! {
mut model := args_
mut args := args_
if model.heroscript == '' {
model.heroscript = heroscript_default()!
if args.heroscript == '' {
args.heroscript = heroscript_default()!
}
mut plbook := model.plbook or { playbook.new(text: model.heroscript)! }
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }
mut configure_actions := plbook.find(filter: 'livekit.configure')!
if configure_actions.len > 0 {
for config_action in configure_actions {
mut p := config_action.params
mut install_actions := plbook.find(filter: 'livekit.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
mut p := install_action.params
mycfg := cfg_play(p)!
console.print_debug('install action livekit.configure\n${mycfg}')
set(mycfg)!
save(mycfg)!
}
}
mut other_actions := plbook.find(filter: 'livekit.')!
for other_action in other_actions {
if other_action.name in ['destroy', 'install', 'build'] {
mut p := other_action.params
reset := p.get_default_false('reset')
if other_action.name == 'destroy' || reset {
console.print_debug('install action livekit.destroy')
destroy_()!
}
if other_action.name == 'install' {
console.print_debug('install action livekit.install')
install_()!
}
}
if other_action.name in ['start', 'stop', 'restart'] {
mut p := other_action.params
name := p.get('name')!
mut livekit_obj := get(name: name)!
console.print_debug('action object:\n${livekit_obj}')
if other_action.name == 'start' {
console.print_debug('install action livekit.${other_action.name}')
livekit_obj.start()!
}
if other_action.name == 'stop' {
console.print_debug('install action livekit.${other_action.name}')
livekit_obj.stop()!
}
if other_action.name == 'restart' {
console.print_debug('install action livekit.${other_action.name}')
livekit_obj.restart()!
}
}
}
}
@@ -153,28 +113,6 @@ pub fn (mut self LivekitServer) reload() ! {
self = obj_init(self)!
}
fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManager {
// unknown
// screen
// zinit
// tmux
// systemd
match cat {
.zinit {
console.print_debug('startupmanager: zinit')
return startupmanager.get(cat: .zinit)!
}
.systemd {
console.print_debug('startupmanager: systemd')
return startupmanager.get(cat: .systemd)!
}
else {
console.print_debug('startupmanager: auto')
return startupmanager.get()!
}
}
}
pub fn (mut self LivekitServer) start() ! {
switch(self.name)
if self.running()! {
@@ -183,21 +121,13 @@ pub fn (mut self LivekitServer) start() ! {
console.print_header('livekit start')
if !installed_()! {
install_()!
}
configure()!
start_pre()!
mut sm := startupmanager.get()!
for zprocess in startupcmd()! {
mut sm := startupmanager_get(zprocess.startuptype)!
console.print_debug('starting livekit with ${zprocess.startuptype}...')
sm.new(zprocess)!
sm.start(zprocess.name)!
}
@@ -212,17 +142,17 @@ pub fn (mut self LivekitServer) start() ! {
return error('livekit did not install properly.')
}
pub fn (mut self LivekitServer) install_start(model InstallArgs) ! {
pub fn (mut self LivekitServer) install_start(args RestartArgs) ! {
switch(self.name)
self.install(model)!
self.install(args)!
self.start()!
}
pub fn (mut self LivekitServer) stop() ! {
switch(self.name)
stop_pre()!
mut sm := startupmanager.get()!
for zprocess in startupcmd()! {
mut sm := startupmanager_get(zprocess.startuptype)!
sm.stop(zprocess.name)!
}
stop_post()!
@@ -236,10 +166,10 @@ pub fn (mut self LivekitServer) restart() ! {
pub fn (mut self LivekitServer) running() !bool {
switch(self.name)
mut sm := startupmanager.get()!
// walk over the generic processes, if not running return
for zprocess in startupcmd()! {
mut sm := startupmanager_get(zprocess.startuptype)!
r := sm.running(zprocess.name)!
if r == false {
return false
@@ -254,6 +184,20 @@ pub mut:
reset bool
}
pub fn (mut self LivekitServer) install(args InstallArgs) ! {
switch(self.name)
if args.reset || (!installed()!) {
install()!
}
}
pub fn (mut self LivekitServer) destroy() ! {
switch(self.name)
self.stop()!
destroy()!
}
// switch instance to be used for livekit
pub fn switch(name string) {
livekit_default = name

View File

@@ -10,8 +10,7 @@ import os
// checks if a certain version or above is installed
fn installed_() !bool {
res := os.execute('${osal.profile_path_source_and()!} go version')
res := os.execute('/bin/bash -c "go version"')
if res.exit_code == 0 {
r := res.output.split_into_lines()
.filter(it.contains('go version'))

View File

@@ -1,11 +1,8 @@
module golang
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.sysadmin.startupmanager
import freeflowuniverse.herolib.osal.zinit
import time
import freeflowuniverse.herolib.ui.console
__global (
golang_global map[string]&GolangInstaller
@@ -14,29 +11,66 @@ __global (
/////////FACTORY
@[params]
pub struct ArgsGet {
pub mut:
name string
}
pub fn get(args_ ArgsGet) !&GolangInstaller {
return &GolangInstaller{}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManager {
// unknown
// screen
// zinit
// tmux
// systemd
match cat {
.zinit {
console.print_debug('startupmanager: zinit')
return startupmanager.get(cat: .zinit)!
}
.systemd {
console.print_debug('startupmanager: systemd')
return startupmanager.get(cat: .systemd)!
}
else {
console.print_debug('startupmanager: auto')
return startupmanager.get()!
}
}
}
@[params]
pub struct InstallArgs {
pub mut:
reset bool
}
pub fn install(args InstallArgs) ! {
if args.reset {
destroy()!
}
if !(installed_()!) {
pub fn (mut self GolangInstaller) install(args InstallArgs) ! {
switch(self.name)
if args.reset || (!installed_()!) {
install_()!
}
}
pub fn destroy() ! {
pub fn (mut self GolangInstaller) build() ! {
switch(self.name)
build_()!
}
pub fn (mut self GolangInstaller) destroy() ! {
switch(self.name)
destroy_()!
}
pub fn build() ! {
build_()!
// switch instance to be used for golang
pub fn switch(name string) {
golang_default = name
}

View File

@@ -10,7 +10,7 @@ import os
// checks if a certain version or above is installed
fn installed_() !bool {
res := os.execute('${osal.profile_path_source_and()!} griddriver --version')
res := os.execute('/bin/bash -c "griddriver --version"')
if res.exit_code != 0 {
return false
}

View File

@@ -1,6 +1,7 @@
module tfgrid3deployer
import freeflowuniverse.herolib.threefold.grid.models as grid_models
import freeflowuniverse.herolib.threefold.gridproxy.model as gridproxy_models
import freeflowuniverse.herolib.threefold.grid
import freeflowuniverse.herolib.ui.console
import compress.zlib
@@ -98,14 +99,16 @@ fn (mut self TFDeployment) set_nodes() ! {
}
nodes := filter_nodes(
node_ids: node_ids
healthy: true
free_mru: convert_to_gigabytes(u64(vm.requirements.memory))
total_cru: u64(vm.requirements.cpu)
free_ips: if vm.requirements.public_ip4 { u64(1) } else { none }
has_ipv6: if vm.requirements.public_ip6 { vm.requirements.public_ip6 } else { none }
status: 'up'
features: if vm.requirements.public_ip4 { [] } else { ['zmachine'] }
node_ids: node_ids
healthy: true
free_mru: convert_to_gigabytes(u64(vm.requirements.memory))
total_cru: u64(vm.requirements.cpu)
free_sru: convert_to_gigabytes(u64(vm.requirements.size))
available_for: gridproxy_models.OptionU64(u64(self.deployer.twin_id))
free_ips: if vm.requirements.public_ip4 { u64(1) } else { none }
has_ipv6: if vm.requirements.public_ip6 { vm.requirements.public_ip6 } else { none }
status: 'up'
features: if vm.requirements.public_ip4 { [] } else { ['zmachine'] }
)!
if nodes.len == 0 {
@@ -114,40 +117,42 @@ fn (mut self TFDeployment) set_nodes() ! {
}
return error('Requested the Grid Proxy and no nodes found.')
}
idx := rand.u32() % u32(nodes.len)
// println('chodes node: ${}')
vm.node_id = u32(nodes[idx].node_id)
vm.node_id = self.pick_node(nodes) or { return error('Failed to pick valid node: ${err}') }
}
for mut zdb in self.zdbs {
size := convert_to_gigabytes(u64(zdb.requirements.size))
nodes := filter_nodes(
free_sru: size
status: 'up'
healthy: true
node_id: zdb.requirements.node_id
free_sru: convert_to_gigabytes(u64(zdb.requirements.size))
status: 'up'
healthy: true
node_id: zdb.requirements.node_id
available_for: gridproxy_models.OptionU64(u64(self.deployer.twin_id))
)!
if nodes.len == 0 {
return error('Requested the Grid Proxy and no nodes found.')
}
zdb.node_id = u32(nodes[0].node_id)
zdb.node_id = self.pick_node(nodes) or { return error('Failed to pick valid node: ${err}') }
}
for mut webname in self.webnames {
nodes := filter_nodes(
domain: true
status: 'up'
healthy: true
node_id: webname.requirements.node_id
domain: true
status: 'up'
healthy: true
node_id: webname.requirements.node_id
available_for: gridproxy_models.OptionU64(u64(self.deployer.twin_id))
)!
if nodes.len == 0 {
return error('Requested the Grid Proxy and no nodes found.')
}
webname.node_id = u32(nodes[0].node_id)
webname.node_id = self.pick_node(nodes) or {
return error('Failed to pick valid node: ${err}')
}
}
}
@@ -200,7 +205,7 @@ fn (mut self TFDeployment) finalize_deployment(setup DeploymentSetup) ! {
}
if create_name_contracts.len > 0 || create_deployments.len > 0 {
console.print_header('Batch deploying the deployment')
console.print_header('Attempting batch deployment')
created_name_contracts_map, ret_dls := self.deployer.batch_deploy(create_name_contracts, mut
create_deployments, none)!
@@ -469,86 +474,35 @@ pub fn (mut self TFDeployment) list_deployments() !map[u32]grid_models.Deploymen
return dls
}
// fn (mut self TFDeployment) vm_delete(vm_name string) ! {
// // delete myself, check on TFChain that deletion was indeed done
// vm := self.vm_get(vm_name)!
fn (mut self TFDeployment) pick_node(nodes []gridproxy_models.Node) !u32 {
mut node_id := ?u32(none)
mut checked := []bool{len: nodes.len}
mut checked_cnt := 0
for checked_cnt < nodes.len {
idx := int(rand.u32() % u32(nodes.len))
if checked[idx] {
continue
}
// // get all deployments
// mut dls := self.list_deployments()!
checked[idx] = true
checked_cnt += 1
if self.ping_node(u32(nodes[idx].twin_id)) {
node_id = u32(nodes[idx].node_id)
break
}
}
// // load network
// mut network_handler := NetworkHandler{
// deployer: self.deployer
// }
if v := node_id {
return v
} else {
return error('No node is reachable.')
}
}
// // network_handler.load_network_state(dls)!
// // remove vm workload
// mut vm_dl := dls[vm.node_id]
// mut public_ip_name := ''
// for idx, workload in vm_dl.workloads {
// if workload.name == vm_name {
// zmachine := json.decode(grid_models.Zmachine, workload.data)!
// public_ip_name = zmachine.network.public_ip
// vm_dl.workloads[idx], vm_dl.workloads[vm_dl.workloads.len - 1] = vm_dl.workloads[vm_dl.workloads.len - 1], vm_dl.workloads[idx]
// vm_dl.workloads.delete_last()
// break
// }
// }
// for idx, workload in vm_dl.workloads {
// if workload.name == public_ip_name {
// vm_dl.workloads[idx], vm_dl.workloads[vm_dl.workloads.len - 1] = vm_dl.workloads[vm_dl.workloads.len - 1], vm_dl.workloads[idx]
// vm_dl.workloads.delete_last()
// break
// }
// }
// // decide if we want to remove the node
// if vm_dl.workloads.len == 1 && vm_dl.workloads[0].type_ == grid_models.workload_types.network {
// mut ipv4_nodes := 0
// for _, endpoint in network_handler.endpoints {
// if endpoint.split('.').len == 4 {
// ipv4_nodes += 1
// }
// }
// if network_handler.public_node == vm.node_id && (ipv4_nodes > 1
// || network_handler.hidden_nodes.len == 0
// || (network_handler.nodes.len == 2 && network_handler.hidden_nodes.len == 1)
// || (ipv4_nodes == 1 && network_handler.hidden_nodes.len > 0)) {
// // we can remove the node
// dls.delete(vm.node_id)
// network_handler.remove_node(vm.node_id)!
// }
// }
// // use network handler to prepare network
// network_workloads := network_handler.generate_workloads(self.dl_versions)!
// // replace deloyments network workloads with the ones coming from network handler
// for node_id, mut dl in dls {
// network_wl := network_workloads[node_id] or { continue }
// for id, _ in dl.workloads {
// if dl.workloads[id].name == network_wl.name {
// dl.workloads[id] = network_wl
// }
// }
// }
// // TODO: update deployments
// /*
// what issues we face:
// 1. Delete the network workload if not needed
// 2. Remove the vm node peer from the other deployments if contract is deleted
// 3. Deploy an access node if the deleted contract was an access node
// node1 := dl -> hidden
// node2 := dl -> hidden
// node3 := dl -> public // will delete it, we need to deploy another access node for node1 and node2
// node1 := dl -> public // Assign node1 instead of node3 and delete node1
// node2 := dl -> hidden
// node3 := dl -> public // will delete it, we need to deploy another access node for node1 and node2
// */
// }
fn (mut self TFDeployment) ping_node(twin_id u32) bool {
if _ := self.deployer.client.get_zos_version(twin_id) {
return true
} else {
return false
}
}