Files
herolib/lib/builder/node_commands.v
2024-12-30 17:36:22 +02:00

228 lines
7.1 KiB
V

module builder
import freeflowuniverse.herolib.core.texttools
import crypto.md5
import time
import freeflowuniverse.herolib.data.ourtime
import freeflowuniverse.herolib.ui.console
// import freeflowuniverse.herolib.osal
// check command exists on the platform, knows how to deal with different platforms
pub fn (mut node Node) cmd_exists(cmd string) bool {
return node.exec_ok('which ${cmd}')
}
pub struct NodeExecCmd {
pub mut:
name string = 'default'
cmd string
period int // period in which we check when this was done last, if 0 then period is indefinite
reset bool = true // means do again or not
remove_installer bool = true // delete the installer
description string
stdout bool = true
checkkey string // if used will use this one in stead of hash of cmd, to check if was executed already
tmpdir string
ignore_error_codes []int
}
// return the ipaddress as known on the public side
// is using resolver4.opendns.com
pub fn (mut node Node) ipaddr_pub_get() !string {
if !node.done_exists('ipaddr') {
cmd := 'dig @resolver4.opendns.com myip.opendns.com +short'
res := node.exec(cmd: cmd, stdout: false)!
node.done_set('ipaddr', res.trim('\n').trim(' \n'))!
}
mut ipaddr := node.done_get('ipaddr') or { return 'Error: ipaddr is none' }
return ipaddr.trim('\n').trim(' \n')
}
// cmd: cmd to execute .
// period in sec, e.g. if 3600, means will only execute this if not done yet within the hour .
// .
// ARGS: .
//```
// struct NodeExecCmd{
// cmd string
// period int //period in which we check when this was done last, if 0 then period is indefinite
// reset bool = true
// description string
// checkkey string //if used will use this one in stead of hash of cmd, to check if was executed already
// }
// ```
pub fn (mut node Node) exec_cmd(args_ NodeExecCmd) !string {
// console.print_debug(args)
mut args := args_
mut cmd := args.cmd
mut now_epoch := time.now().unix()
mut now_str := now_epoch.str()
if cmd.contains('\n') {
cmd = texttools.dedent(cmd)
}
mut hhash := ''
if args.checkkey.len > 0 {
hhash = args.checkkey
} else {
hhash = md5.hexhash(cmd)
}
mut description := args.description
if description == '' {
description = cmd
if description.contains('\n') {
description = '\n${description}\n'
}
}
if !args.reset && node.done_exists('exec_${hhash}') {
if args.period == 0 {
console.print_debug(' - exec cmd:${description} on ${node.name}: was already done, period indefinite.')
return node.done_get('exec_${hhash}') or { '' }
}
nodedone := node.done_get_str('exec_${hhash}')
splitted := nodedone.split('|')
if splitted.len != 2 {
panic("Cannot return from done on exec needs to have |, now \n'${nodedone}' ")
}
exec_last_time := splitted[0].int()
lastoutput := splitted[1]
assert exec_last_time > 10000
// console.print_debug(args)
// console.print_debug(" - check exec cmd:$cmd on $node.name: time:$exec_last_time")
if exec_last_time > now_epoch - args.period {
hours := args.period / 3600
console.print_header('exec cmd:${description} on ${node.name}: was already done, period ${hours} h')
return lastoutput
}
}
if args.tmpdir.len == 0 {
if 'TMPDIR' !in node.environment {
args.tmpdir = '/tmp'
} else {
args.tmpdir = node.environment['TMPDIR']
}
}
now := ourtime.now()
r_path := '${args.tmpdir}/installer_${args.name}_${now.key()}.sh'
node.file_write(r_path, cmd)!
cmd = "mkdir -p ${args.tmpdir} && cd ${args.tmpdir} && export TMPDIR='${args.tmpdir}' && bash ${r_path}"
if args.remove_installer {
cmd += ' && rm -f ${r_path}'
}
$if debug {
console.print_header('exec ${r_path} on ${node.name}')
}
res := node.exec(cmd: cmd, stdout: args.stdout) or {
return error('cmd:\n${args.cmd}\n${err.msg()}')
}
node.done_set('exec_${hhash}', '${now_str}|${res}')!
return res
}
// check if we can execute and there is not errorcode
pub fn (mut node Node) exec_ok(cmd string) bool {
// TODO: need to put in support for multiline text files
if cmd.contains('\n') {
panic('cannot have \\n in cmd. ${cmd}, use exec function instead')
}
node.exec_silent(cmd) or {
// see if it executes ok, if cmd not found is false
return false
}
// console.print_debug(e)
return true
}
fn (mut node Node) platform_load() ! {
console.print_header('platform load ${node.name}')
cmd := '
if [[ "\$OSTYPE" == "darwin"* ]]; then
export OSNAME="darwin"
elif [ -e /etc/os-release ]; then
# Read the ID field from the /etc/os-release file
export OSNAME=$(grep \'^ID=\' /etc/os-release | cut -d= -f2)
if [ "\${os_id,,}" == "ubuntu" ]; then
export OSNAME="ubuntu"
fi
if [ "\${OSNAME}" == "archarm" ]; then
export OSNAME="arch"
fi
else
echo "Unable to determine the operating system."
exit 1
fi
export HOSTNAME=\$(hostname)
export UNAME=\$(uname -m)
echo ***\${OSNAME}:\${UNAME}:\${HOSTNAME}
'
out := node.exec_cmd(cmd: cmd, name: 'platform_load', stdout: false)!
out2 := out.split_into_lines().map(if it.starts_with('***') { it.trim_left('*') } else { '' }).first()
if out2.count(':') != 2 {
panic('platform loader bug')
}
splitted := out2.split(':').map(it.trim_space())
osname := splitted[0]
cputype := splitted[1]
node.hostname = splitted[2]
if cputype == 'x86_64' {
node.cputype = CPUType.intel
} else if cputype == 'arm64' || cputype == 'aarch64' {
node.cputype = CPUType.arm
} else {
return error("did not find cpu type, implement more types e.g. 32 bit. found: '${cputype}'")
}
if osname == 'arch' {
node.platform = PlatformType.arch
} else if osname == 'ubuntu' || osname == 'debian' {
node.platform = PlatformType.ubuntu
} else if osname == 'darwin' {
node.platform = PlatformType.osx
} else if osname == 'alpine' {
node.platform = PlatformType.alpine
} else {
console.print_stderr(osname)
panic('only ubuntu, arch and osx supported for now')
}
console.print_debug('platform loaded')
}
pub fn (mut node Node) package_refresh() ! {
if node.platform == PlatformType.ubuntu {
console.print_header('package refresh')
node.exec(cmd: 'apt-get update', stdout: false) or {
return error('could not update packages list\nerror:\n${err}')
}
}
}
pub fn (mut node Node) package_install(package Package) ! {
name := package.name
if node.platform == PlatformType.osx {
node.exec(cmd: 'brew install ${name}') or {
return error('could not install package:${package.name}\nerror:\n${err}')
}
} else if node.platform == PlatformType.ubuntu {
node.exec(cmd: 'apt install -y ${name}') or {
return error('could not install package:${package.name}\nerror:\n${err}')
}
} else if node.platform == PlatformType.alpine {
node.exec(cmd: 'apk install ${name}') or {
return error('could not install package:${package.name}\nerror:\n${err}')
}
} else if node.platform == PlatformType.alpine {
node.exec(cmd: 'pacman -Su ${name}') or {
return error('could not install package:${package.name}\nerror:\n${err}')
}
} else {
panic('only ubuntu, alpine,arch and osx supported for now')
}
}