This commit is contained in:
2025-02-09 12:24:12 +01:00
parent ae4e92e090
commit 3e309b6379
32 changed files with 1968 additions and 618 deletions

View File

@@ -12,31 +12,31 @@ println(client)
// Send a message to a node by public key
// Parameters: public_key, payload, topic, wait_for_reply
msg := client.send_msg(r.public_key, // destination public key
'Hello World', // message payload
'greetings', // optional topic
false // wait for reply
)!
msg := client.send_msg(
public_key: '0569a9c54da7a52b2ab3a2fb03f2b9be2c1c11d65d14a4888182bd12ed1dbf38' // destination public key
payload: 'Hello World' // message payload
topic: 'greetings' // optional topic
)!
println('Sent message ID: ${msg.id}')
println('send succeeded')
// Receive messages
// Parameters: wait_for_message, peek_only, topic_filter
received := client.receive_msg(true, false, 'greetings')!
received := client.receive_msg(wait: true, peek: false, topic: 'greetings')!
println('Received message from: ${received.src_pk}')
println('Message payload: ${received.payload}')
// // Reply to a message
// client.reply_msg(
// received.id, // original message ID
// received.src_pk, // sender's public key
// 'Got your message!', // reply payload
// 'greetings' // topic
// )!
// Reply to a message
client.reply_msg(
id: received.id
public_key: received.src_pk
payload: 'Got your message!'
topic: 'greetings'
)!
// // Check message status
// status := client.get_msg_status(msg.id)!
// println('Message status: ${status.state}')
// println('Created at: ${status.created}')
// println('Expires at: ${status.deadline}')
// Check message status
status := client.get_msg_status(msg.id)!
println('Message status: ${status.state}')
println('Created at: ${status.created}')
println('Expires at: ${status.deadline}')

View File

@@ -1,5 +1,9 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.infra.coredns as coredns_installer
import freeflowuniverse.herolib.osal
// coredns_installer.delete()!
mut installer:= coredns_installer.get()!
installer.build()!
coredns_installer.install()!

13
examples/installers/traefik.vsh Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import os
import freeflowuniverse.herolib.installers.web.traefik as traefik_installer
traefik_installer.delete()!
mut installer:= traefik_installer.get()!
installer.password = "planet"
traefik_installer.set(installer)!
installer.start()!

View File

