From 3ee0e5b29c8822b717fc340ee410914365c6b713 Mon Sep 17 00:00:00 2001 From: Mahmoud-Emad Date: Sun, 2 Nov 2025 17:24:01 +0200 Subject: [PATCH] feat: Implement Element Chat Kubernetes installer - Add Element Chat installer module - Integrate Conduit and Element Web deployments - Support TFGW integration for FQDNs and TLS - Implement installation and destruction logic - Generate Kubernetes YAML from templates Co-authored-by: peternashaaat --- examples/installers/k8s/.gitignore | 1 + examples/installers/k8s/element_chat.vsh | 42 ++++ lib/installers/k8s/element_chat/.heroscript | 12 ++ lib/installers/k8s/element_chat/README.md | 120 +++++++++++ .../k8s/element_chat/element_chat_actions.v | 195 ++++++++++++++++++ .../k8s/element_chat/element_chat_factory_.v | 188 +++++++++++++++++ .../k8s/element_chat/element_chat_model.v | 177 ++++++++++++++++ .../k8s/element_chat/templates/chat-app.yaml | 139 +++++++++++++ .../element_chat/templates/conduit.toml.temp | 9 + .../templates/element-config.json | 10 + .../k8s/element_chat/templates/tfgw.yaml | 24 +++ 11 files changed, 917 insertions(+) create mode 100755 examples/installers/k8s/element_chat.vsh create mode 100644 lib/installers/k8s/element_chat/.heroscript create mode 100644 lib/installers/k8s/element_chat/README.md create mode 100644 lib/installers/k8s/element_chat/element_chat_actions.v create mode 100644 lib/installers/k8s/element_chat/element_chat_factory_.v create mode 100644 lib/installers/k8s/element_chat/element_chat_model.v create mode 100644 lib/installers/k8s/element_chat/templates/chat-app.yaml create mode 100644 lib/installers/k8s/element_chat/templates/conduit.toml.temp create mode 100644 lib/installers/k8s/element_chat/templates/element-config.json create mode 100644 lib/installers/k8s/element_chat/templates/tfgw.yaml diff --git a/examples/installers/k8s/.gitignore b/examples/installers/k8s/.gitignore index f032778d..05081403 100644 --- a/examples/installers/k8s/.gitignore +++ b/examples/installers/k8s/.gitignore @@ -1 +1,2 @@ cryptpad +element_chat \ No newline at end of file diff --git a/examples/installers/k8s/element_chat.vsh b/examples/installers/k8s/element_chat.vsh new file mode 100755 index 00000000..26491a56 --- /dev/null +++ b/examples/installers/k8s/element_chat.vsh @@ -0,0 +1,42 @@ +#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run + +import incubaid.herolib.installers.k8s.element_chat + +// This example demonstrates how to use the Element Chat installer. + +// 1. Create a new installer instance with specific hostnames. +// Replace 'matrixchattest' and 'elementchattest' with your desired hostnames. +// Note: Use only alphanumeric characters (no underscores or dashes). +mut installer := element_chat.get( + name: 'myelementchat' + create: true +)! + +element_chat.delete()! + +// 2. Configure the installer (all settings are optional with sensible defaults) +// installer.matrix_hostname = 'matrixchattest' +// installer.element_hostname = 'elementchattest' +// installer.namespace = 'chat' + +// // Conduit (Matrix homeserver) configuration +// installer.conduit_port = 6167 // Default: 6167 +// installer.database_backend = 'rocksdb' // Default: 'rocksdb' (can be 'sqlite') +// installer.database_path = '/var/lib/matrix-conduit' // Default: '/var/lib/matrix-conduit' +// installer.allow_registration = true // Default: true +// installer.allow_federation = true // Default: true +// installer.log_level = 'info' // Default: 'info' (can be 'debug', 'warn', 'error') + +// // Element web client configuration +// installer.element_brand = 'Element' // Default: 'Element' + +// 3. Install Element Chat. +// This will generate the necessary Kubernetes YAML files and apply them to your cluster. +installer.install()! + +// println('Element Chat installation started.') +// println('Matrix homeserver will be available at: https://${installer.matrix_hostname}.gent01.grid.tf') +// println('Element web client will be available at: https://${installer.element_hostname}.gent01.grid.tf') + +// 4. To destroy the deployment, you can run the following: +// installer.destroy()! diff --git a/lib/installers/k8s/element_chat/.heroscript b/lib/installers/k8s/element_chat/.heroscript new file mode 100644 index 00000000..c9616b59 --- /dev/null +++ b/lib/installers/k8s/element_chat/.heroscript @@ -0,0 +1,12 @@ + +!!hero_code.generate_installer + name:'' + classname:'ElementChat' + singleton:0 + templates:1 + default:0 + title:'' + supported_platforms:'' + startupmanager:0 + hasconfig:1 + build:0 \ No newline at end of file diff --git a/lib/installers/k8s/element_chat/README.md b/lib/installers/k8s/element_chat/README.md new file mode 100644 index 00000000..22e403c4 --- /dev/null +++ b/lib/installers/k8s/element_chat/README.md @@ -0,0 +1,120 @@ +# Element Chat Kubernetes Installer + +A Kubernetes installer for Element Chat (Matrix Conduit + Element Web) with TFGrid Gateway integration. + +## Overview + +This installer deploys a complete Matrix chat solution consisting of: +- **Conduit**: A lightweight Matrix homeserver implementation +- **Element Web**: A modern web client for Matrix +- **TFGW (ThreeFold Gateway)**: Provides public FQDNs with TLS termination + +## Quick Start + +```v +import incubaid.herolib.installers.k8s.element_chat + +// Create and install Element Chat with defaults +mut installer := element_chat.get( + name: 'myelementchat' + create: true +)! + +installer.install()! +``` + +## Configuration Options + +All configuration options are optional and have sensible defaults: + +### Hostnames and Namespace + +```v +installer.matrix_hostname = 'matrixchat' // Default: 'matrixchat' +installer.element_hostname = 'elementchat' // Default: 'elementchat' +installer.namespace = 'chat' // Default: 'chat' +``` + +**Note**: Use only alphanumeric characters in hostnames (no underscores or dashes). + +### Conduit (Matrix Homeserver) Configuration + +```v +// Server port +installer.conduit_port = 6167 // Default: 6167 + +// Database configuration +installer.database_backend = 'rocksdb' // Default: 'rocksdb' (options: 'rocksdb', 'sqlite') +installer.database_path = '/var/lib/matrix-conduit' // Default: '/var/lib/matrix-conduit' + +// Federation and registration +installer.allow_registration = true // Default: true (allow new user registration) +installer.allow_federation = true // Default: true (federate with other Matrix servers) + +// Logging +installer.log_level = 'info' // Default: 'info' (options: 'info', 'debug', 'warn', 'error') +``` + +### Element Web Client Configuration + +```v +installer.element_brand = 'Element' // Default: 'Element' (customize the branding name) +``` + +## Full Example + +```v +import incubaid.herolib.installers.k8s.element_chat + +mut installer := element_chat.get( + name: 'myelementchat' + create: true +)! + +// Configure hostnames +installer.matrix_hostname = 'mymatrix' +installer.element_hostname = 'mychat' +installer.namespace = 'chat' + +// Configure Conduit +installer.conduit_port = 6167 +installer.database_backend = 'rocksdb' +installer.allow_registration = false // Disable public registration +installer.allow_federation = true +installer.log_level = 'debug' + +// Configure Element +installer.element_brand = 'My Chat' + +// Install +installer.install()! + +println('Matrix homeserver: https://${installer.matrix_hostname}.gent01.grid.tf') +println('Element web client: https://${installer.element_hostname}.gent01.grid.tf') +``` + +## Management + +### Check Installation Status + +```v +if installer.installed()! { + println('Element Chat is installed') +} else { + println('Element Chat is not installed') +} +``` + +### Destroy Deployment + +```v +installer.destroy()! +``` + +This will delete the entire namespace and all resources within it. + +## See Also + +- [Matrix Conduit Documentation](https://gitlab.com/famedly/conduit) +- [Element Web Documentation](https://github.com/vector-im/element-web) +- [Matrix Protocol](https://matrix.org/) diff --git a/lib/installers/k8s/element_chat/element_chat_actions.v b/lib/installers/k8s/element_chat/element_chat_actions.v new file mode 100644 index 00000000..c2a8526d --- /dev/null +++ b/lib/installers/k8s/element_chat/element_chat_actions.v @@ -0,0 +1,195 @@ +module element_chat + +import incubaid.herolib.osal.core as osal +import incubaid.herolib.ui.console +import incubaid.herolib.installers.ulist +import time + +const max_deployment_retries = 30 +const deployment_check_interval_seconds = 2 + +//////////////////// following actions are not specific to instance of the object + +// checks if a certain version or above is installed +pub fn installed() !bool { + installer := get()! + mut k8s := installer.kube_client + + // Try to get the conduit deployment + deployments := k8s.get_deployments(installer.namespace) or { + // If we can't get deployments, it's not running + return false + } + + // Check if conduit and element-web deployments exist + mut conduit_found := false + mut element_found := false + for deployment in deployments { + if deployment.name == 'conduit' { + conduit_found = true + } + if deployment.name == 'element-web' { + element_found = true + } + } + + return conduit_found && element_found +} + +// 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: 'element_chat' + // source: '${gitpath}/target/x86_64-unknown-linux-musl/release/element_chat' + // )! +} + +fn install() ! { + console.print_header('Installing Element Chat...') + + // Get installer config to access namespace + installer := get()! + mut k8s := installer.kube_client + configure()! + + // 1. Check for dependencies. + console.print_info('Checking for kubectl...') + kubectl_installed()! + console.print_info('kubectl is installed and configured.') + + // 4. Apply the YAML files using kubernetes client + console.print_info('Applying Gateway YAML file to the cluster...') + res1 := k8s.apply_yaml('/tmp/element_chat/tfgw-element.yaml')! + if !res1.success { + return error('Failed to apply tfgw-element.yaml: ${res1.stderr}') + } + console.print_info('Gateway YAML file applied successfully.') + + // 5. Verify TFGW deployments + verify_tfgw_deployment(tfgw_name: 'matrix-gw', namespace: installer.namespace)! + verify_tfgw_deployment(tfgw_name: 'element-gw', namespace: installer.namespace)! + + // 6. Apply Chat App YAML + console.print_info('Applying Chat App YAML file to the cluster...') + res2 := k8s.apply_yaml('/tmp/element_chat/chat-app.yaml')! + if !res2.success { + return error('Failed to apply chat-app.yaml: ${res2.stderr}') + } + console.print_info('Chat App YAML file applied successfully.') + + // 7. Verify deployment status + console.print_info('Verifying deployment status...') + mut is_running := false + for i in 0 .. max_deployment_retries { + if installed()! { + is_running = true + break + } + console.print_info('Waiting for Element Chat deployment to be ready... (${i + 1}/${max_deployment_retries})') + time.sleep(deployment_check_interval_seconds * time.second) + } + + if is_running { + console.print_header('Element Chat installation successful!') + console.print_header('You can access Element Chat at https://${installer.element_hostname}.gent01.grid.tf') + console.print_header('You can access Matrix at https://${installer.matrix_hostname}.gent01.grid.tf') + } else { + return error('Element Chat deployment failed to start.') + } +} + +// params for verifying the generating of the FQDN using tfgw crd +@[params] +struct VerifyTfgwDeployment { +pub mut: + tfgw_name string // tfgw serivce generating the FQDN + namespace string // namespace name for element chat deployments/services +} + +// Function for verifying the generating of of the FQDN using tfgw crd +fn verify_tfgw_deployment(args VerifyTfgwDeployment) ! { + console.print_info('Verifying TFGW deployment for ${args.tfgw_name}...') + installer := get()! + mut k8s := installer.kube_client + mut is_fqdn_generated := false + + for i in 0 .. max_deployment_retries { + // Use kubectl_exec for custom resource (TFGW) with jsonpath + result := k8s.kubectl_exec( + command: 'get tfgw ${args.tfgw_name} -n ${args.namespace} -o jsonpath="{.status.fqdn}"' + ) or { + console.print_info('Waiting for FQDN to be generated for ${args.tfgw_name}... (${i + 1}/${max_deployment_retries})') + time.sleep(deployment_check_interval_seconds * time.second) + continue + } + + if result.success && result.stdout != '' { + is_fqdn_generated = true + break + } + console.print_info('Waiting for FQDN to be generated for ${args.tfgw_name}... (${i + 1}/${max_deployment_retries})') + time.sleep(deployment_check_interval_seconds * time.second) + } + + if !is_fqdn_generated { + console.print_stderr('Failed to get FQDN for ${args.tfgw_name}.') + // Use describe_resource to get detailed information about the TFGW resource + result := k8s.describe_resource( + resource: 'tfgw' + resource_name: args.tfgw_name + namespace: args.namespace + ) or { return error('TFGW deployment failed for ${args.tfgw_name}.') } + console.print_stderr(result.stdout) + return error('TFGW deployment failed for ${args.tfgw_name}.') + } + console.print_info('TFGW deployment for ${args.tfgw_name} verified successfully.') +} + +fn destroy() ! { + console.print_header('Destroying Element Chat...') + installer := get()! + mut k8s := installer.kube_client + + console.print_debug('Attempting to delete namespace: ${installer.namespace}') + + // Delete the namespace using kubernetes client + result := k8s.delete_resource('namespace', installer.namespace, '') or { + console.print_stderr('Failed to delete namespace ${installer.namespace}: ${err}') + return error('Failed to delete namespace ${installer.namespace}: ${err}') + } + + console.print_debug('Delete command completed. Exit code: ${result.exit_code}, Success: ${result.success}') + + if !result.success { + // Namespace not found is OK - it means it's already deleted + if result.stderr.contains('NotFound') { + console.print_info('Namespace ${installer.namespace} does not exist (already deleted).') + } else { + console.print_stderr('Failed to delete namespace ${installer.namespace}: ${result.stderr}') + return error('Failed to delete namespace ${installer.namespace}: ${result.stderr}') + } + } else { + console.print_info('Namespace ${installer.namespace} deleted successfully.') + } +} + +fn kubectl_installed() ! { + // Check if kubectl command exists + if !osal.cmd_exists('kubectl') { + return error('kubectl is not installed. Please install it to continue.') + } + + // Check if kubectl is configured to connect to a cluster + installer := get()! + mut k8s := installer.kube_client + + if !k8s.test_connection()! { + return error('kubectl is not configured to connect to a Kubernetes cluster. Please check your kubeconfig.') + } +} diff --git a/lib/installers/k8s/element_chat/element_chat_factory_.v b/lib/installers/k8s/element_chat/element_chat_factory_.v new file mode 100644 index 00000000..38216d3e --- /dev/null +++ b/lib/installers/k8s/element_chat/element_chat_factory_.v @@ -0,0 +1,188 @@ +module element_chat + +import incubaid.herolib.core.base +import incubaid.herolib.core.playbook { PlayBook } +import incubaid.herolib.ui.console +import json + +__global ( + element_chat_global map[string]&ElementChat + element_chat_default string +) + +/////////FACTORY + +@[params] +pub struct ArgsGet { +pub mut: + name string = 'element_chat' + fromdb bool // will load from filesystem + create bool // default will not create if not exist +} + +pub fn new(args ArgsGet) !&ElementChat { + mut obj := ElementChat{ + name: args.name + } + set(obj)! + return get(name: args.name)! +} + +pub fn get(args_ ArgsGet) !&ElementChat { + mut context := base.context()! + mut args := args_ + + if args.name == 'element_chat' && element_chat_default != '' { + args.name = element_chat_default + } + if args.fromdb || args.name !in element_chat_global { + mut r := context.redis()! + if r.hexists('context:element_chat', args.name)! { + data := r.hget('context:element_chat', args.name)! + if data.len == 0 { + print_backtrace() + return error('ElementChat with name: ${args.name} does not exist, prob bug.') + } + mut obj := json.decode(ElementChat, data)! + set_in_mem(obj)! + } else { + if args.create { + new(args)! + } else { + print_backtrace() + return error("ElementChat with name '${args.name}' does not exist") + } + } + return get(name: args.name)! // no longer from db nor create + } + return element_chat_global[args.name] or { + print_backtrace() + return error('could not get config for element_chat with name:${args.name}') + } +} + +// register the config for the future +pub fn set(o ElementChat) ! { + mut o2 := set_in_mem(o)! + element_chat_default = o2.name + mut context := base.context()! + mut r := context.redis()! + r.hset('context:element_chat', o2.name, json.encode(o2))! +} + +// does the config exists? +pub fn exists(args ArgsGet) !bool { + mut context := base.context()! + mut r := context.redis()! + return r.hexists('context:element_chat', args.name)! +} + +pub fn delete(args ArgsGet) ! { + mut context := base.context()! + mut r := context.redis()! + r.hdel('context:element_chat', args.name)! +} + +@[params] +pub struct ArgsList { +pub mut: + fromdb bool // will load from filesystem +} + +// if fromdb set: load from filesystem, and not from mem, will also reset what is in mem +pub fn list(args ArgsList) ![]&ElementChat { + mut res := []&ElementChat{} + mut context := base.context()! + if args.fromdb { + // reset what is in mem + element_chat_global = map[string]&ElementChat{} + element_chat_default = '' + } + if args.fromdb { + mut r := context.redis()! + mut l := r.hkeys('context:element_chat')! + + for name in l { + res << get(name: name, fromdb: true)! + } + return res + } else { + // load from memory + for _, client in element_chat_global { + res << client + } + } + return res +} + +// only sets in mem, does not set as config +fn set_in_mem(o ElementChat) !ElementChat { + mut o2 := obj_init(o)! + element_chat_global[o2.name] = &o2 + element_chat_default = o2.name + return o2 +} + +pub fn play(mut plbook PlayBook) ! { + if !plbook.exists(filter: 'element_chat.') { + return + } + mut install_actions := plbook.find(filter: 'element_chat.configure')! + if install_actions.len > 0 { + for mut install_action in install_actions { + heroscript := install_action.heroscript() + mut obj2 := heroscript_loads(heroscript)! + set(obj2)! + install_action.done = true + } + } + mut other_actions := plbook.find(filter: 'element_chat.')! + for mut 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 element_chat.destroy') + destroy()! + } + if other_action.name == 'install' { + console.print_debug('install action element_chat.install') + install()! + } + } + other_action.done = true + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS /////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// load from disk and make sure is properly intialized +pub fn (mut self ElementChat) reload() ! { + switch(self.name) + self = obj_init(self)! +} + +@[params] +pub struct InstallArgs { +pub mut: + reset bool +} + +pub fn (mut self ElementChat) install(args InstallArgs) ! { + switch(self.name) + if args.reset || (!installed()!) { + install()! + } +} + +pub fn (mut self ElementChat) destroy() ! { + switch(self.name) + destroy()! +} + +// switch instance to be used for element_chat +pub fn switch(name string) { + element_chat_default = name +} diff --git a/lib/installers/k8s/element_chat/element_chat_model.v b/lib/installers/k8s/element_chat/element_chat_model.v new file mode 100644 index 00000000..8e7c83af --- /dev/null +++ b/lib/installers/k8s/element_chat/element_chat_model.v @@ -0,0 +1,177 @@ +module element_chat + +import incubaid.herolib.ui.console +import incubaid.herolib.data.encoderhero +import incubaid.herolib.virt.kubernetes +import incubaid.herolib.core.pathlib +import strings + +pub const version = '0.0.0' +const singleton = false +const default = false + +struct ConfigValues { +pub mut: + matrix_hostname string // The Matrix homeserver hostname + element_hostname string // The Element web hostname + backends string // The backends for the TFGW + namespace string // The namespace for the Element Chat deployment + conduit_port int // Conduit server port + database_backend string // Database backend (rocksdb, sqlite) + database_path string // Database path + allow_registration bool // Allow user registration + allow_federation bool // Allow federation with other Matrix servers + log_level string // Log level (info, debug, warn, error) + element_brand string // Element branding name + conduit_toml string // Generated conduit.toml content + element_json string // Generated element-config.json content +} + +@[heap] +pub struct ElementChat { +pub mut: + name string = 'element_chat' + matrix_hostname string + element_hostname string + namespace string + // Conduit configuration + conduit_port int = 6167 + database_backend string = 'rocksdb' + database_path string = '/var/lib/matrix-conduit' + allow_registration bool = true + allow_federation bool = true + log_level string = 'info' + // Element configuration + element_brand string = 'Element' + // Internal paths + chat_app_path string = '/tmp/element_chat/chat-app.yaml' + tfgw_path string = '/tmp/element_chat/tfgw-element.yaml' + conduit_cfg_path string = '/tmp/element_chat/conduit.toml' + element_cfg_path string = '/tmp/element_chat/element-config.json' + kube_client kubernetes.KubeClient @[skip] +} + +// your checking & initialization code if needed +fn obj_init(mycfg_ ElementChat) !ElementChat { + mut mycfg := mycfg_ + + if mycfg.namespace == '' { + mycfg.namespace = 'chat' + } + + if mycfg.matrix_hostname == '' { + mycfg.matrix_hostname = 'matrixchat' + } + + if mycfg.element_hostname == '' { + mycfg.element_hostname = 'elementchat' + } + + mycfg.kube_client = kubernetes.get(create: true)! + mycfg.kube_client.config.namespace = mycfg.namespace + return mycfg +} + +// called before start if done +fn configure() ! { + mut installer := get()! + + master_ips := get_master_node_ips()! + console.print_info('Master node IPs: ${master_ips}') + + mut backends_str_builder := strings.new_builder(100) + for ip in master_ips { + backends_str_builder.writeln(' - "http://[${ip}]:80"') + } + + console.print_info('Generating configuration files from templates...') + + // Create config_values for template generation + mut config_values := ConfigValues{ + matrix_hostname: installer.matrix_hostname + element_hostname: installer.element_hostname + backends: backends_str_builder.str() + namespace: installer.namespace + conduit_port: installer.conduit_port + database_backend: installer.database_backend + database_path: installer.database_path + allow_registration: installer.allow_registration + allow_federation: installer.allow_federation + log_level: installer.log_level + element_brand: installer.element_brand + conduit_toml: '' + element_json: '' + } + + // Generate conduit.toml and element-config.json + conduit_toml_raw := $tmpl('./templates/conduit.toml.temp') + element_json_raw := $tmpl('./templates/element-config.json') + + // Indent the configs for proper YAML formatting (4 spaces for ConfigMap data) + conduit_toml_lines := conduit_toml_raw.split('\n') + mut conduit_toml_indented := strings.new_builder(conduit_toml_raw.len + 100) + for line in conduit_toml_lines { + if line.len > 0 { + conduit_toml_indented.writeln(' ${line}') + } + } + + element_json_lines := element_json_raw.split('\n') + mut element_json_indented := strings.new_builder(element_json_raw.len + 100) + for line in element_json_lines { + if line.len > 0 { + element_json_indented.writeln(' ${line}') + } + } + + // Update config_values with the generated and indented configs + config_values.conduit_toml = conduit_toml_indented.str() + config_values.element_json = element_json_indented.str() + + // Ensure the output directory exists + _ := pathlib.get_dir(path: '/tmp/element_chat', create: true)! + + // Generate TFGW YAML + tfgw_yaml := $tmpl('./templates/tfgw.yaml') + mut tfgw_path := pathlib.get_file( + path: installer.tfgw_path + create: true + check: true + )! + tfgw_path.write(tfgw_yaml)! + + // Generate chat-app YAML + chat_app_yaml := $tmpl('./templates/chat-app.yaml') + mut chat_app_path := pathlib.get_file(path: installer.chat_app_path, create: true)! + chat_app_path.write(chat_app_yaml)! + + console.print_info('Configuration files generated successfully.') +} + +// Get Kubernetes master node IPs +fn get_master_node_ips() ![]string { + mut master_ips := []string{} + installer := get()! + + // Get all nodes using the kubernetes client + mut k8s := installer.kube_client + nodes := k8s.get_nodes()! + + // Extract IPv6 internal IPs from all nodes (dual-stack support) + for node in nodes { + // Check all internal IPs (not just the first one) for IPv6 addresses + for ip in node.internal_ips { + if ip.len > 0 && ip.contains(':') { + master_ips << ip + } + } + } + return master_ips +} + +/////////////NORMALLY NO NEED TO TOUCH + +pub fn heroscript_loads(heroscript string) !ElementChat { + mut obj := encoderhero.decode[ElementChat](heroscript)! + return obj +} diff --git a/lib/installers/k8s/element_chat/templates/chat-app.yaml b/lib/installers/k8s/element_chat/templates/chat-app.yaml new file mode 100644 index 00000000..a218e536 --- /dev/null +++ b/lib/installers/k8s/element_chat/templates/chat-app.yaml @@ -0,0 +1,139 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: @{config_values.namespace} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: conduit-config + namespace: @{config_values.namespace} +data: + conduit.toml: | +@{config_values.conduit_toml} + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: conduit + namespace: @{config_values.namespace} +spec: + replicas: 1 + selector: { matchLabels: { app: conduit } } + template: + metadata: { labels: { app: conduit } } + spec: + enableServiceLinks: false + containers: + - name: conduit + image: matrixconduit/matrix-conduit:latest + env: + - name: CONDUIT_CONFIG + value: /etc/conduit/conduit.toml + ports: [{ name: http, containerPort: @{config_values.conduit_port} }] + volumeMounts: + - { name: cfg, mountPath: /etc/conduit, readOnly: true } + - { name: data, mountPath: /var/lib/matrix-conduit } + readinessProbe: + httpGet: { path: /_matrix/client/versions, port: @{config_values.conduit_port} } + initialDelaySeconds: 5 + periodSeconds: 5 + volumes: + - name: cfg + configMap: { name: conduit-config } + - name: data + emptyDir: {} + +--- +apiVersion: v1 +kind: Service +metadata: + name: matrix-hs + namespace: @{config_values.namespace} +spec: + selector: { app: conduit } + ports: + - { name: http, port: @{config_values.conduit_port}, targetPort: @{config_values.conduit_port} } + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: element-config + namespace: @{config_values.namespace} +data: + config.json: | +@{config_values.element_json} + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: element-web + namespace: @{config_values.namespace} +spec: + replicas: 1 + selector: { matchLabels: { app: element-web } } + template: + metadata: { labels: { app: element-web } } + spec: + enableServiceLinks: false + containers: + - name: element + image: vectorim/element-web:latest + env: + - { name: PORT, value: "80" } + ports: [{ name: http, containerPort: 80 }] + volumeMounts: + - name: element-config + mountPath: /app/config.json + subPath: config.json + readOnly: true + volumes: + - name: element-config + configMap: { name: element-config } + +--- +apiVersion: v1 +kind: Service +metadata: + name: element-web + namespace: @{config_values.namespace} +spec: + selector: { app: element-web } + ports: + - { name: http, port: 80, targetPort: 80 } + +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: matrix + namespace: @{config_values.namespace} +spec: + ingressClassName: traefik + rules: + - host: @{config_values.matrix_hostname}.gent01.grid.tf + http: + paths: + - path: / + pathType: Prefix + backend: { service: { name: matrix-hs, port: { number: @{config_values.conduit_port} } } } + +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: element + namespace: @{config_values.namespace} +spec: + ingressClassName: traefik + rules: + - host: @{config_values.element_hostname}.gent01.grid.tf + http: + paths: + - path: / + pathType: Prefix + backend: { service: { name: element-web, port: { number: 80 } } } + diff --git a/lib/installers/k8s/element_chat/templates/conduit.toml.temp b/lib/installers/k8s/element_chat/templates/conduit.toml.temp new file mode 100644 index 00000000..2d0091a8 --- /dev/null +++ b/lib/installers/k8s/element_chat/templates/conduit.toml.temp @@ -0,0 +1,9 @@ +[global] +allow_registration = @{config_values.allow_registration} +allow_federation = @{config_values.allow_federation} +port = @{config_values.conduit_port} +server_name = "@{config_values.matrix_hostname}.gent01.grid.tf" +address = "0.0.0.0" +database_backend = "@{config_values.database_backend}" +database_path = "@{config_values.database_path}" +log = "@{config_values.log_level}" diff --git a/lib/installers/k8s/element_chat/templates/element-config.json b/lib/installers/k8s/element_chat/templates/element-config.json new file mode 100644 index 00000000..6dc29c71 --- /dev/null +++ b/lib/installers/k8s/element_chat/templates/element-config.json @@ -0,0 +1,10 @@ +{ + "disable_custom_urls": true, + "default_server_config": { + "m.homeserver": { + "base_url": "https://@{config_values.matrix_hostname}.gent01.grid.tf", + "server_name": "@{config_values.matrix_hostname}.gent01.grid.tf" + } + }, + "brand": "@{config_values.element_brand}" +} \ No newline at end of file diff --git a/lib/installers/k8s/element_chat/templates/tfgw.yaml b/lib/installers/k8s/element_chat/templates/tfgw.yaml new file mode 100644 index 00000000..dd15255b --- /dev/null +++ b/lib/installers/k8s/element_chat/templates/tfgw.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: @{config_values.namespace} +--- +apiVersion: ingress.grid.tf/v1 +kind: TFGW +metadata: + name: matrix-gw + namespace: @{config_values.namespace} +spec: + hostname: "@{config_values.matrix_hostname}" + backends: +@{config_values.backends} +--- +apiVersion: ingress.grid.tf/v1 +kind: TFGW +metadata: + name: element-gw + namespace: @{config_values.namespace} +spec: + hostname: "@{config_values.element_hostname}" + backends: +@{config_values.backends}