Files
herolib/lib/mycelium/grid3/deploy_tosort/deployment.v
2025-12-02 10:17:45 +01:00

259 lines
6.9 KiB
V

module deploy
import incubaid.herolib.mycelium.grid3.models as grid_models
import incubaid.herolib.data.paramsparser
import incubaid.herolib.mycelium.grid
import incubaid.herolib.data.encoder
import incubaid.herolib.ui.console
import rand
import json
import encoding.base64
import os
@[heap]
pub struct TFDeployment {
pub mut:
name string = 'default'
description string
vms []VMachine
mut:
deployer ?grid.Deployer @[skip; str: skip]
}
fn (mut self TFDeployment) new_deployer() !grid.Deployer {
if self.deployer == none {
mut grid_client := get()!
network := match grid_client.network {
.dev { grid.ChainNetwork.dev }
.test { grid.ChainNetwork.test }
.main { grid.ChainNetwork.main }
}
self.deployer = grid.new_deployer(grid_client.mnemonic, network)!
}
return self.deployer or { return error('Deployer not initialized') }
}
pub fn (mut self TFDeployment) vm_deploy(args_ VMRequirements) !VMachine {
console.print_header('Starting deployment process.')
mut deployer := self.new_deployer() or { return error('Failed to initialize deployer: ${err}') }
mut node_id := get_node_id(args_) or { return error('Failed to determine node ID: ${err}') }
mut network := args_.network or {
NetworkSpecs{
name: 'net' + rand.string(5)
ip_range: '10.249.0.0/16'
subnet: '10.249.0.0/24'
}
}
wg_port := deployer.assign_wg_port(node_id)!
workloads := create_workloads(args_, network, wg_port)!
console.print_header('Creating deployment.')
mut deployment := grid_models.new_deployment(
twin_id: deployer.twin_id
description: 'VGridClient Deployment'
workloads: workloads
signature_requirement: create_signature_requirement(deployer.twin_id)
)
console.print_header('Setting metadata and deploying workloads.')
deployment.add_metadata('vm', args_.name)
contract_id := deployer.deploy(node_id, mut deployment, deployment.metadata, 0) or {
return error('Deployment failed: ${err}')
}
console.print_header('Deployment successful. Contract ID: ${contract_id}')
vm := VMachine{
tfchain_id: '${deployer.twin_id}${args_.name}'
tfchain_contract_id: int(contract_id)
requirements: VMRequirements{
name: args_.name
description: args_.description
cpu: args_.cpu
memory: args_.memory
}
}
self.name = args_.name
self.description = args_.description
self.vms << vm
self.save()!
self.load(args_.name)!
return vm
}
fn get_node_id(args_ VMRequirements) !u32 {
if args_.nodes.len == 0 {
console.print_header('Requesting the proxy to filter nodes.')
nodes := nodefilter(args_)!
if nodes.len == 0 {
return error('No suitable nodes found.')
}
return u32(nodes[0].node_id)
}
return u32(args_.nodes[0])
}
fn create_workloads(args_ VMRequirements, network NetworkSpecs, wg_port u32) ![]grid_models.Workload {
mut workloads := []grid_models.Workload{}
workloads << create_network_workload(network, wg_port)!
mut public_ip_name := ''
if args_.public_ip4 || args_.public_ip6 {
public_ip_name = rand.string(5).to_lower()
workloads << create_public_ip_workload(args_.public_ip4, args_.public_ip6, public_ip_name)
}
zmachine := create_zmachine_workload(args_, network, public_ip_name)!
workloads << zmachine.to_workload(
name: args_.name
description: args_.description
)
return workloads
}
fn create_signature_requirement(twin_id int) grid_models.SignatureRequirement {
console.print_header('Setting signature requirement.')
return grid_models.SignatureRequirement{
weight_required: 1
requests: [
grid_models.SignatureRequest{
twin_id: u32(twin_id)
weight: 1
},
]
}
}
fn create_network_workload(network NetworkSpecs, wg_port u32) !grid_models.Workload {
console.print_header('Creating network workload.')
return grid_models.Znet{
ip_range: network.ip_range
subnet: network.subnet
wireguard_private_key: 'GDU+cjKrHNJS9fodzjFDzNFl5su3kJXTZ3ipPgUjOUE='
wireguard_listen_port: u16(wg_port)
mycelium: grid_models.Mycelium{
hex_key: rand.string(32).bytes().hex()
}
peers: [
grid_models.Peer{
subnet: network.subnet
wireguard_public_key: '4KTvZS2KPWYfMr+GbiUUly0ANVg8jBC7xP9Bl79Z8zM='
allowed_ips: [network.subnet]
},
]
}.to_workload(
name: network.name
description: 'VGridClient Network'
)
}
fn create_public_ip_workload(is_v4 bool, is_v6 bool, name string) grid_models.Workload {
console.print_header('Creating Public IP workload.')
return grid_models.PublicIP{
v4: is_v4
v6: is_v6
}.to_workload(name: name)
}
fn create_zmachine_workload(args_ VMRequirements, network NetworkSpecs, public_ip_name string) !grid_models.Zmachine {
console.print_header('Creating Zmachine workload.')
mut grid_client := get()!
return grid_models.Zmachine{
network: grid_models.ZmachineNetwork{
interfaces: [
grid_models.ZNetworkInterface{
network: network.name
ip: network.ip_range.split('/')[0]
},
]
public_ip: public_ip_name
planetary: args_.planetary
mycelium: grid_models.MyceliumIP{
network: network.name
hex_seed: rand.string(6).bytes().hex()
}
}
flist: 'https://hub.grid.tf/tf-official-vms/ubuntu-24.04-latest.flist'
entrypoint: '/sbin/zinit init'
compute_capacity: grid_models.ComputeCapacity{
cpu: u8(args_.cpu)
memory: i64(args_.memory) * 1024 * 1024 * 1024
}
env: {
'SSH_KEY': grid_client.ssh_key
}
}
}
pub fn (mut self TFDeployment) vm_get(name string) ![]VMachine {
d := self.load(name)!
return d.vms
}
pub fn (mut self TFDeployment) load(deployment_name string) !TFDeployment {
path := '${os.home_dir()}/hero/var/tfgrid/deploy/'
filepath := '${path}${deployment_name}'
base64_string := os.read_file(filepath) or {
return error('Failed to open file due to: ${err}')
}
bytes := base64.decode(base64_string)
d := self.decode(bytes)!
return d
}
fn (mut self TFDeployment) save() ! {
dir_path := '${os.home_dir()}/hero/var/tfgrid/deploy/'
os.mkdir_all(dir_path)!
file_path := dir_path + self.name
encoded_data := self.encode() or { return error('Failed to encode deployment data: ${err}') }
base64_string := base64.encode(encoded_data)
os.write_file(file_path, base64_string) or { return error('Failed to write to file: ${err}') }
}
fn (self TFDeployment) encode() ![]u8 {
mut b := encoder.new()
b.add_string(self.name)
b.add_int(self.vms.len)
for vm in self.vms {
vm_data := vm.encode()!
b.add_int(vm_data.len)
b.add_bytes(vm_data)
}
return b.data
}
fn (self TFDeployment) decode(data []u8) !TFDeployment {
if data.len == 0 {
return error('Data cannot be empty')
}
mut d := encoder.decoder_new(data)
mut result := TFDeployment{
name: d.get_string()
}
num_vms := d.get_int()
for _ in 0 .. num_vms {
d.get_int()
vm_data := d.get_bytes()
mut dd := encoder.decoder_new(vm_data)
vm := decode_vmachine(dd.data)!
result.vms << vm
}
return result
}