@@ -15,8 +15,8 @@ pub:
pub struct PushMessageBody {
pub:
dst MessageDestination
topic string // optional message topic
payload string // base64 encoded message
topic ?string // optional message topic
payload string // base64 encoded message
}
// Response containing message ID after pushing
@@ -73,19 +73,32 @@ pub fn (mut self Mycelium) connection() !&httpconnection.HTTPConnection {
return c
}
@[params]
pub struct SendMessageArgs {
pub mut:
public_key string @[required]
payload string @[required]
topic ?string
wait bool
}
// Send a message to a node identified by public key
pub fn (mut self Mycelium) send_msg(pk string, payload string, topic string, wait bool) !InboundMessage {
pub fn (mut self Mycelium) send_msg(args SendMessageArgs) !InboundMessage {
mut conn := self.connection()!
mut body := PushMessageBody{
dst: MessageDestination{
pk: pk
pk: args.public_key
ip: ''
}
payload: base64.encode_str(payload)
topic: base64.encode_str(topic)
payload: base64.encode_str(args.payload)
topic: if v := args.topic {
base64.encode_str(v)
} else {
none
}
}
mut prefix := '/api/v1/messages'
if wait {
if args.wait {
prefix += '?reply_timeout=120'
}
return conn.post_json_generic[InboundMessage](
@@ -96,19 +109,27 @@ pub fn (mut self Mycelium) send_msg(pk string, payload string, topic string, wai
)!
}
@[params]
pub struct ReceiveMessageArgs {
pub mut:
topic ?string
wait bool
peek bool
}
// Receive a message from the queue
pub fn (mut self Mycelium) receive_msg(wait bool, peek bool, topic string) !InboundMessage {
pub fn (mut self Mycelium) receive_msg(args ReceiveMessageArgs) !InboundMessage {
mut conn := self.connection()!
mut prefix := '/api/v1/messages?'
if wait {
prefix += 'timeout=60&'
mut prefix := '/api/v1/messages?peek=${args.peek}&'
if args.wait {
prefix += 'timeout=120&'
}
if peek {
prefix += 'peek=true&'
}
if topic.len > 0 {
prefix += 'topic=${base64.encode_str(topic)}'
if v := args.topic {
prefix += 'topic=${base64.encode_str(v)}'
}
return conn.get_json_generic[InboundMessage](
method: .get
prefix: prefix
@@ -117,23 +138,8 @@ pub fn (mut self Mycelium) receive_msg(wait bool, peek bool, topic string) !Inbo
}
// Optional version of receive_msg that returns none on 204
pub fn (mut self Mycelium) receive_msg_opt(wait bool, peek bool, topic string) ?InboundMessage {
mut conn := self.connection() or { panic(err) }
mut prefix := '/api/v1/messages?'
if wait {
prefix += 'timeout=60&'
}
if peek {
prefix += 'peek=true&'
}
if topic.len > 0 {
prefix += 'topic=${base64.encode_str(topic)}'
}
res := conn.get_json_generic[InboundMessage](
method: .get
prefix: prefix
dataformat: .json
) or {
pub fn (mut self Mycelium) receive_msg_opt(args ReceiveMessageArgs) ?InboundMessage {
res := self.receive_msg(args) or {
if err.msg().contains('204') {
return none
}
@@ -152,20 +158,29 @@ pub fn (mut self Mycelium) get_msg_status(id string) !MessageStatusResponse {
)!
}
@[params]
pub struct ReplyMessageArgs {
pub mut:
id string @[required]
public_key string @[required]
payload string @[required]
topic ?string
}
// Reply to a message
pub fn (mut self Mycelium) reply_msg(id string, pk string, payload string, topic string) ! {
pub fn (mut self Mycelium) reply_msg(args ReplyMessageArgs) ! {
mut conn := self.connection()!
mut body := PushMessageBody{
dst: MessageDestination{
pk: pk
pk: args.public_key
ip: ''
}
payload: base64.encode_str(payload)
topic: base64.encode_str(topic)
payload: base64.encode_str(args.payload)
topic: if v := args.topic { base64.encode_str(v) } else { none }
}
_ := conn.post_json_generic[MessageDestination](
_ := conn.post_json_str(
method: .post
prefix: '/api/v1/messages/reply/${id}'
prefix: '/api/v1/messages/reply/${args.id}'
data: json.encode(body)
dataformat: .json
)!

View File

@@ -2,8 +2,6 @@ module mycelium
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.data.paramsparser
__global (
mycelium_global map[string]&Mycelium

View File

@@ -2,7 +2,6 @@ module mycelium
import freeflowuniverse.herolib.core.httpconnection
import freeflowuniverse.herolib.data.encoderhero
import os
pub const version = '0.0.0'
const singleton = true

View File

@@ -152,11 +152,16 @@ fn build_() ! {
// if core.platform()!= .ubuntu {
// return error('only support ubuntu for now')
// }
// golang.install()!
//mut g:=golang.get()!
//g.install()!
//console.print_header('build coredns')
//mut gs := gittools.new(coderoot: '~/code')!
// console.print_header('build ${model.name}')
// gitpath := gittools.get_repo(coderoot: '/tmp/builder', url: url, reset: true, pull: true)!
// gitpath := gittools.get_repo(url: url, reset: true, pull: true)!
// cmd := '
// cd ??{gitpath}

View File

@@ -0,0 +1,13 @@
!!hero_code.generate_installer
name:'coredns'
classname:'CoreDNS'
singleton:1
templates:1
default:1
title:'coredns'
supported_platforms:''
reset:0
startupmanager:1
hasconfig:1
build:1

View File

@@ -1,138 +0,0 @@
module coredns
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.osal.screen
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.httpconnection
import os
@[params]
pub struct InstallArgs {
pub mut:
reset bool // this means we re-install and forgot what we did before
start bool = true
stop bool
restart bool // this means we stop if started, otherwise just start
homedir string // not sure what this is?
config_path string // path to Corefile, if empty will install default one
config_url string // path to Corefile through e.g. git url, will pull it if it is not local yet
dnszones_path string // path to where all the dns zones are
dnszones_url string // path on git url pull if needed
plugins []string // list of plugins to build CoreDNS with
example bool // if true we will install examples
}
pub fn install_(args_ InstallArgs) ! {
mut args := args_
version := '1.11.1'
res := os.execute('${osal.profile_path_source_and()!} coredns version')
if res.exit_code == 0 {
r := res.output.split_into_lines().filter(it.trim_space().starts_with('CoreDNS-'))
if r.len != 1 {
return error("couldn't parse coredns version.\n${res.output}")
}
if texttools.version(version) > texttools.version(r[0].all_after_first('CoreDNS-')) {
args.reset = true
}
} else {
args.reset = true
}
if args.reset {
console.print_header('install coredns')
mut url := ''
if core.is_linux_arm()! {
url = 'https://github.com/coredns/coredns/releases/download/v${version}/coredns_${version}_linux_arm64.tgz'
} else if core.is_linux_intel()! {
url = 'https://github.com/coredns/coredns/releases/download/v${version}/coredns_${version}_linux_amd64.tgz'
} else if core.is_osx_arm()! {
url = 'https://github.com/coredns/coredns/releases/download/v${version}/coredns_${version}_darwin_arm64.tgz'
} else if core.is_osx_intel()! {
url = 'https://github.com/coredns/coredns/releases/download/v${version}/coredns_${version}_darwin_amd64.tgz'
} else {
return error('unsported platform')
}
mut dest := osal.download(
url: url
minsize_kb: 13000
expand_dir: '/tmp/coredns'
)!
mut binpath := dest.file_get('coredns')!
osal.cmd_add(
cmdname: 'coredns'
source: binpath.path
)!
}
configure(args)!
if args.example {
example_configure(args)!
}
if args.restart {
restart(args)!
return
}
if args.start {
start(args)!
}
}
pub fn restart(args_ InstallArgs) ! {
stop(args_)!
start(args_)!
}
pub fn stop(args_ InstallArgs) ! {
console.print_header('coredns stop')
name := 'coredns'
// use startup manager, see caddy
mut scr := screen.new()!
scr.kill(name)!
}
pub fn start(args_ InstallArgs) ! {
mut args := args_
configure(args)!
if check()! {
return
}
console.print_header('coredns start')
name := 'coredns'
mut scr := screen.new()!
mut s := scr.add(name: name, reset: true)!
cmd2 := "coredns -conf '${args.config_path}'"
s.cmd_send(cmd2)!
if !check()! {
return error("coredns did not install propertly, do: curl 'http://localhost:3334/health'")
}
console.print_header('coredns running')
}
pub fn check() !bool {
// this checks health of coredns
mut conn := httpconnection.new(name: 'coredns', url: 'http://localhost:3334')!
r := conn.get(prefix: 'health')!
if r.trim_space() == 'OK' {
return true
}
return false
}

View File

@@ -1,49 +0,0 @@
module coredns
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.installers.base
import os
pub fn play(mut plbook playbook.PlayBook) ! {
base.play(playbook)!
coredns_actions := plbook.find(filter: 'coredns.')!
if coredns_actions.len == 0 {
return
}
mut install_actions := plbook.find(filter: 'coredns.install')!
if install_actions.len > 0 {
for install_action in install_actions {
mut p := install_action.params
// CoreDNS parameters
reset := p.get_default_false('reset')
start := p.get_default_true('start')
stop := p.get_default_false('stop')
restart := p.get_default_false('restart')
homedir := p.get_default('homedir', '${os.home_dir()}/hero/var/coredns')!
config_path := p.get_default('config_path', '${os.home_dir()}/hero/cfg/Corefile')!
config_url := p.get_default('config_url', '')!
dnszones_path := p.get_default('dnszones_path', '${os.home_dir()}/hero/var/coredns/zones')!
dnszones_url := p.get_default('dnszones_url', '')!
plugins := p.get_list_default('plugins', [])!
example := p.get_default_false('example')
install(
reset: reset
start: start
stop: stop
restart: restart
homedir: homedir
config_path: config_path
config_url: config_url
dnszones_path: dnszones_path
dnszones_url: dnszones_url
plugins: plugins
example: example
)!
}
}
}

View File

@@ -0,0 +1,183 @@
module coredns
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.develop.gittools
import freeflowuniverse.herolib.osal.zinit
import freeflowuniverse.herolib.installers.ulist
import freeflowuniverse.herolib.core.httpconnection
import freeflowuniverse.herolib.installers.lang.golang
import os
fn startupcmd () ![]zinit.ZProcessNewArgs{
mut args := get()!
mut res := []zinit.ZProcessNewArgs{}
cmd := "coredns -conf '${args.config_path}'"
res << zinit.ZProcessNewArgs{
name: 'coredns'
cmd: cmd
}
return res
}
fn running() !bool {
mut installer := get()!
mut conn := httpconnection.new(name: 'coredns', url: 'http://localhost:3334')!
r := conn.get(prefix: 'health')!
if r.trim_space() == 'OK' {
return true
}
return false
}
fn start_pre()!{
fix()!
}
fn start_post()!{
set_local_dns()
}
fn stop_pre()!{
}
fn stop_post()!{
}
//////////////////// following actions are not specific to instance of the object
// checks if a certain version or above is installed
fn installed() !bool {
res := os.execute('${osal.profile_path_source_and()!} coredns version')
if res.exit_code != 0 {
return false
}
r := res.output.split_into_lines().filter(it.trim_space().starts_with('CoreDNS-'))
if r.len != 1 {
return error("couldn't parse coredns version.\n${res.output}")
}
if texttools.version(version) == texttools.version(r[0].all_after_first('CoreDNS-')) {
return true
}
return false
}
//get the Upload List of the files
fn ulist_get() !ulist.UList {
//optionally build a UList which is all paths which are result of building, is then used e.g. in upload
return ulist.UList{}
}
//uploads to S3 server if configured
fn upload() ! {
// installers.upload(
// cmdname: 'coredns'
// source: '${gitpath}/target/x86_64-unknown-linux-musl/release/coredns'
// )!
}
fn install() ! {
console.print_header('install coredns')
mut url := ''
if core.is_linux_arm()! {
url = 'https://github.com/coredns/coredns/releases/download/v${version}/coredns_${version}_linux_arm64.tgz'
} else if core.is_linux_intel()! {
url = 'https://github.com/coredns/coredns/releases/download/v${version}/coredns_${version}_linux_amd64.tgz'
} else if core.is_osx_arm()! {
url = 'https://github.com/coredns/coredns/releases/download/v${version}/coredns_${version}_darwin_arm64.tgz'
} else if core.is_osx_intel()! {
url = 'https://github.com/coredns/coredns/releases/download/v${version}/coredns_${version}_darwin_amd64.tgz'
} else {
return error('unsported platform')
}
mut dest := osal.download(
url: url
minsize_kb: 13000
expand_dir: '/tmp/coredns'
)!
mut binpath := dest.file_get('coredns')!
osal.cmd_add(
cmdname: 'coredns'
source: binpath.path
)!
}
fn build() ! {
url := 'https://github.com/coredns/coredns'
if core.platform()! != .ubuntu {
return error('only support ubuntu for now')
}
mut g:=golang.get()!
g.install()!
console.print_header('build coredns')
mut gs := gittools.new(coderoot: '~/code')!
gitpath := gs.get_repo(url: url, reset: true, pull: true)!
cmd := '
cd ${gitpath}
make
'
osal.execute_stdout(cmd)!
//now copy to the default bin path
// mut binpath := dest.file_get('...')!
// adds it to path
// osal.cmd_add(
// cmdname: 'griddriver2'
// source: binpath.path
// )!
}
fn destroy() ! {
// mut systemdfactory := systemd.new()!
// systemdfactory.destroy("zinit")!
// osal.process_kill_recursive(name:'zinit')!
// osal.cmd_delete('zinit')!
// osal.package_remove('
// podman
// conmon
// buildah
// skopeo
// runc
// ')!
// //will remove all paths where go/bin is found
// osal.profile_path_add_remove(paths2delete:"go/bin")!
// osal.rm("
// podman
// conmon
// buildah
// skopeo
// runc
// /var/lib/containers
// /var/lib/podman
// /var/lib/buildah
// /tmp/podman
// /tmp/conmon
// ")!
}

View File

@@ -1,14 +1,18 @@
module coredns
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.develop.gittools
import os
pub fn configure(args_ InstallArgs) ! {
mut args := args_
pub fn configure() ! {
mut args := get()!
mut gs := gittools.get()!
mut repo_path := ''
set_global_dns()
if args.config_url.len > 0 {
mut repo := gs.get_repo(
url: args.config_url
@@ -37,15 +41,23 @@ pub fn configure(args_ InstallArgs) ! {
mycorefile := $tmpl('templates/Corefile')
mut path := pathlib.get_file(path: args.config_path, create: true)!
path.write(mycorefile)!
if args.example{
example_configure() !
}
}
pub fn example_configure(args_ InstallArgs) ! {
mut args := args_
pub fn example_configure() ! {
mut args := get()!
exampledbfile := $tmpl('templates/db.example.org')
myipaddr:=osal.ipaddr_pub_get()!
exampledbfile := $tmpl('templates/ourexample.org')
mut path_testzone := pathlib.get_file(
path: '${args_.dnszones_path}/db.example.org'
path: '${args.dnszones_path}/ourexample.org'
create: true
)!
path_testzone.template_write(exampledbfile, true)!

View File

@@ -0,0 +1,291 @@
module coredns
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.data.paramsparser
import freeflowuniverse.herolib.sysadmin.startupmanager
import freeflowuniverse.herolib.osal.zinit
import time
__global (
coredns_global map[string]&CoreDNS
coredns_default string
)
/////////FACTORY
@[params]
pub struct ArgsGet{
pub mut:
name string
}
fn args_get (args_ ArgsGet) ArgsGet {
mut args:=args_
if args.name == ""{
args.name = "default"
}
return args
}
pub fn get(args_ ArgsGet) !&CoreDNS {
mut context:=base.context()!
mut args := args_get(args_)
mut obj := CoreDNS{}
if !(args.name in coredns_global) {
if ! exists(args)!{
set(obj)!
}else{
heroscript := context.hero_config_get("coredns",args.name)!
mut obj_:=heroscript_loads(heroscript)!
set_in_mem(obj_)!
}
}
return coredns_global[args.name] or {
println(coredns_global)
//bug if we get here because should be in globals
panic("could not get config for coredns with name, is bug:${args.name}")
}
}
//register the config for the future
pub fn set(o CoreDNS)! {
set_in_mem(o)!
mut context := base.context()!
heroscript := heroscript_dumps(o)!
context.hero_config_set("coredns", o.name, heroscript)!
}
//does the config exists?
pub fn exists(args_ ArgsGet)! bool {
mut context := base.context()!
mut args := args_get(args_)
return context.hero_config_exists("coredns", args.name)
}
pub fn delete(args_ ArgsGet)! {
mut args := args_get(args_)
mut context:=base.context()!
context.hero_config_delete("coredns",args.name)!
if args.name in coredns_global {
//del coredns_global[args.name]
}
}
//only sets in mem, does not set as config
fn set_in_mem(o CoreDNS)! {
mut o2:=obj_init(o)!
coredns_global[o.name] = &o2
coredns_default = o.name
}
@[params]
pub struct PlayArgs {
pub mut:
heroscript string //if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
}
pub fn play(args_ PlayArgs) ! {
mut args:=args_
mut plbook := args.plbook or {
playbook.new(text: args.heroscript)!
}
mut install_actions := plbook.find(filter: 'coredns.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
heroscript:=install_action.heroscript()
mut obj2:=heroscript_loads(heroscript)!
set(obj2)!
}
}
mut other_actions := plbook.find(filter: 'coredns.')!
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 coredns.destroy")
destroy()!
}
if other_action.name == "install"{
console.print_debug("install action coredns.install")
install()!
}
}
if other_action.name in ["start","stop","restart"]{
mut p := other_action.params
name := p.get('name')!
mut coredns_obj:=get(name:name)!
console.print_debug("action object:\n${coredns_obj}")
if other_action.name == "start"{
console.print_debug("install action coredns.${other_action.name}")
coredns_obj.start()!
}
if other_action.name == "stop"{
console.print_debug("install action coredns.${other_action.name}")
coredns_obj.stop()!
}
if other_action.name == "restart"{
console.print_debug("install action coredns.${other_action.name}")
coredns_obj.restart()!
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////# 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()!
}
}
}
//load from disk and make sure is properly intialized
pub fn (mut self CoreDNS) reload() ! {
switch(self.name)
self=obj_init(self)!
}
pub fn (mut self CoreDNS) start() ! {
switch(self.name)
if self.running()!{
return
}
console.print_header('coredns start')
if ! installed()!{
install()!
}
configure()!
start_pre()!
for zprocess in startupcmd()!{
mut sm:=startupmanager_get(zprocess.startuptype)!
console.print_debug('starting coredns with ${zprocess.startuptype}...')
sm.new(zprocess)!
sm.start(zprocess.name)!
}
start_post()!
for _ in 0 .. 50 {
if self.running()! {
return
}
time.sleep(100 * time.millisecond)
}
return error('coredns did not install properly.')
}
pub fn (mut self CoreDNS) install_start(args InstallArgs) ! {
switch(self.name)
self.install(args)!
self.start()!
}
pub fn (mut self CoreDNS) stop() ! {
switch(self.name)
stop_pre()!
for zprocess in startupcmd()!{
mut sm:=startupmanager_get(zprocess.startuptype)!
sm.stop(zprocess.name)!
}
stop_post()!
}
pub fn (mut self CoreDNS) restart() ! {
switch(self.name)
self.stop()!
self.start()!
}
pub fn (mut self CoreDNS) running() !bool {
switch(self.name)
//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
}
}
return running()!
}
@[params]
pub struct InstallArgs{
pub mut:
reset bool
}
pub fn (mut self CoreDNS) install(args InstallArgs) ! {
switch(self.name)
if args.reset || (!installed()!) {
install()!
}
}
pub fn (mut self CoreDNS) build() ! {
switch(self.name)
build()!
}
pub fn (mut self CoreDNS) destroy() ! {
switch(self.name)
self.stop() or {}
destroy()!
}
//switch instance to be used for coredns
pub fn switch(name string) {
coredns_default = name
}
//helpers
@[params]
pub struct DefaultConfigArgs{
instance string = 'default'
}

View File

@@ -0,0 +1,62 @@
module coredns
import os
import net
import freeflowuniverse.herolib.ui.console
fn is_systemd_resolved_active() bool {
result := os.execute('systemctl is-active systemd-resolved')
return result.exit_code == 0 && result.output.trim_space() == 'active'
}
fn disable_systemd_resolved() {
console.print_debug('Stopping and disabling systemd-resolved...')
os.execute('sudo systemctl stop systemd-resolved')
os.execute('sudo systemctl disable systemd-resolved')
os.execute('sudo systemctl mask systemd-resolved')
}
fn is_dns_port_free() bool {
result := os.execute("sudo ss -tunlp | grep ':53 '")
return result.exit_code != 0
}
fn set_local_dns() {
console.print_debug('Updating /etc/resolv.conf to use local DNS...')
os.execute('sudo rm -f /etc/resolv.conf')
os.write_file('/etc/resolv.conf', 'nameserver 127.0.0.1\n') or {
console.print_debug('Failed to update /etc/resolv.conf')
return
}
console.print_debug('/etc/resolv.conf updated successfully.')
}
fn set_global_dns() {
console.print_debug('Updating /etc/resolv.conf to use local DNS...')
os.execute('sudo rm -f /etc/resolv.conf')
os.write_file('/etc/resolv.conf', 'nameserver 8.8.8.8\n') or {
console.print_debug('Failed to update /etc/resolv.conf')
return
}
console.print_debug('/etc/resolv.conf updated successfully for global.')
}
pub fn fix()! {
console.print_debug('Checking if systemd-resolved is active...')
if is_systemd_resolved_active() {
disable_systemd_resolved()
} else {
println('systemd-resolved is already disabled.')
}
console.print_debug('Checking if DNS UDP port 53 is free...')
if is_dns_port_free() {
console.print_debug('UDP port 53 is free.')
} else {
console.print_debug('UDP port 53 is still in use. Ensure CoreDNS or another service is properly set up.')
return
}
set_global_dns()
console.print_debug('Setup complete. Ensure CoreDNS is running.')
}

View File

@@ -0,0 +1,42 @@
module coredns
import freeflowuniverse.herolib.data.paramsparser
import freeflowuniverse.herolib.data.encoderhero
import os
pub const version = '1.12.0'
const singleton = true
const default = true
//THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
@[heap]
pub struct CoreDNS {
pub mut:
name string = 'default'
config_path string
config_url string // path to Corefile through e.g. git url, will pull it if it is not local yet
dnszones_path string // path to where all the dns zones are
dnszones_url string // path on git url pull if needed (is comma or \n separated list)
plugins string // list of plugins to build CoreDNS with (is comma or \n separated list)
example bool = true // if true we will install examples
}
//your checking & initialization code if needed
fn obj_init(mycfg_ CoreDNS)!CoreDNS{
mut mycfg:=mycfg_
return mycfg
}
/////////////NORMALLY NO NEED TO TOUCH
pub fn heroscript_dumps(obj CoreDNS) !string {
return encoderhero.encode[CoreDNS ](obj)!
}
pub fn heroscript_loads(heroscript string) !CoreDNS {
mut obj := encoderhero.decode[CoreDNS](heroscript)!
return obj
}

View File

@@ -0,0 +1,43 @@
# coredns
coredns
To get started
```vlang
import freeflowuniverse.herolib.lib.installers.infra.coredns as coredns_installer
heroscript:="
!!coredns.configure name:'test'
config_path: '/etc/coredns/Corefile'
dnszones_path: '/etc/coredns/zones'
plugins: 'forward,cache'
example: true
!!coredns.start name:'test' reset:1
"
coredns_installer.play(heroscript=heroscript)!
//or we can call the default and do a start with reset
//mut installer:= coredns_installer.get()!
//installer.start(reset:true)!
```
## example heroscript
```hero
!!coredns.configure
name: 'custom'
config_path: '/etc/coredns/Corefile'
config_url: 'https://github.com/example/coredns-config'
dnszones_path: '/etc/coredns/zones'
dnszones_url: 'https://github.com/example/dns-zones'
plugins: 'forward,cache'
example: false
```

View File

@@ -3,5 +3,7 @@
log
errors
health :3334
import '${args.dnszones_path}/*'
auto ${args.dnszones_path} {
reload 30s
}
}

View File

@@ -1,14 +0,0 @@
??ORIGIN example.org.
^^ 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. (
2017042745 ; serial
7200 ; refresh (2 hours)
3600 ; retry (1 hour)
1209600 ; expire (2 weeks)
3600 ; minimum (1 hour)
)
3600 IN NS a.iana-servers.net.
3600 IN NS b.iana-servers.net.
www IN A 127.0.0.1
IN AAAA ::1

View File

@@ -0,0 +1,17 @@
??ORIGIN ourexample.org.
^^ 3600 IN SOA ns1.ourexample.org. hostmaster.ourexample.org. (
2017042745 ; serial
7200 ; refresh (2 hours)
3600 ; retry (1 hour)
1209600 ; expire (2 weeks)
3600 ; minimum (1 hour)
)
3600 IN NS ns1.ourexample.org.
3600 IN NS ns2.ourexample.org.
ns1 IN A ${myipaddr}
ns2 IN A ${myipaddr}
www IN A ${myipaddr}
test IN A ${myipaddr}
IN AAAA ::1

View File

@@ -3,165 +3,200 @@ module mycelium
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.installers.sysadmintools.zinit as zinit_installer
import freeflowuniverse.herolib.clients.mycelium
import freeflowuniverse.herolib.develop.gittools
import freeflowuniverse.herolib.osal.systemd
import freeflowuniverse.herolib.osal.zinit
import freeflowuniverse.herolib.installers.ulist
import freeflowuniverse.herolib.installers.lang.golang
import freeflowuniverse.herolib.installers.lang.rust
import freeflowuniverse.herolib.installers.lang.python
import os
fn startupcmd() ![]zinit.ZProcessNewArgs {
mut installer := get()!
mut res := []zinit.ZProcessNewArgs{}
fn startupcmd () ![]zinit.ZProcessNewArgs{
mut installer := get()!
mut res := []zinit.ZProcessNewArgs{}
//THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
// res << zinit.ZProcessNewArgs{
// name: 'mycelium'
// cmd: 'mycelium server'
// env: {
// 'HOME': '/root'
// }
// }
mut peers_str := installer.peers.join(' ')
mut tun_name := 'tun${installer.tun_nr}'
res << zinit.ZProcessNewArgs{
name: 'mycelium'
cmd: 'mycelium --key-file ${osal.hero_path()!}/cfg/priv_key.bin --peers ${peers_str} --tun-name ${tun_name}'
env: {
'HOME': '/root'
}
}
return res
return res
}
fn running() !bool {
mycelium.inspect() or { return false }
return true
mut installer := get()!
//THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
// this checks health of mycelium
// curl http://localhost:3333/api/v1/s --oauth2-bearer 1234 works
// url:='http://127.0.0.1:${cfg.port}/api/v1'
// mut conn := httpconnection.new(name: 'mycelium', url: url)!
// if cfg.secret.len > 0 {
// conn.default_header.add(.authorization, 'Bearer ${cfg.secret}')
// }
// conn.default_header.add(.content_type, 'application/json')
// console.print_debug("curl -X 'GET' '${url}'/tags --oauth2-bearer ${cfg.secret}")
// r := conn.get_json_dict(prefix: 'tags', debug: false) or {return false}
// println(r)
// if true{panic("ssss")}
// tags := r['Tags'] or { return false }
// console.print_debug(tags)
// console.print_debug('mycelium is answering.')
return false
}
fn start_pre() ! {
fn start_pre()!{
}
fn start_post() ! {
fn start_post()!{
}
fn stop_pre() ! {
fn stop_pre()!{
}
fn stop_post() ! {
fn stop_post()!{
}
//////////////////// following actions are not specific to instance of the object
// checks if a certain version or above is installed
fn installed() !bool {
cmd := '${osal.profile_path_source_and()!} mycelium -V'
// println(cmd)
res := os.execute(cmd)
if res.exit_code != 0 {
println(res)
return false
}
r := res.output.split_into_lines().filter(it.trim_space().len > 0)
if r.len != 1 {
return error("couldn't parse mycelium version.\n${res.output}")
}
if texttools.version(version) == texttools.version(r[0].all_after_last('mycelium')) {
return true
}
return false
//THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
// res := os.execute('${osal.profile_path_source_and()!} mycelium version')
// if res.exit_code != 0 {
// return false
// }
// r := res.output.split_into_lines().filter(it.trim_space().len > 0)
// if r.len != 1 {
// return error("couldn't parse mycelium version.\n${res.output}")
// }
// if texttools.version(version) == texttools.version(r[0]) {
// return true
// }
return false
}
// get the Upload List of the files
//get the Upload List of the files
fn ulist_get() !ulist.UList {
// optionally build a UList which is all paths which are result of building, is then used e.g. in upload
return ulist.UList{}
//optionally build a UList which is all paths which are result of building, is then used e.g. in upload
return ulist.UList{}
}
// uploads to S3 server if configured
//uploads to S3 server if configured
fn upload() ! {
// installers.upload(
// cmdname: 'mycelium'
// source: '${gitpath}/target/x86_64-unknown-linux-musl/release/mycelium'
// )!
// installers.upload(
// cmdname: 'mycelium'
// source: '${gitpath}/target/x86_64-unknown-linux-musl/release/mycelium'
// )!
}
fn install() ! {
console.print_header('install mycelium')
console.print_header('install mycelium')
//THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED
// mut url := ''
// if core.is_linux_arm() {
// url = 'https://github.com/mycelium-dev/mycelium/releases/download/v${version}/mycelium_${version}_linux_arm64.tar.gz'
// } else if core.is_linux_intel() {
// url = 'https://github.com/mycelium-dev/mycelium/releases/download/v${version}/mycelium_${version}_linux_amd64.tar.gz'
// } else if core.is_osx_arm() {
// url = 'https://github.com/mycelium-dev/mycelium/releases/download/v${version}/mycelium_${version}_darwin_arm64.tar.gz'
// } else if osal.is_osx_intel() {
// url = 'https://github.com/mycelium-dev/mycelium/releases/download/v${version}/mycelium_${version}_darwin_amd64.tar.gz'
// } else {
// return error('unsported platform')
// }
mut z_installer := zinit_installer.get()!
z_installer.start()!
// mut dest := osal.download(
// url: url
// minsize_kb: 9000
// expand_dir: '/tmp/mycelium'
// )!
mut url := ''
if core.is_linux_arm()! {
url = 'https://github.com/threefoldtech/mycelium/releases/download/v${version}/mycelium-aarch64-unknown-linux-musl.tar.gz'
} else if core.is_linux_intel()! {
url = 'https://github.com/threefoldtech/mycelium/releases/download/v${version}/mycelium-x86_64-unknown-linux-musl.tar.gz'
} else if core.is_osx_arm()! {
url = 'https://github.com/threefoldtech/mycelium/releases/download/v${version}/mycelium-aarch64-apple-darwin.tar.gz'
} else if core.is_osx_intel()! {
url = 'https://github.com/threefoldtech/mycelium/releases/download/v${version}/mycelium-x86_64-apple-darwin.tar.gz'
} else {
return error('unsported platform')
}
// //dest.moveup_single_subdir()!
pathlib.get_dir(
path: '${osal.hero_path()!}/cfg'
create: true
)!
mut dest := osal.download(
url: url
minsize_kb: 5000
expand_dir: '/tmp/mycelium'
)!
mut binpath := dest.file_get('mycelium')!
osal.cmd_add(
cmdname: 'mycelium'
source: binpath.path
)!
// mut binpath := dest.file_get('mycelium')!
// osal.cmd_add(
// cmdname: 'mycelium'
// source: binpath.path
// )!
}
fn build() ! {
url := 'https://github.com/threefoldtech/mycelium'
myplatform := core.platform()!
if myplatform != .ubuntu {
return error('only support ubuntu for now')
}
rust.install()!
//url := 'https://github.com/threefoldtech/mycelium'
console.print_header('build mycelium')
// make sure we install base on the node
// if osal.platform() != .ubuntu {
// return error('only support ubuntu for now')
// }
// golang.install()!
mut gs := gittools.new()!
gitpath := gs.get_path(
pull: true
reset: false
url: url
)!
// console.print_header('build mycelium')
panic('implement')
// gitpath := gittools.get_repo(coderoot: '/tmp/builder', url: url, reset: true, pull: true)!
cmd := '
cd ${gitpath}
source ~/.cargo/env
cargo install --path . --locked
cargo build --release --locked --no-default-features --features=native-tls
cp target/release/mycelium ~/.cargo/bin/mycelium
mycelium --version
'
osal.execute_stdout(cmd)!
// cmd := '
// cd ${gitpath}
// source ~/.cargo/env
// exit 1 #todo
// '
// osal.execute_stdout(cmd)!
//
// //now copy to the default bin path
// mut binpath := dest.file_get('...')!
// adds it to path
// osal.cmd_add(
// cmdname: 'griddriver2'
// source: binpath.path
// )!
// //now copy to the default bin path
// mut binpath := dest.file_get('...')!
// adds it to path
// osal.cmd_add(
// cmdname: 'griddriver2'
// source: binpath.path
// )!
}
fn destroy() ! {
osal.process_kill_recursive(name: 'mycelium')!
osal.cmd_delete('mycelium')!
osal.rm('
mycelium
')!
// mut systemdfactory := systemd.new()!
// systemdfactory.destroy("zinit")!
// osal.process_kill_recursive(name:'zinit')!
// osal.cmd_delete('zinit')!
// osal.package_remove('
// podman
// conmon
// buildah
// skopeo
// runc
// ')!
// //will remove all paths where go/bin is found
// osal.profile_path_add_remove(paths2delete:"go/bin")!
// osal.rm("
// podman
// conmon
// buildah
// skopeo
// runc
// /var/lib/containers
// /var/lib/podman
// /var/lib/buildah
// /tmp/podman
// /tmp/conmon
// ")!
}

View File

@@ -0,0 +1,167 @@
module mycelium
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.installers.sysadmintools.zinit as zinit_installer
import freeflowuniverse.herolib.clients.mycelium
import freeflowuniverse.herolib.develop.gittools
import freeflowuniverse.herolib.osal.zinit
import freeflowuniverse.herolib.installers.ulist
import freeflowuniverse.herolib.installers.lang.rust
import os
fn startupcmd() ![]zinit.ZProcessNewArgs {
mut installer := get()!
mut res := []zinit.ZProcessNewArgs{}
mut peers_str := installer.peers.join(' ')
mut tun_name := 'tun${installer.tun_nr}'
res << zinit.ZProcessNewArgs{
name: 'mycelium'
cmd: 'mycelium --key-file ${osal.hero_path()!}/cfg/priv_key.bin --peers ${peers_str} --tun-name ${tun_name}'
env: {
'HOME': '/root'
}
}
return res
}
fn running() !bool {
mycelium.inspect() or { return false }
return true
}
fn start_pre() ! {
}
fn start_post() ! {
}
fn stop_pre() ! {
}
fn stop_post() ! {
}
//////////////////// following actions are not specific to instance of the object
// checks if a certain version or above is installed
fn installed() !bool {
cmd := '${osal.profile_path_source_and()!} mycelium -V'
// println(cmd)
res := os.execute(cmd)
if res.exit_code != 0 {
println(res)
return false
}
r := res.output.split_into_lines().filter(it.trim_space().len > 0)
if r.len != 1 {
return error("couldn't parse mycelium version.\n${res.output}")
}
if texttools.version(version) == texttools.version(r[0].all_after_last('mycelium')) {
return true
}
return false
}
// get the Upload List of the files
fn ulist_get() !ulist.UList {
// optionally build a UList which is all paths which are result of building, is then used e.g. in upload
return ulist.UList{}
}
// uploads to S3 server if configured
fn upload() ! {
// installers.upload(
// cmdname: 'mycelium'
// source: '${gitpath}/target/x86_64-unknown-linux-musl/release/mycelium'
// )!
}
fn install() ! {
console.print_header('install mycelium')
mut z_installer := zinit_installer.get()!
z_installer.start()!
mut url := ''
if core.is_linux_arm()! {
url = 'https://github.com/threefoldtech/mycelium/releases/download/v${version}/mycelium-aarch64-unknown-linux-musl.tar.gz'
} else if core.is_linux_intel()! {
url = 'https://github.com/threefoldtech/mycelium/releases/download/v${version}/mycelium-x86_64-unknown-linux-musl.tar.gz'
} else if core.is_osx_arm()! {
url = 'https://github.com/threefoldtech/mycelium/releases/download/v${version}/mycelium-aarch64-apple-darwin.tar.gz'
} else if core.is_osx_intel()! {
url = 'https://github.com/threefoldtech/mycelium/releases/download/v${version}/mycelium-x86_64-apple-darwin.tar.gz'
} else {
return error('unsported platform')
}
pathlib.get_dir(
path: '${osal.hero_path()!}/cfg'
create: true
)!
mut dest := osal.download(
url: url
minsize_kb: 5000
expand_dir: '/tmp/mycelium'
)!
mut binpath := dest.file_get('mycelium')!
osal.cmd_add(
cmdname: 'mycelium'
source: binpath.path
)!
}
fn build() ! {
url := 'https://github.com/threefoldtech/mycelium'
myplatform := core.platform()!
if myplatform != .ubuntu {
return error('only support ubuntu for now')
}
rust.install()!
console.print_header('build mycelium')
mut gs := gittools.new()!
gitpath := gs.get_path(
pull: true
reset: false
url: url
)!
panic('implement')
cmd := '
cd ${gitpath}
source ~/.cargo/env
cargo install --path . --locked
cargo build --release --locked --no-default-features --features=native-tls
cp target/release/mycelium ~/.cargo/bin/mycelium
mycelium --version
'
osal.execute_stdout(cmd)!
// //now copy to the default bin path
// mut binpath := dest.file_get('...')!
// adds it to path
// osal.cmd_add(
// cmdname: 'griddriver2'
// source: binpath.path
// )!
}
fn destroy() ! {
osal.process_kill_recursive(name: 'mycelium')!
osal.cmd_delete('mycelium')!
osal.rm('
mycelium
')!
}

View File

@@ -4,277 +4,288 @@ import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.data.paramsparser
import freeflowuniverse.herolib.sysadmin.startupmanager
import freeflowuniverse.herolib.osal.zinit
import time
__global (
mycelium_global map[string]&MyceliumInstaller
mycelium_default string
mycelium_global map[string]&MyceliumInstaller
mycelium_default string
)
/////////FACTORY
@[params]
pub struct ArgsGet {
pub struct ArgsGet{
pub mut:
name string
name string
}
fn args_get(args_ ArgsGet) ArgsGet {
mut args := args_
if args.name == '' {
args.name = 'default'
}
return args
fn args_get (args_ ArgsGet) ArgsGet {
mut args:=args_
if args.name == ""{
args.name = "default"
}
return args
}
pub fn get(args_ ArgsGet) !&MyceliumInstaller {
mut context := base.context()!
mut args := args_get(args_)
mut obj := MyceliumInstaller{}
if args.name !in mycelium_global {
if !exists(args)! {
set(obj)!
} else {
heroscript := context.hero_config_get('mycelium', args.name)!
mut obj_ := heroscript_loads(heroscript)!
set_in_mem(obj_)!
}
}
return mycelium_global[args.name] or {
println(mycelium_global)
// bug if we get here because should be in globals
panic('could not get config for mycelium with name, is bug:${args.name}')
}
pub fn get(args_ ArgsGet) !&MyceliumInstaller {
mut context:=base.context()!
mut args := args_get(args_)
mut obj := MyceliumInstaller{}
if !(args.name in mycelium_global) {
if ! exists(args)!{
set(obj)!
}else{
heroscript := context.hero_config_get("mycelium",args.name)!
mut obj_:=heroscript_loads(heroscript)!
set_in_mem(obj_)!
}
}
return mycelium_global[args.name] or {
println(mycelium_global)
//bug if we get here because should be in globals
panic("could not get config for mycelium with name, is bug:${args.name}")
}
}
// register the config for the future
pub fn set(o MyceliumInstaller) ! {
set_in_mem(o)!
mut context := base.context()!
heroscript := heroscript_dumps(o)!
context.hero_config_set('mycelium', o.name, heroscript)!
//register the config for the future
pub fn set(o MyceliumInstaller)! {
set_in_mem(o)!
mut context := base.context()!
heroscript := heroscript_dumps(o)!
context.hero_config_set("mycelium", o.name, heroscript)!
}
// does the config exists?
pub fn exists(args_ ArgsGet) !bool {
mut context := base.context()!
mut args := args_get(args_)
return context.hero_config_exists('mycelium', args.name)
//does the config exists?
pub fn exists(args_ ArgsGet)! bool {
mut context := base.context()!
mut args := args_get(args_)
return context.hero_config_exists("mycelium", args.name)
}
pub fn delete(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()!
context.hero_config_delete('mycelium', args.name)!
if args.name in mycelium_global {
// del mycelium_global[args.name]
}
pub fn delete(args_ ArgsGet)! {
mut args := args_get(args_)
mut context:=base.context()!
context.hero_config_delete("mycelium",args.name)!
if args.name in mycelium_global {
//del mycelium_global[args.name]
}
}
// only sets in mem, does not set as config
fn set_in_mem(o MyceliumInstaller) ! {
mut o2 := obj_init(o)!
mycelium_global[o.name] = &o2
mycelium_default = o.name
//only sets in mem, does not set as config
fn set_in_mem(o MyceliumInstaller)! {
mut o2:=obj_init(o)!
mycelium_global[o.name] = &o2
mycelium_default = o.name
}
@[params]
pub struct PlayArgs {
pub mut:
heroscript string // if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
heroscript string //if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
}
pub fn play(args_ PlayArgs) ! {
mut args := args_
mut args:=args_
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }
mut install_actions := plbook.find(filter: 'mycelium.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
heroscript := install_action.heroscript()
mut obj2 := heroscript_loads(heroscript)!
set(obj2)!
}
}
mut plbook := args.plbook or {
playbook.new(text: args.heroscript)!
}
mut install_actions := plbook.find(filter: 'mycelium.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
heroscript:=install_action.heroscript()
mut obj2:=heroscript_loads(heroscript)!
set(obj2)!
}
}
mut other_actions := plbook.find(filter: 'mycelium.')!
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 mycelium.destroy')
destroy()!
}
if other_action.name == 'install' {
console.print_debug('install action mycelium.install')
install()!
}
}
if other_action.name in ['start', 'stop', 'restart'] {
mut p := other_action.params
name := p.get('name')!
mut mycelium_obj := get(name: name)!
console.print_debug('action object:\n${mycelium_obj}')
if other_action.name == 'start' {
console.print_debug('install action mycelium.${other_action.name}')
mycelium_obj.start()!
}
mut other_actions := plbook.find(filter: 'mycelium.')!
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 mycelium.destroy")
destroy()!
}
if other_action.name == "install"{
console.print_debug("install action mycelium.install")
install()!
}
}
if other_action.name in ["start","stop","restart"]{
mut p := other_action.params
name := p.get('name')!
mut mycelium_obj:=get(name:name)!
console.print_debug("action object:\n${mycelium_obj}")
if other_action.name == "start"{
console.print_debug("install action mycelium.${other_action.name}")
mycelium_obj.start()!
}
if other_action.name == "stop"{
console.print_debug("install action mycelium.${other_action.name}")
mycelium_obj.stop()!
}
if other_action.name == "restart"{
console.print_debug("install action mycelium.${other_action.name}")
mycelium_obj.restart()!
}
}
}
if other_action.name == 'stop' {
console.print_debug('install action mycelium.${other_action.name}')
mycelium_obj.stop()!
}
if other_action.name == 'restart' {
console.print_debug('install action mycelium.${other_action.name}')
mycelium_obj.restart()!
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////# 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()!
}
}
// 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()!
}
}
}
// load from disk and make sure is properly intialized
//load from disk and make sure is properly intialized
pub fn (mut self MyceliumInstaller) reload() ! {
switch(self.name)
self = obj_init(self)!
switch(self.name)
self=obj_init(self)!
}
pub fn (mut self MyceliumInstaller) start() ! {
switch(self.name)
if self.running()! {
return
}
switch(self.name)
if self.running()!{
return
}
console.print_header('mycelium start')
console.print_header('mycelium start')
if !installed()! {
install()!
}
if ! installed()!{
install()!
}
configure()!
configure()!
start_pre()!
start_pre()!
for zprocess in startupcmd()! {
mut sm := startupmanager_get(zprocess.startuptype)!
for zprocess in startupcmd()!{
mut sm:=startupmanager_get(zprocess.startuptype)!
console.print_debug('starting mycelium with ${zprocess.startuptype}...')
console.print_debug('starting mycelium with ${zprocess.startuptype}...')
sm.new(zprocess)!
sm.new(zprocess)!
sm.start(zprocess.name)!
}
sm.start(zprocess.name)!
}
start_post()!
start_post()!
for _ in 0 .. 50 {
if self.running()! {
return
}
time.sleep(100 * time.millisecond)
}
return error('mycelium did not install properly.')
for _ in 0 .. 50 {
if self.running()! {
return
}
time.sleep(100 * time.millisecond)
}
return error('mycelium did not install properly.')
}
pub fn (mut self MyceliumInstaller) install_start(args InstallArgs) ! {
switch(self.name)
self.install(args)!
self.start()!
switch(self.name)
self.install(args)!
self.start()!
}
pub fn (mut self MyceliumInstaller) stop() ! {
switch(self.name)
stop_pre()!
for zprocess in startupcmd()! {
mut sm := startupmanager_get(zprocess.startuptype)!
sm.stop(zprocess.name)!
}
stop_post()!
switch(self.name)
stop_pre()!
for zprocess in startupcmd()!{
mut sm:=startupmanager_get(zprocess.startuptype)!
sm.stop(zprocess.name)!
}
stop_post()!
}
pub fn (mut self MyceliumInstaller) restart() ! {
switch(self.name)
self.stop()!
self.start()!
switch(self.name)
self.stop()!
self.start()!
}
pub fn (mut self MyceliumInstaller) running() !bool {
switch(self.name)
switch(self.name)
// 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
}
}
return running()!
//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
}
}
return running()!
}
@[params]
pub struct InstallArgs {
pub struct InstallArgs{
pub mut:
reset bool
reset bool
}
pub fn (mut self MyceliumInstaller) install(args InstallArgs) ! {
switch(self.name)
if args.reset || (!installed()!) {
install()!
}
switch(self.name)
if args.reset || (!installed()!) {
install()!
}
}
pub fn (mut self MyceliumInstaller) build() ! {
switch(self.name)
build()!
switch(self.name)
build()!
}
pub fn (mut self MyceliumInstaller) destroy() ! {
switch(self.name)
self.stop() or {}
destroy()!
switch(self.name)
self.stop() or {}
destroy()!
}
// switch instance to be used for mycelium
//switch instance to be used for mycelium
pub fn switch(name string) {
mycelium_default = name
mycelium_default = name
}
// helpers
//helpers
@[params]
pub struct DefaultConfigArgs {
instance string = 'default'
pub struct DefaultConfigArgs{
instance string = 'default'
}

View File

@@ -1,70 +1,50 @@
module mycelium
import freeflowuniverse.herolib.data.paramsparser
import freeflowuniverse.herolib.data.encoderhero
import freeflowuniverse.herolib.osal.tun
import os
pub const version = '0.5.7'
const singleton = true
pub const version = '0.0.0'
const singleton = false
const default = true
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
//THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
@[heap]
pub struct MyceliumInstaller {
pub mut:
name string = 'default'
peers []string = [
'tcp://188.40.132.242:9651',
'quic://[2a01:4f8:212:fa6::2]:9651',
'tcp://185.69.166.7:9651',
'quic://[2a02:1802:5e:0:ec4:7aff:fe51:e36b]:9651',
'tcp://65.21.231.58:9651',
'quic://[2a01:4f9:5a:1042::2]:9651',
'tcp://[2604:a00:50:17b:9e6b:ff:fe1f:e054]:9651',
'quic://5.78.122.16:9651',
'tcp://[2a01:4ff:2f0:3621::1]:9651',
'quic://142.93.217.194:9651',
]
tun_nr int
name string = 'default'
homedir string
configpath string
username string
password string @[secret]
title string
host string
port int
}
// your checking & initialization code if needed
fn obj_init(mycfg_ MyceliumInstaller) !MyceliumInstaller {
mut mycfg := mycfg_
return mycfg
//your checking & initialization code if needed
fn obj_init(mycfg_ MyceliumInstaller)!MyceliumInstaller{
mut mycfg:=mycfg_
if mycfg.password == '' && mycfg.secret == '' {
return error('password or secret needs to be filled in for ${mycfg.name}')
}
return mycfg
}
// called before start if done
//called before start if done
fn configure() ! {
mut installer := get()!
if installer.tun_nr == 0 {
// Check if TUN is available first
if available := tun.available() {
if !available {
return error('TUN is not available on this system')
}
// Get free TUN interface name
if interface_name := tun.free() {
// Parse the interface number from the name (e.g. "tun0" -> 0)
nr := interface_name.trim_string_left('tun').int()
installer.tun_nr = nr
} else {
return error('Failed to get free TUN interface: ${err}')
}
} else {
return error('Failed to check TUN availability: ${err}')
}
set(installer)!
}
//mut installer := get()!
}
/////////////NORMALLY NO NEED TO TOUCH
pub fn heroscript_dumps(obj MyceliumInstaller) !string {
return encoderhero.encode[MyceliumInstaller](obj)!
return encoderhero.encode[MyceliumInstaller ](obj)!
}
pub fn heroscript_loads(heroscript string) !MyceliumInstaller {
mut obj := encoderhero.decode[MyceliumInstaller](heroscript)!
return obj
mut obj := encoderhero.decode[MyceliumInstaller](heroscript)!
return obj
}

View File

@@ -0,0 +1,74 @@
module mycelium
import freeflowuniverse.herolib.data.encoderhero
import freeflowuniverse.herolib.osal.tun
pub const version = '0.5.7'
const singleton = true
const default = true
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
@[heap]
pub struct MyceliumInstaller {
pub mut:
name string = 'default'
peers []string = [
'tcp://188.40.132.242:9651',
'quic://[2a01:4f8:212:fa6::2]:9651',
'tcp://185.69.166.7:9651',
'quic://[2a02:1802:5e:0:ec4:7aff:fe51:e36b]:9651',
'tcp://65.21.231.58:9651',
'quic://[2a01:4f9:5a:1042::2]:9651',
'tcp://[2604:a00:50:17b:9e6b:ff:fe1f:e054]:9651',
'quic://5.78.122.16:9651',
'tcp://[2a01:4ff:2f0:3621::1]:9651',
'quic://142.93.217.194:9651',
]
tun_nr int = 1
tcp_listen_port int = 9651
quic_listen_port int = 9651
peer_discovery_port int = 9650
api_address string = '127.0.0.1:8989'
key_file string = 'priv_key.bin'
}
// your checking & initialization code if needed
fn obj_init(mycfg_ MyceliumInstaller) !MyceliumInstaller {
mut mycfg := mycfg_
return mycfg
}
// called before start if done
fn configure() ! {
mut installer := get()!
if installer.tun_nr == 0 {
// Check if TUN is available first
if available := tun.available() {
if !available {
return error('TUN is not available on this system')
}
// Get free TUN interface name
if interface_name := tun.free() {
// Parse the interface number from the name (e.g. "tun0" -> 0)
nr := interface_name.trim_string_left('tun').int()
installer.tun_nr = nr
} else {
return error('Failed to get free TUN interface: ${err}')
}
} else {
return error('Failed to check TUN availability: ${err}')
}
set(installer)!
}
}
/////////////NORMALLY NO NEED TO TOUCH
pub fn heroscript_dumps(obj MyceliumInstaller) !string {
return encoderhero.encode[MyceliumInstaller](obj)!
}
pub fn heroscript_loads(heroscript string) !MyceliumInstaller {
mut obj := encoderhero.decode[MyceliumInstaller](heroscript)!
return obj
}

View File

@@ -0,0 +1,11 @@
!!hero_code.generate_installer
name:'traefik'
classname:'TraefikServer'
singleton:1 //there can only be 1 object in the globals, is called 'default'
templates:1 //are there templates for the installer
default:1 //can we create a default when the factory is used
title:''
supported_platforms:'' //osx, ... (empty means all)
reset:0 // regenerate all, dangerous !!!
startupmanager:1 //managed by a startup manager, default true

View File

@@ -0,0 +1,19 @@
module traefik
import crypto.bcrypt
// generate_htpasswd creates an Apache-style htpasswd entry for the given user and password
// using bcrypt hashing with configurable cost (default 12)
fn generate_htpasswd(user string, password string) !string {
// Generate bcrypt hash
hashed_password := bcrypt.generate_from_password(password.bytes(), 12) or {
return error('Failed to hash password: ${err}')
}
println(hashed_password)
// Return final formatted string
return '${user}:${hashed_password}'
}

View File

@@ -0,0 +1,21 @@
# traefik
To get started
```vlang
import freeflowuniverse.herolib.installers.web.traefik as traefik_installer
mut installer:= traefik_installer.get()!
installer.start()!
```

View File

@@ -0,0 +1,36 @@
[api]
dashboard = true
debug = false
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.https]
address = ":443"
# [providers]
# [providers.file]
# filename = "/etc/traefik/dynamic_conf.toml"
# watch = true
[log]
level = "WARN"
[accessLog]
[middlewares]
[middlewares.basicAuth]
[middlewares.basicAuth.basicAuth]
users = [
"${htaccesscode}"
]
realm = "Traefik Dashboard"
removeHeader = true
[http.routers]
[http.routers.api]
rule = "Host(`traefik.local`)"
service = "api^^internal"
entryPoints = ["https"]
middlewares = ["basicAuth"]

View File

@@ -0,0 +1,127 @@
module traefik
import freeflowuniverse.herolib.osal
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.osal.zinit
import freeflowuniverse.herolib.installers.ulist
import os
fn startupcmd () ![]zinit.ZProcessNewArgs{
mut installer := get()!
mut res := []zinit.ZProcessNewArgs{}
res << zinit.ZProcessNewArgs{
name: 'traefik'
cmd: 'traefik'
}
return res
}
fn running() !bool {
cmd:="traefik healthcheck"
res := os.execute(cmd)
if res.exit_code != 0 {
return false
}
return true
}
fn start_pre()!{
}
fn start_post()!{
}
fn stop_pre()!{
}
fn stop_post()!{
}
//////////////////// following actions are not specific to instance of the object
// checks if a certain version or above is installed
fn installed() !bool {
res := os.execute('${osal.profile_path_source_and()!} traefik version')
if res.exit_code != 0 {
return false
}
r := res.output.split_into_lines().filter(it.contains("Version"))
if r.len != 1 {
return error("couldn't parse traefik version.\n${res.output}")
}
if texttools.version(version) == texttools.version(r[0].all_after("Version:")) {
return true
}
return false
}
//get the Upload List of the files
fn ulist_get() !ulist.UList {
return ulist.UList{}
}
fn upload() ! {
// installers.upload(
// cmdname: 'traefik'
// source: '${gitpath}/target/x86_64-unknown-linux-musl/release/traefik'
// )!
}
fn install() ! {
console.print_header('install traefik')
mut url := ''
if core.is_linux_arm()! {
url = 'https://github.com/traefik/traefik/releases/download/v${version}/traefik_v${version}_linux_arm64.tar.gz'
} else if core.is_linux_intel()! {
url = 'https://github.com/traefik/traefik/releases/download/v${version}/traefik_v${version}_linux_amd64.tar.gz'
} else if core.is_osx_arm()! {
url = 'https://github.com/traefik/traefik/releases/download/v${version}/traefik_v${version}_darwin_arm64.tar.gz'
} else if core.is_osx_intel()! {
url = 'https://github.com/traefik/traefik/releases/download/v${version}/traefik_v${version}_darwin_arm64.tar.gz'
} else {
return error('unsported platform')
}
mut dest := osal.download(
url: url
minsize_kb: 20000
expand_dir: '/tmp/traefik'
)!
mut binpath := dest.file_get('traefik')!
osal.cmd_add(
cmdname: 'traefik'
source: binpath.path
)!
}
fn destroy() ! {
osal.process_kill_recursive(name:'traefik')!
osal.cmd_delete('traefik')!
osal.package_remove('
traefik
')!
osal.rm("
traefik
")!
}

View File

@@ -0,0 +1,287 @@
module traefik
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.data.paramsparser
import freeflowuniverse.herolib.sysadmin.startupmanager
import freeflowuniverse.herolib.osal.zinit
import time
__global (
traefik_global map[string]&TraefikServer
traefik_default string
)
/////////FACTORY
@[params]
pub struct ArgsGet{
pub mut:
name string
}
fn args_get (args_ ArgsGet) ArgsGet {
mut args:=args_
if args.name == ""{
args.name = "default"
}
return args
}
pub fn get(args_ ArgsGet) !&TraefikServer {
mut context:=base.context()!
mut args := args_get(args_)
mut obj := TraefikServer{}
if !(args.name in traefik_global) {
if ! exists(args)!{
set(obj)!
}else{
heroscript := context.hero_config_get("traefik",args.name)!
mut obj_:=heroscript_loads(heroscript)!
set_in_mem(obj_)!
}
}
return traefik_global[args.name] or {
println(traefik_global)
//bug if we get here because should be in globals
panic("could not get config for traefik with name, is bug:${args.name}")
}
}
//register the config for the future
pub fn set(o TraefikServer)! {
set_in_mem(o)!
mut context := base.context()!
heroscript := heroscript_dumps(o)!
context.hero_config_set("traefik", o.name, heroscript)!
}
//does the config exists?
pub fn exists(args_ ArgsGet)! bool {
mut context := base.context()!
mut args := args_get(args_)
return context.hero_config_exists("traefik", args.name)
}
pub fn delete(args_ ArgsGet)! {
mut args := args_get(args_)
mut context:=base.context()!
context.hero_config_delete("traefik",args.name)!
if args.name in traefik_global {
//del traefik_global[args.name]
}
}
//only sets in mem, does not set as config
fn set_in_mem(o TraefikServer)! {
mut o2:=obj_init(o)!
traefik_global[o.name] = &o2
traefik_default = o.name
}
@[params]
pub struct PlayArgs {
pub mut:
heroscript string //if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
}
pub fn play(args_ PlayArgs) ! {
mut args:=args_
mut plbook := args.plbook or {
playbook.new(text: args.heroscript)!
}
mut install_actions := plbook.find(filter: 'traefik.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
heroscript:=install_action.heroscript()
mut obj2:=heroscript_loads(heroscript)!
set(obj2)!
}
}
mut other_actions := plbook.find(filter: 'traefik.')!
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 traefik.destroy")
destroy()!
}
if other_action.name == "install"{
console.print_debug("install action traefik.install")
install()!
}
}
if other_action.name in ["start","stop","restart"]{
mut p := other_action.params
name := p.get('name')!
mut traefik_obj:=get(name:name)!
console.print_debug("action object:\n${traefik_obj}")
if other_action.name == "start"{
console.print_debug("install action traefik.${other_action.name}")
traefik_obj.start()!
}
if other_action.name == "stop"{
console.print_debug("install action traefik.${other_action.name}")
traefik_obj.stop()!
}
if other_action.name == "restart"{
console.print_debug("install action traefik.${other_action.name}")
traefik_obj.restart()!
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////# 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()!
}
}
}
//load from disk and make sure is properly intialized
pub fn (mut self TraefikServer) reload() ! {
switch(self.name)
self=obj_init(self)!
}
pub fn (mut self TraefikServer) start() ! {
switch(self.name)
if self.running()!{
return
}
console.print_header('traefik start')
if ! installed()!{
install()!
}
configure()!
start_pre()!
for zprocess in startupcmd()!{
mut sm:=startupmanager_get(zprocess.startuptype)!
console.print_debug('starting traefik with ${zprocess.startuptype}...')
sm.new(zprocess)!
sm.start(zprocess.name)!
}
start_post()!
for _ in 0 .. 50 {
if self.running()! {
return
}
time.sleep(100 * time.millisecond)
}
return error('traefik did not install properly.')
}
pub fn (mut self TraefikServer) install_start(args InstallArgs) ! {
switch(self.name)
self.install(args)!
self.start()!
}
pub fn (mut self TraefikServer) stop() ! {
switch(self.name)
stop_pre()!
for zprocess in startupcmd()!{
mut sm:=startupmanager_get(zprocess.startuptype)!
sm.stop(zprocess.name)!
}
stop_post()!
}
pub fn (mut self TraefikServer) restart() ! {
switch(self.name)
self.stop()!
self.start()!
}
pub fn (mut self TraefikServer) running() !bool {
switch(self.name)
//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
}
}
return running()!
}
@[params]
pub struct InstallArgs{
pub mut:
reset bool
}
pub fn (mut self TraefikServer) install(args InstallArgs) ! {
switch(self.name)
if args.reset || (!installed()!) {
install()!
}
}
pub fn (mut self TraefikServer) destroy() ! {
switch(self.name)
self.stop() or {}
destroy()!
}
//switch instance to be used for traefik
pub fn switch(name string) {
traefik_default = name
}
//helpers
@[params]
pub struct DefaultConfigArgs{
instance string = 'default'
}

View File

@@ -0,0 +1,54 @@
module traefik
import freeflowuniverse.herolib.data.paramsparser
import freeflowuniverse.herolib.data.encoderhero
import os
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.pathlib
pub const version = '3.3.3'
const singleton = true
const default = true
//THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
@[heap]
pub struct TraefikServer {
pub mut:
name string = 'default'
// homedir string
// configpath string
// username string
password string @[secret]
// title string
// host string
// port int
}
//your checking & initialization code if needed
fn obj_init(mycfg_ TraefikServer)!TraefikServer{
mut mycfg:=mycfg_
return mycfg
}
//called before start if done
fn configure() ! {
mut installer := get()!
htaccesscode:=generate_htpasswd("admin",installer.password)!
mut mycode := $tmpl('templates/traefik.toml')
mut path := pathlib.get_file(path: "/etc/traefik/traefik.toml", create: true)!
path.write(mycode)!
console.print_debug(mycode)
}
/////////////NORMALLY NO NEED TO TOUCH
pub fn heroscript_dumps(obj TraefikServer) !string {
return encoderhero.encode[TraefikServer ](obj)!
}
pub fn heroscript_loads(heroscript string) !TraefikServer {
mut obj := encoderhero.decode[TraefikServer](heroscript)!
return obj
}

View File

@@ -4,6 +4,7 @@ import net
import time
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core
import os
pub enum PingResult {
ok
@@ -100,10 +101,39 @@ pub fn tcp_port_test(args TcpPortTestArgs) bool {
return false
}
// Returns the ipaddress as known on the public side
// is using resolver4.opendns.com
// Returns the public IP address as known on the public side
// Uses resolver4.opendns.com to fetch the IP address
pub fn ipaddr_pub_get() !string {
cmd := 'dig @resolver4.opendns.com myip.opendns.com +short'
ipaddr := exec(cmd: cmd)!
return ipaddr.output.trim('\n').trim(' \n')
cmd := 'dig @resolver4.opendns.com myip.opendns.com +short'
ipaddr := exec(cmd: cmd)!
public_ip := ipaddr.output.trim('\n').trim(' \n')
// Check if the public IP matches any local interface
if ! is_ip_on_local_interface(public_ip)! {
return error ('Public IP $public_ip is NOT bound to any local interface (possibly behind a NAT firewall).')
}
return public_ip
}
// Check if the public IP matches any of the local network interfaces
pub fn is_ip_on_local_interface(public_ip string) !bool {
interfaces := exec(cmd:'ip addr show',stdout:false) or {
return error ('Failed to enumerate network interfaces.')
}
lines := interfaces.output.split('\n')
// Parse through the `ip addr show` output to find local IPs
for line in lines {
if line.contains('inet ') {
parts := line.trim_space().split(' ')
if parts.len > 1 {
local_ip := parts[1].split('/')[0] // Extract the IP address
if public_ip == local_ip {
return true
}
}
}
}
return false
}