diff --git a/examples/virt/hetzner/.gitignore b/examples/virt/hetzner/.gitignore new file mode 100644 index 00000000..240229c5 --- /dev/null +++ b/examples/virt/hetzner/.gitignore @@ -0,0 +1 @@ +hetzner_example \ No newline at end of file diff --git a/examples/virt/hetzner/hetzner_example b/examples/virt/hetzner/hetzner_example index 397ac6b2..8bff4099 100755 Binary files a/examples/virt/hetzner/hetzner_example and b/examples/virt/hetzner/hetzner_example differ diff --git a/examples/virt/hetzner/hetzner_example.vsh b/examples/virt/hetzner/hetzner_example.vsh index 8b8ebc96..4b2598d8 100755 --- a/examples/virt/hetzner/hetzner_example.vsh +++ b/examples/virt/hetzner/hetzner_example.vsh @@ -45,17 +45,20 @@ println(serverinfo) // cl.server_reset(name:"kristof2",wait:true)! -cl.server_rescue(name:"kristof2",wait:true, hero_install:true,sshkey_name:"kristof")! +//don't forget to specify the keyname needed +// cl.server_rescue(name:"kristof2",wait:true, hero_install:true,sshkey_name:"kristof")! -mut ks:=cl.keys_get()! -println(ks) +// mut ks:=cl.keys_get()! +// println(ks) -console.print_header('SSH login') -mut b := builder.new()! -mut n := b.node_new(ipaddr: serverinfo.server_ip)! +// console.print_header('SSH login') +// mut b := builder.new()! +// mut n := b.node_new(ipaddr: serverinfo.server_ip)! //this will put hero in debug mode on the system -n.hero_install(compile:true)! +// n.hero_install(compile:true)! // n.shell("")! +cl.ubuntu_install(name:"kristof2",wait:true, hero_install:true,sshkey_name:"kristof")! + diff --git a/lib/builder/bootstrapper.v b/lib/builder/bootstrapper.v index 9d078cbb..c98b0d09 100644 --- a/lib/builder/bootstrapper.v +++ b/lib/builder/bootstrapper.v @@ -59,7 +59,7 @@ pub fn (mut node Node) hero_install(args HeroInstallArgs) ! { mut todo := []string{} if ! args.compile { - todo << "curl https://raw.githubusercontent.com/freeflowuniverse/herolib/refs/heads/development/install_herolib.sh > /tmp/install.sh" + todo << "curl https://raw.githubusercontent.com/freeflowuniverse/herolib/refs/heads/development/install_hero.sh > /tmp/install.sh" todo << "bash /tmp/install.sh" }else{ todo << "curl 'https://raw.githubusercontent.com/freeflowuniverse/herolib/refs/heads/development/install_v.sh' > /tmp/install_v.sh" diff --git a/lib/builder/executor_ssh.v b/lib/builder/executor_ssh.v index 1e795b99..07fb16e9 100644 --- a/lib/builder/executor_ssh.v +++ b/lib/builder/executor_ssh.v @@ -7,6 +7,7 @@ import freeflowuniverse.herolib.osal.rsync import freeflowuniverse.herolib.core.pathlib import freeflowuniverse.herolib.data.ipaddress import freeflowuniverse.herolib.ui.console +import freeflowuniverse.herolib.core.texttools @[heap] pub struct ExecutorSSH { @@ -52,7 +53,16 @@ pub fn (mut executor ExecutorSSH) exec(args_ ExecArgs) !string { if executor.ipaddr.port > 10 { port = '-p ${executor.ipaddr.port}' } + + if args.cmd.contains("\n"){ + //need to upload the file first + args.cmd = texttools.dedent(args.cmd) + executor.file_write('/tmp/toexec.sh', args.cmd)! + args.cmd = "bash /tmp/toexec.sh" + } + args.cmd = 'ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${executor.user}@${executor.ipaddr.addr} ${port} "${args.cmd}"' + res := osal.exec(cmd: args.cmd, stdout: args.stdout, debug: executor.debug)! return res.output } @@ -63,7 +73,15 @@ pub fn (mut executor ExecutorSSH) exec_interactive(args_ ExecArgs) ! { if executor.ipaddr.port > 10 { port = '-p ${executor.ipaddr.port}' } + + if args.cmd.contains("\n"){ + args.cmd = texttools.dedent(args.cmd) + //need to upload the file first + executor.file_write('/tmp/toexec.sh', args.cmd)! + args.cmd = "bash /tmp/toexec.sh" + } args.cmd = 'ssh -tt -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${executor.user}@${executor.ipaddr.addr} ${port} "${args.cmd}"' + console.print_debug(args.cmd) osal.execute_interactive(args.cmd)! } diff --git a/lib/osal/core/net.v b/lib/osal/core/net.v index 9dfc6b22..5eecf829 100644 --- a/lib/osal/core/net.v +++ b/lib/osal/core/net.v @@ -70,6 +70,49 @@ pub fn ping(args PingArgs) !PingResult { return .ok } + +@[params] +pub struct RebootWaitArgs { +pub mut: + address string @[required] // 192.168.8.8 + timeout_down i64 = 2 // total time in seconds to wait till its down + timeout_up i64 = 60 * 5 +} + +// test if a tcp port answers +//``` +// address string //192.168.8.8 +// port int = 22 +// timeout u16 = 2000 // total time in milliseconds to keep on trying +//``` +pub fn reboot_wait(args RebootWaitArgs) ! { + start_time := time.now().unix_milli() + mut run_time := 0.0 + for true { + console.print_debug("Waiting for server to go down...") + run_time = time.now().unix_milli() + if run_time > start_time + args.timeout_down { + return error("timeout in waiting for server down") + } + if ping(address:args.address)! == .timeout { + break + } + time.sleep(1) + } + for true { + console.print_debug("Waiting for server to come back up...") + run_time = time.now().unix_milli() + if run_time > start_time + args.timeout_up { + return error("timeout in waiting for server up") + } + if ping(address:args.address)! == .ok { + break + } + time.sleep(1) + } +} + + @[params] pub struct TcpPortTestArgs { pub mut: diff --git a/lib/virt/hetznermanager/prepare.v b/lib/virt/hetznermanager/prepare.v deleted file mode 100644 index 2293bda6..00000000 --- a/lib/virt/hetznermanager/prepare.v +++ /dev/null @@ -1,63 +0,0 @@ -module hetznermanager - -// TODO: couldn't get ssh lib to work - -// pub fn (h HetznerManager) server_prepare(name string) !bool { -// srvs := h.servers_list()! - -// // console.print_debug(srvs) -// mut srvid := 0 -// mut srvip := "" - -// for s in srvs { -// if s.server.server_name == name { -// // console.print_debug(s) -// srvid = s.server.server_number -// srvip = s.server.server_ip -// } -// } - -// if srvid == 0 { -// panic("could not find server") -// } - -// console.print_debug("[+] request rescue mode") -// resc := h.server_rescue(srvid)! -// password := resc.rescue.password -// console.print_debug("[+] rescue password: $password") -// // console.print_debug(resc) - -// console.print_debug("[+] fetching server information") -// boot := h.server_boot(srvid)! -// // console.print_debug(boot) - -// console.print_debug("[+] forcing reboot") -// reset := h.server_reset(srvid)! -// // console.print_debug(reset) - -// time.sleep(30000 * time.millisecond) - -// console.print_debug("[+] waiting for rescue to be ready") -// for { -// target := vssh.new(srvip, 22) or { -// console.print_debug("$err") -// time.sleep(30000 * time.millisecond) -// continue -// } - -// // rescue doesn't support keyboard-interactive, fallback to password -// target.authenticate(.password, "root", password)! -// check := target.execute("grep 'Hetzner Rescue System.' /etc/motd")! -// if check.exitcode == 0 { -// console.print_debug("[+] we are logged in on the rescue system !") - -// // executing deployment binary -// target.upload(os.args[0], "/tmp/deployment")! -// target.stream("stdbuf -i0 -o0 -e0 /tmp/deployment")! - -// exit(0) -// } -// } - -// return true -// } diff --git a/lib/virt/hetznermanager/rescue.v b/lib/virt/hetznermanager/rescue.v index f57bd12a..ad32c00f 100644 --- a/lib/virt/hetznermanager/rescue.v +++ b/lib/virt/hetznermanager/rescue.v @@ -37,18 +37,27 @@ pub fn (mut h HetznerManager) server_rescue(args_ ServerRescueArgs) !ServerInfoD mut args := args_ mut serverinfo := h.server_info_get(id: args.id, name: args.name)! - console.print_header('server ${serverinfo.server_name} goes into rescue mode') - if serverinfo.rescue && ! args.reset { if osal.ssh_test(address: serverinfo.server_ip, port: 22)! == .ok { + console.print_debug('test server ${serverinfo.server_name} is in rescue mode?') + mut b := builder.new()! + mut n := b.node_new(ipaddr: serverinfo.server_ip)! - console.print_debug('server ${serverinfo.server_name} is in rescue mode') + res:=n.exec(cmd:"ls /root/.oldroot/nfs/install/installimage") or { + "ERROR" + } + if res.contains("nfs/install/installimage"){ + console.print_debug('server ${serverinfo.server_name} is in rescue mode') + return serverinfo + } } serverinfo.rescue = false } // only do it if its not in rescue yet if serverinfo.rescue == false || args.reset { + console.print_header('server ${serverinfo.server_name} goes into rescue mode') + mut keyfps := []string{} if args.sshkey_name != '' { keyfps << h.key_get(args.sshkey_name)!.fingerprint @@ -67,9 +76,9 @@ pub fn (mut h HetznerManager) server_rescue(args_ ServerRescueArgs) !ServerInfoD dataformat: .urlencoded )! - console.print_debug('hetzner rescue\n${rescue}') + // console.print_debug('hetzner rescue\n${rescue}') - h.server_reset(id: args.id, name: args.name, wait: args.wait)! + h.server_reset(id: args.id, name: args.name, wait: args.wait, msg:" to get up and running in rescue mode.")! os.execute_opt("ssh-keygen -R ${serverinfo.server_ip}")! } @@ -81,6 +90,7 @@ pub fn (mut h HetznerManager) server_rescue(args_ ServerRescueArgs) !ServerInfoD if args.wait { mut b := builder.new()! mut n := b.node_new(ipaddr: serverinfo.server_ip)! + n.exec_silent("apt update && apt install -y mc redis")! if args.hero_install { n.hero_install()! } @@ -94,8 +104,62 @@ pub fn (mut h HetznerManager) server_rescue(args_ ServerRescueArgs) !ServerInfoD pub fn (mut h HetznerManager) server_rescue_node(args ServerRescueArgs) !&builder.Node { mut serverinfo := h.server_rescue(args)! - mut b := builder.new()! - mut n := b.node_new(ipaddr: serverinfo.server_ip)! + mut b := builder.new()! + mut n := b.node_new(ipaddr: serverinfo.server_ip)! return n } + + +pub struct ServerInstallArgs { +pub mut: + id int + name string + wait bool = true + hero_install bool + hero_install_compile bool + sshkey_name string @[required] + raid bool +} + +pub fn (mut h HetznerManager) ubuntu_install(args ServerInstallArgs) !&builder.Node { + + mut serverinfo := h.server_rescue(id:args.id,name:args.name,wait:true,sshkey_name:args.sshkey_name)! + + mut b := builder.new()! + mut n := b.node_new(ipaddr: serverinfo.server_ip)! + + // installconfig:=$tmpl("templates/ubuntu_install.sh") + // n.file_write("/tmp/installconfig",installconfig)! + // n.exec_interactive("installimage -a -c /tmp/installconfig")! + + mut rstr:="" + if args.raid { + rstr="-r yes -l 1 " + } + + n.exec_interactive(' + set -ex + echo "go into install mode, try to install ubuntu 24.04" + /root/.oldroot/nfs/install/installimage -a -n kristof2 ${rstr} -i /root/.oldroot/nfs/images/Ubuntu-2404-noble-amd64-base.tar.gz -f yes -t yes swap:swap:4G,/boot:ext3:1024M,/:btrfs:all + reboot')! + + os.execute_opt("ssh-keygen -R ${serverinfo.server_ip}")! + + console.print_debug('server ${serverinfo.server_name} is installed in ubuntu now, should be restarting.') + + osal.reboot_wait( + address: serverinfo.server_ip + timeout_down: 60 + timeout_up: 60 * 5 + )! + + if args.hero_install { + n.hero_install(compile: args.hero_install_compile)! + } + + return n +} + + + diff --git a/lib/virt/hetznermanager/reset.v b/lib/virt/hetznermanager/reset.v index 26a474e2..e8442198 100644 --- a/lib/virt/hetznermanager/reset.v +++ b/lib/virt/hetznermanager/reset.v @@ -21,6 +21,7 @@ pub mut: id int name string wait bool = true + msg string } pub fn (mut h HetznerManager) server_reset(args ServerRebootArgs) !ResetInfo { @@ -45,7 +46,6 @@ pub fn (mut h HetznerManager) server_reset(args ServerRebootArgs) !ResetInfo { dataformat: .urlencoded // dict_key:'reset' )! - println(o) console.print_debug('server ${serverinfo.server_name} reset done.') // now need to wait till it goes off if serveractive { @@ -64,7 +64,7 @@ pub fn (mut h HetznerManager) server_reset(args ServerRebootArgs) !ResetInfo { if args.wait { for { time.sleep(1000 * time.millisecond) - console.print_debug('wait for ${serverinfo.server_name}') + console.print_debug('wait for ${serverinfo.server_name} ${args.msg}') if osal.ssh_test(address: serverinfo.server_ip)! == .ok { console.print_debug('ssh test ok') console.print_header('server is rebooted: ${serverinfo.server_name}') @@ -80,19 +80,3 @@ pub fn (mut h HetznerManager) server_reset(args ServerRebootArgs) !ResetInfo { return o } - -// /////////////////////////////////////BOOT - -// struct BootRoot { -// boot Boot -// } - -// struct Boot { -// rescue RescueInfo -// } - -// pub fn (mut h HetznerManager) server_boot(id int) !RescueInfo { -// mut conn := h.connection()! -// boot := conn.get_json[BootRoot](prefix: 'boot/${id}')! -// return boot.boot.rescue -// }