diff --git a/examples/installers/k8s/cryptpad.vsh b/examples/installers/k8s/cryptpad.vsh index 95649b25..b081bf0d 100755 --- a/examples/installers/k8s/cryptpad.vsh +++ b/examples/installers/k8s/cryptpad.vsh @@ -6,14 +6,18 @@ import incubaid.herolib.installers.k8s.cryptpad // 1. Create a new installer instance with a specific hostname. // Replace 'mycryptpad' with your desired hostname. -mut installer := cryptpad.new(hostname: 'omda')! +mut installer := cryptpad.get( + name: 'kristof' + create: true +)! // 2. Install CryptPad. // This will generate the necessary Kubernetes YAML files and apply them to your cluster. -installer.install()! +// installer.install()! +// cryptpad.delete()! -println('CryptPad installation started.') -println('You can access it at https://${installer.hostname}.gent01.grid.tf') +// println('CryptPad installation started.') +// println('You can access it at https://${installer.hostname}.gent01.grid.tf') // 3. To destroy the deployment, you can run the following: -// installer.destroy()! +installer.destroy()! diff --git a/lib/apps/biz/cryptpad/.heroscript b/lib/apps/biz/cryptpad/.heroscript deleted file mode 100644 index b3269b96..00000000 --- a/lib/apps/biz/cryptpad/.heroscript +++ /dev/null @@ -1,12 +0,0 @@ - -!!hero_code.generate_installer - name:'' - classname:'CryptPad' - singleton:0 - templates:1 - default:1 - title:'' - supported_platforms:'' - startupmanager:1 - hasconfig:1 - build:1 \ No newline at end of file diff --git a/lib/apps/biz/cryptpad/cryptpad_actions.v b/lib/apps/biz/cryptpad/cryptpad_actions.v deleted file mode 100644 index ed3bb12d..00000000 --- a/lib/apps/biz/cryptpad/cryptpad_actions.v +++ /dev/null @@ -1,187 +0,0 @@ -module cryptpad - -import incubaid.herolib.osal.core as osal -import incubaid.herolib.ui.console -import incubaid.herolib.core.texttools -import incubaid.herolib.core.pathlib -import incubaid.herolib.osal.systemd -import incubaid.herolib.osal.startupmanager -import incubaid.herolib.installers.ulist -import incubaid.herolib.installers.lang.golang -import incubaid.herolib.installers.lang.rust -import incubaid.herolib.installers.lang.python -import os - -fn startupcmd() ![]startupmanager.ZProcessNewArgs { - mut installer := get()! - mut res := []startupmanager.ZProcessNewArgs{} - // THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED - // res << startupmanager.ZProcessNewArgs{ - // name: 'cryptpad' - // cmd: 'cryptpad server' - // env: { - // 'HOME': '/root' - // } - // } - - return res -} - -fn running() !bool { - mut installer := get()! - // THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED - // this checks health of cryptpad - // 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: 'cryptpad', 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('cryptpad is answering.') - return false -} - -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 { - // THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED - // res := os.execute('${osal.profile_path_source_and()!} cryptpad 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 cryptpad version.\n${res.output}") - // } - // if texttools.version(version) == texttools.version(r[0]) { - // 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: 'cryptpad' - // source: '${gitpath}/target/x86_64-unknown-linux-musl/release/cryptpad' - // )! -} - -fn install() ! { - console.print_header('install cryptpad') - // THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED - // mut url := '' - // if core.is_linux_arm()! { - // url = 'https://github.com/cryptpad-dev/cryptpad/releases/download/v${version}/cryptpad_${version}_linux_arm64.tar.gz' - // } else if core.is_linux_intel()! { - // url = 'https://github.com/cryptpad-dev/cryptpad/releases/download/v${version}/cryptpad_${version}_linux_amd64.tar.gz' - // } else if core.is_osx_arm()! { - // url = 'https://github.com/cryptpad-dev/cryptpad/releases/download/v${version}/cryptpad_${version}_darwin_arm64.tar.gz' - // } else if osal.is_osx_intel()! { - // url = 'https://github.com/cryptpad-dev/cryptpad/releases/download/v${version}/cryptpad_${version}_darwin_amd64.tar.gz' - // } else { - // return error('unsported platform') - // } - - // mut dest := osal.download( - // url: url - // minsize_kb: 9000 - // expand_dir: '/tmp/cryptpad' - // )! - - // //dest.moveup_single_subdir()! - - // mut binpath := dest.file_get('cryptpad')! - // osal.cmd_add( - // cmdname: 'cryptpad' - // source: binpath.path - // )! -} - -fn build() ! { - // url := 'https://github.com/threefoldtech/cryptpad' - - // make sure we install base on the node - // if core.platform() != .ubuntu { - // return error('only support ubuntu for now') - // } - // golang.install()! - - // console.print_header('build cryptpad') - - // gitpath := gittools.get_repo(coderoot: '/tmp/builder', url: url, reset: true, pull: true)! - - // 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 - // )! -} - -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 - // ")! -} diff --git a/lib/apps/biz/cryptpad/cryptpad_factory_.v b/lib/apps/biz/cryptpad/cryptpad_factory_.v deleted file mode 100644 index 231041de..00000000 --- a/lib/apps/biz/cryptpad/cryptpad_factory_.v +++ /dev/null @@ -1,312 +0,0 @@ -module cryptpad - -import incubaid.herolib.core.base -import incubaid.herolib.core.playbook { PlayBook } -import incubaid.herolib.ui.console -import json -import incubaid.herolib.osal.startupmanager -import time - -__global ( - cryptpad_global map[string]&CryptPad - cryptpad_default string -) - -/////////FACTORY - -@[params] -pub struct ArgsGet { -pub mut: - name string = 'default' - fromdb bool // will load from filesystem - create bool // default will not create if not exist -} - -pub fn new(args ArgsGet) !&CryptPad { - mut obj := CryptPad{ - name: args.name - } - set(obj)! - return get(name: args.name)! -} - -pub fn get(args ArgsGet) !&CryptPad { - mut context := base.context()! - cryptpad_default = args.name - if args.fromdb || args.name !in cryptpad_global { - mut r := context.redis()! - if r.hexists('context:cryptpad', args.name)! { - data := r.hget('context:cryptpad', args.name)! - if data.len == 0 { - print_backtrace() - return error('CryptPad with name: ${args.name} does not exist, prob bug.') - } - mut obj := json.decode(CryptPad, data)! - set_in_mem(obj)! - } else { - if args.create { - new(args)! - } else { - print_backtrace() - return error("CryptPad with name '${args.name}' does not exist") - } - } - return get(name: args.name)! // no longer from db nor create - } - return cryptpad_global[args.name] or { - print_backtrace() - return error('could not get config for cryptpad with name:${args.name}') - } -} - -// register the config for the future -pub fn set(o CryptPad) ! { - mut o2 := set_in_mem(o)! - cryptpad_default = o2.name - mut context := base.context()! - mut r := context.redis()! - r.hset('context:cryptpad', 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:cryptpad', args.name)! -} - -pub fn delete(args ArgsGet) ! { - mut context := base.context()! - mut r := context.redis()! - r.hdel('context:cryptpad', 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) ![]&CryptPad { - mut res := []&CryptPad{} - mut context := base.context()! - if args.fromdb { - // reset what is in mem - cryptpad_global = map[string]&CryptPad{} - cryptpad_default = '' - } - if args.fromdb { - mut r := context.redis()! - mut l := r.hkeys('context:cryptpad')! - - for name in l { - res << get(name: name, fromdb: true)! - } - return res - } else { - // load from memory - for _, client in cryptpad_global { - res << client - } - } - return res -} - -// only sets in mem, does not set as config -fn set_in_mem(o CryptPad) !CryptPad { - mut o2 := obj_init(o)! - cryptpad_global[o2.name] = &o2 - cryptpad_default = o2.name - return o2 -} - -pub fn play(mut plbook PlayBook) ! { - if !plbook.exists(filter: 'cryptpad.') { - return - } - mut install_actions := plbook.find(filter: 'cryptpad.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: 'cryptpad.')! - 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 cryptpad.destroy') - destroy()! - } - if other_action.name == 'install' { - console.print_debug('install action cryptpad.install') - install()! - } - } - if other_action.name in ['start', 'stop', 'restart'] { - mut p := other_action.params - name := p.get('name')! - mut cryptpad_obj := get(name: name)! - console.print_debug('action object:\n${cryptpad_obj}') - if other_action.name == 'start' { - console.print_debug('install action cryptpad.${other_action.name}') - cryptpad_obj.start()! - } - - if other_action.name == 'stop' { - console.print_debug('install action cryptpad.${other_action.name}') - cryptpad_obj.stop()! - } - if other_action.name == 'restart' { - console.print_debug('install action cryptpad.${other_action.name}') - cryptpad_obj.restart()! - } - } - other_action.done = true - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS /////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// - -fn startupmanager_get(cat startupmanager.StartupManagerType) !startupmanager.StartupManager { - // unknown - // screen - // zinit - // tmux - // systemd - match cat { - .screen { - console.print_debug("installer: cryptpad' startupmanager get screen") - return startupmanager.get(.screen)! - } - .zinit { - console.print_debug("installer: cryptpad' startupmanager get zinit") - return startupmanager.get(.zinit)! - } - .systemd { - console.print_debug("installer: cryptpad' startupmanager get systemd") - return startupmanager.get(.systemd)! - } - else { - console.print_debug("installer: cryptpad' startupmanager get auto") - return startupmanager.get(.auto)! - } - } -} - -// load from disk and make sure is properly intialized -pub fn (mut self CryptPad) reload() ! { - switch(self.name) - self = obj_init(self)! -} - -pub fn (mut self CryptPad) start() ! { - switch(self.name) - if self.running()! { - return - } - - console.print_header('installer: cryptpad start') - - if !installed()! { - install()! - } - - configure()! - - start_pre()! - - for zprocess in startupcmd()! { - mut sm := startupmanager_get(zprocess.startuptype)! - - console.print_debug('installer: cryptpad starting 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('cryptpad did not install properly.') -} - -pub fn (mut self CryptPad) install_start(args InstallArgs) ! { - switch(self.name) - self.install(args)! - self.start()! -} - -pub fn (mut self CryptPad) 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 CryptPad) restart() ! { - switch(self.name) - self.stop()! - self.start()! -} - -pub fn (mut self CryptPad) running() !bool { - switch(self.name) - - // walk over the generic processes, if not running return - for zprocess in startupcmd()! { - if zprocess.startuptype != .screen { - 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 CryptPad) install(args InstallArgs) ! { - switch(self.name) - if args.reset || (!installed()!) { - install()! - } -} - -pub fn (mut self CryptPad) build() ! { - switch(self.name) - build()! -} - -pub fn (mut self CryptPad) destroy() ! { - switch(self.name) - self.stop() or {} - destroy()! -} - -// switch instance to be used for cryptpad -pub fn switch(name string) { - cryptpad_default = name -} diff --git a/lib/apps/biz/cryptpad/cryptpad_model.v b/lib/apps/biz/cryptpad/cryptpad_model.v deleted file mode 100644 index abd50e3e..00000000 --- a/lib/apps/biz/cryptpad/cryptpad_model.v +++ /dev/null @@ -1,52 +0,0 @@ -module cryptpad - -import incubaid.herolib.data.paramsparser -import incubaid.herolib.data.encoderhero -import os - -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 -@[heap] -pub struct CryptPad { -pub mut: - name string = 'default' - domain string - domain_sandbox string - configpath string //can be left empty, will be set to default path (is kubernetes config file) - cryptpad_configpath string //can be left empty, will be set to default path - masters []string //list of master servers for kubernetes - domainname string //can be 1 name e.g. mycryptpad or it can be a fully qualified domain name e.g. mycryptpad.mycompany.com -} - -// your checking & initialization code if needed -fn obj_init(mycfg_ CryptPad) !CryptPad { - mut mycfg := mycfg_ - if mycfg.domain == '' { - return error('CryptPad client "${mycfg.name}" missing domain') - } - if mycfg.configpath == '' { - mycfg.configpath = '${os.home_dir()}/.apps/cryptpad/${mycfg.name}/config.yaml' - } - //call kubernetes client to get master nodes and put them in - mycfg.masters = []string{} //TODO get from kubernetes - return mycfg -} - -// called before start if done -fn configure() ! { - mut installer := get()! - mut mycode := $tmpl('templates/main.yaml') - mut path := pathlib.get_file(path: cfg.configpath, create: true)! - path.write(mycode)! - console.print_debug(mycode) -} - -/////////////NORMALLY NO NEED TO TOUCH - -pub fn heroscript_loads(heroscript string) !CryptPad { - mut obj := encoderhero.decode[CryptPad](heroscript)! - return obj -} diff --git a/lib/apps/biz/cryptpad/readme.md b/lib/apps/biz/cryptpad/readme.md deleted file mode 100644 index 3e9a4862..00000000 --- a/lib/apps/biz/cryptpad/readme.md +++ /dev/null @@ -1,44 +0,0 @@ -# cryptpad - - - -To get started - -```v - - -import incubaid.herolib.installers.something.cryptpad as cryptpad_installer - -heroscript:=" -!!cryptpad.configure name:'test' - password: '1234' - port: 7701 - -!!cryptpad.start name:'test' reset:1 -" - -cryptpad_installer.play(heroscript=heroscript)! - -//or we can call the default and do a start with reset -//mut installer:= cryptpad_installer.get()! -//installer.start(reset:true)! - - - - -``` - -## example heroscript - - -```hero -!!cryptpad.configure - homedir: '/home/user/cryptpad' - username: 'admin' - password: 'secretpassword' - title: 'Some Title' - host: 'localhost' - port: 8888 - -``` - diff --git a/lib/apps/biz/cryptpad/templates/cryptpad.yaml b/lib/apps/biz/cryptpad/templates/cryptpad.yaml deleted file mode 100644 index 1160378f..00000000 --- a/lib/apps/biz/cryptpad/templates/cryptpad.yaml +++ /dev/null @@ -1,127 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: collab ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: cryptpad-config - namespace: collab -data: - config.js: | - module.exports = { - httpUnsafeOrigin: 'https://cryptpadp2.gent01.grid.tf', - httpSafeOrigin: 'https://cryptpads2.gent01.grid.tf', - httpAddress: '0.0.0.0', - httpPort: 3000, - - websocketPort: 3003, - websocketPath: '/cryptpad_websocket', - - blockPath: './block', - blobPath: './blob', - dataPath: './data', - filePath: './datastore', - logToStdout: true, - logLevel: 'info', - }; ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: cryptpad - namespace: collab -spec: - replicas: 1 - selector: - matchLabels: { app: cryptpad } - template: - metadata: - labels: { app: cryptpad } - spec: - initContainers: - - name: fix-perms - image: busybox:1.36 - command: ["/bin/sh","-lc"] - args: - - > - chown -R 4001:4001 - /cryptpad/blob /cryptpad/block /cryptpad/data /cryptpad/datastore /cryptpad/customize || true - volumeMounts: - - { name: blob, mountPath: /cryptpad/blob } - - { name: block, mountPath: /cryptpad/block } - - { name: data, mountPath: /cryptpad/data } - - { name: files, mountPath: /cryptpad/datastore } - - { name: customize, mountPath: /cryptpad/customize } - containers: - - name: cryptpad - image: cryptpad/cryptpad:latest - ports: - - { name: http, containerPort: 3000 } - - { name: ws, containerPort: 3003 } - env: - - { name: CPAD_CONF, value: "/cryptpad/config/config.js" } - - { name: CPAD_MAIN_DOMAIN, value: "https://cryptpadp2.gent01.grid.tf" } - - { name: CPAD_SANDBOX_DOMAIN, value: "https://cryptpads2.gent01.grid.tf" } - - { name: CPAD_INSTALL_ONLYOFFICE, value: "no" } - readinessProbe: - httpGet: { path: /, port: 3000 } - initialDelaySeconds: 20 - periodSeconds: 10 - volumeMounts: - - { name: cfg, mountPath: /cryptpad/config/config.js, subPath: config.js, readOnly: true } - - { name: blob, mountPath: /cryptpad/blob } - - { name: block, mountPath: /cryptpad/block } - - { name: data, mountPath: /cryptpad/data } - - { name: files, mountPath: /cryptpad/datastore } - - { name: customize, mountPath: /cryptpad/customize } - volumes: - - name: cfg - configMap: - name: cryptpad-config - items: [{ key: config.js, path: config.js }] - - { name: blob, emptyDir: {} } - - { name: block, emptyDir: {} } - - { name: data, emptyDir: {} } - - { name: files, emptyDir: {} } - - { name: customize, emptyDir: {} } ---- -apiVersion: v1 -kind: Service -metadata: - name: cryptpad - namespace: collab -spec: - selector: { app: cryptpad } - ports: - - { name: http, port: 3000, targetPort: 3000 } - - { name: ws, port: 3003, targetPort: 3003 } ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: cryptpad - namespace: collab - annotations: - kubernetes.io/ingress.class: traefik -spec: - rules: - - host: cryptpadp2.gent01.grid.tf - http: - paths: - - path: / - pathType: Prefix - backend: { service: { name: cryptpad, port: { number: 3000 } } } - - path: /cryptpad_websocket - pathType: Prefix - backend: { service: { name: cryptpad, port: { number: 3003 } } } - - host: cryptpads2.gent01.grid.tf - http: - paths: - - path: / - pathType: Prefix - backend: { service: { name: cryptpad, port: { number: 3000 } } } - - path: /cryptpad_websocket - pathType: Prefix - backend: { service: { name: cryptpad, port: { number: 3003 } } } diff --git a/lib/apps/biz/cryptpad/templates/tfgw-cryptpad.yaml b/lib/apps/biz/cryptpad/templates/tfgw-cryptpad.yaml deleted file mode 100644 index 9ed46f6f..00000000 --- a/lib/apps/biz/cryptpad/templates/tfgw-cryptpad.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: collab ---- -apiVersion: ingress.grid.tf/v1 -kind: TFGW -metadata: - name: cryptpad-main - namespace: collab -spec: - hostname: "cryptpadp2" - backends: - - "http://[49a:bfc3:59bf:872c:ff0f:d25f:751c:5106]:80" - - "http://[53c:609e:6488:9ddd:ff0f:2ffc:ac2a:94a6]:80" - - "http://[58e:ca1d:ce3d:355c:ff0f:a065:a40a:a08c]:80" ---- -apiVersion: ingress.grid.tf/v1 -kind: TFGW -metadata: - name: cryptpad-sandbox - namespace: collab -spec: - hostname: "cryptpads2" - backends: - - "http://[49a:bfc3:59bf:872c:ff0f:d25f:751c:5106]:80" - - "http://[53c:609e:6488:9ddd:ff0f:2ffc:ac2a:94a6]:80" - - "http://[58e:ca1d:ce3d:355c:ff0f:a065:a40a:a08c]:80" diff --git a/lib/installers/k8s/cryptpad/.heroscript b/lib/installers/k8s/cryptpad/.heroscript index 2e74ad46..5956e3a6 100644 --- a/lib/installers/k8s/cryptpad/.heroscript +++ b/lib/installers/k8s/cryptpad/.heroscript @@ -3,7 +3,7 @@ classname:'CryptpadServer' singleton:0 //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 + default:0 //can we create a default when the factory is used title:'' supported_platforms:'' //osx, ... (empty means all) reset:0 // regenerate all, dangerous !!! diff --git a/lib/installers/k8s/cryptpad/README.md b/lib/installers/k8s/cryptpad/README.md new file mode 100644 index 00000000..e90a1baa --- /dev/null +++ b/lib/installers/k8s/cryptpad/README.md @@ -0,0 +1,72 @@ +# CryptPad Kubernetes Installer + +A Kubernetes installer for CryptPad with TFGrid Gateway integration. + +## Quick Start + +```v +import incubaid.herolib.installers.k8s.cryptpad + +// Create and install CryptPad +mut installer := cryptpad.get( + name: 'mycryptpad' + create: true +)! + +installer.install()! +``` + +to change the hostname and the namespace, you can override the default values: + +```v +mut installer := cryptpad.get( + name: 'mycryptpad' + create: true +)! + +installer.hostname = 'customhostname' +installer.namespace = 'customnamespace' +installer.install()! +``` + +## Usage + +### Create an Instance + +```v +mut installer := cryptpad.get( + name: 'mycryptpad' // Unique name for this instance + create: true // Create if doesn't exist +)! +``` + +The instance name will be used as: + +- Kubernetes namespace name +- Hostname prefix (e.g., `mycryptpad.gent01.grid.tf`) + +### Install + +```v +installer.install()! +``` + +This will: + +1. Generate Kubernetes YAML files for CryptPad and TFGrid Gateway +2. Apply them to your k3s cluster +3. Wait for deployment to be ready + +### Destroy + +```v +installer.destroy()! +``` + +Removes all CryptPad resources from the cluster. + +## Requirements + +- kubectl installed and configured +- k3s cluster running +- Redis server running (for configuration storage) diff --git a/lib/installers/k8s/cryptpad/cryptpad_actions.v b/lib/installers/k8s/cryptpad/cryptpad_actions.v index 81171a26..605123b8 100644 --- a/lib/installers/k8s/cryptpad/cryptpad_actions.v +++ b/lib/installers/k8s/cryptpad/cryptpad_actions.v @@ -2,38 +2,18 @@ module cryptpad import incubaid.herolib.osal.core as osal import incubaid.herolib.ui.console -import incubaid.herolib.osal.startupmanager import incubaid.herolib.installers.ulist -import incubaid.herolib.virt.kubernetes -import os -import strings import time const max_deployment_retries = 30 const deployment_check_interval_seconds = 2 -fn startupcmd() ![]startupmanager.ZProcessNewArgs { - // We don't have a long-running process to manage with startupmanager for this installer, - // but we'll keep the function for consistency. - return []startupmanager.ZProcessNewArgs{} -} +//////////////////// following actions are not specific to instance of the object -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 - mut k8s := kubernetes.get()! - if !k8s.test_connection()! { - return error('kubectl is not configured to connect to a Kubernetes cluster. Please check your kubeconfig.') - } -} - -fn running() !bool { +// checks if a certain version or above is installed +fn installed() !bool { installer := get()! - mut k8s := kubernetes.get()! + mut k8s := installer.kube_client // Try to get the cryptpad deployment deployments := k8s.get_deployments(installer.namespace) or { @@ -51,59 +31,18 @@ fn running() !bool { return false } -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 { - return running() -} - // 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() ! { - // Not needed for this installer. -} - -fn get_master_node_ips() ![]string { - mut master_ips := []string{} - mut k8s := kubernetes.get()! - - // Get all nodes using the kubernetes 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 -} - -struct ConfigValues { -pub mut: - hostname string - backends string - namespace string + // installers.upload( + // cmdname: 'cryptpad' + // source: '${gitpath}/target/x86_64-unknown-linux-musl/release/cryptpad' + // )! } fn install() ! { @@ -111,46 +50,14 @@ fn install() ! { // Get installer config to access namespace installer := get()! - if installer.hostname == '' { - return error('hostname is empty') - } - - // Configure kubernetes client with the correct namespace - mut k8s := kubernetes.get()! - k8s.config.namespace = installer.namespace + 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.') - // 2. Get Kubernetes master node IPs. - console.print_info('Getting Kubernetes master node IPs...') - master_ips := get_master_node_ips()! - console.print_info('Master node IPs: ${master_ips}') - - // 3. Generate YAML files from templates. - console.print_info('Generating YAML files from templates...') - - mut backends_str_builder := strings.new_builder(100) - for ip in master_ips { - backends_str_builder.writeln(' - "http://[${ip}]:80"') - } - config_values := ConfigValues{ - hostname: installer.hostname - backends: backends_str_builder.str() - namespace: installer.namespace - } - - // Write to tfgw file - temp := $tmpl('./templates/tfgw-cryptpad.yaml') - os.write_file('/tmp/tfgw-cryptpad.yaml', temp)! - - // write to cryptpad yaml file - temp2 := $tmpl('./templates/cryptpad.yaml') - os.write_file('/tmp/cryptpad.yaml', temp2)! - console.print_info('YAML files generated successfully.') - // 4. Apply the YAML files using kubernetes client console.print_info('Applying Gateway YAML file to the cluster...') res1 := k8s.apply_yaml('/tmp/tfgw-cryptpad.yaml')! @@ -175,7 +82,7 @@ fn install() ! { console.print_info('Verifying deployment status...') mut is_running := false for i in 0 .. max_deployment_retries { - if running()! { + if installed()! { is_running = true break } @@ -199,10 +106,11 @@ pub mut: retry int = 30 } -// Function for verifying the generating of of the FQDN using tfgw crd +// 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}...') - mut k8s := kubernetes.get()! + installer := get()! + mut k8s := installer.kube_client mut is_fqdn_generated := false for i in 0 .. args.retry { @@ -240,17 +148,42 @@ fn verify_tfgw_deployment(args VerifyTfgwDeployment) ! { fn destroy() ! { console.print_header('Destroying CryptPad...') installer := get()! - mut k8s := kubernetes.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 + 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 { - console.print_stderr('Failed to delete namespace ${installer.namespace}: ${result.stderr}') + // 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.') + 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/cryptpad/cryptpad_factory_.v b/lib/installers/k8s/cryptpad/cryptpad_factory_.v index 36f35747..e448cb1e 100644 --- a/lib/installers/k8s/cryptpad/cryptpad_factory_.v +++ b/lib/installers/k8s/cryptpad/cryptpad_factory_.v @@ -4,8 +4,6 @@ import incubaid.herolib.core.base import incubaid.herolib.core.playbook { PlayBook } import incubaid.herolib.ui.console import json -import incubaid.herolib.osal.startupmanager -import time __global ( cryptpad_global map[string]&CryptpadServer @@ -17,26 +15,25 @@ __global ( @[params] pub struct ArgsGet { pub mut: - name string = 'default' + name string = 'cryptpad' fromdb bool // will load from filesystem create bool // default will not create if not exist - hostname string - namespace string } pub fn new(args ArgsGet) !&CryptpadServer { mut obj := CryptpadServer{ name: args.name - hostname: args.hostname - namespace: args.namespace } set(obj)! return get(name: args.name)! } -pub fn get(args ArgsGet) !&CryptpadServer { +pub fn get(args_ ArgsGet) !&CryptpadServer { + mut args := args_ mut context := base.context()! - cryptpad_default = args.name + if args.name == 'cryptpad' && cryptpad_default != '' { + args.name = cryptpad_default + } if args.fromdb || args.name !in cryptpad_global { mut r := context.redis()! if r.hexists('context:cryptpad', args.name)! { @@ -55,12 +52,17 @@ pub fn get(args ArgsGet) !&CryptpadServer { return error("CryptpadServer with name '${args.name}' does not exist") } } - return get(name: args.name)! // no longer from db nor create + return get( + name: args.name + fromdb: args.fromdb + create: args.create + )! // no longer from db nor create } - return cryptpad_global[args.name] or { + result := cryptpad_global[args.name] or { print_backtrace() return error('could not get config for cryptpad with name:${args.name}') } + return result } // register the config for the future @@ -69,7 +71,8 @@ pub fn set(o CryptpadServer) ! { cryptpad_default = o2.name mut context := base.context()! mut r := context.redis()! - r.hset('context:cryptpad', o2.name, json.encode(o2))! + encoded := json.encode(o2) + r.hset('context:cryptpad', o2.name, encoded)! } // does the config exists? @@ -152,25 +155,6 @@ pub fn play(mut plbook PlayBook) ! { install()! } } - if other_action.name in ['start', 'stop', 'restart'] { - mut p := other_action.params - name := p.get('name')! - mut cryptpad_obj := get(name: name)! - console.print_debug('action object:\n${cryptpad_obj}') - if other_action.name == 'start' { - console.print_debug('install action cryptpad.${other_action.name}') - cryptpad_obj.start()! - } - - if other_action.name == 'stop' { - console.print_debug('install action cryptpad.${other_action.name}') - cryptpad_obj.stop()! - } - if other_action.name == 'restart' { - console.print_debug('install action cryptpad.${other_action.name}') - cryptpad_obj.restart()! - } - } other_action.done = true } } @@ -179,118 +163,18 @@ pub fn play(mut plbook PlayBook) ! { //////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS /////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// -fn startupmanager_get(cat startupmanager.StartupManagerType) !startupmanager.StartupManager { - // unknown - // screen - // zinit - // tmux - // systemd - match cat { - .screen { - console.print_debug("installer: cryptpad' startupmanager get screen") - return startupmanager.get(.screen)! - } - .zinit { - console.print_debug("installer: cryptpad' startupmanager get zinit") - return startupmanager.get(.zinit)! - } - .systemd { - console.print_debug("installer: cryptpad' startupmanager get systemd") - return startupmanager.get(.systemd)! - } - else { - console.print_debug("installer: cryptpad' startupmanager get auto") - return startupmanager.get(.auto)! - } - } -} - // load from disk and make sure is properly intialized pub fn (mut self CryptpadServer) reload() ! { + switch(self.name) self = obj_init(self)! } -pub fn (mut self CryptpadServer) start() ! { - if self.running()! { - return - } - - console.print_header('installer: cryptpad start') - - if !installed()! { - install()! - } - - configure()! - - start_pre()! - - for zprocess in startupcmd()! { - mut sm := startupmanager_get(zprocess.startuptype)! - - console.print_debug('installer: cryptpad starting 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('cryptpad did not install properly.') -} - @[params] pub struct InstallArgs { pub mut: reset bool } -pub fn (mut self CryptpadServer) install_start(args InstallArgs) ! { - switch(self.name) - self.install(args)! - self.start()! -} - -pub fn (mut self CryptpadServer) 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 CryptpadServer) restart() ! { - switch(self.name) - self.stop()! - self.start()! -} - -pub fn (mut self CryptpadServer) running() !bool { - switch(self.name) - - // walk over the generic processes, if not running return - for zprocess in startupcmd()! { - if zprocess.startuptype != .screen { - mut sm := startupmanager_get(zprocess.startuptype)! - r := sm.running(zprocess.name)! - if r == false { - return false - } - } - } - return running()! -} - - pub fn (mut self CryptpadServer) install(args InstallArgs) ! { switch(self.name) if args.reset || (!installed()!) { @@ -300,10 +184,10 @@ pub fn (mut self CryptpadServer) install(args InstallArgs) ! { pub fn (mut self CryptpadServer) destroy() ! { switch(self.name) - self.stop() or {} destroy()! } // switch instance to be used for cryptpad pub fn switch(name string) { + cryptpad_default = name } diff --git a/lib/installers/k8s/cryptpad/cryptpad_model.v b/lib/installers/k8s/cryptpad/cryptpad_model.v index 6ea8176d..e612b2c3 100644 --- a/lib/installers/k8s/cryptpad/cryptpad_model.v +++ b/lib/installers/k8s/cryptpad/cryptpad_model.v @@ -1,46 +1,103 @@ module cryptpad -import incubaid.herolib.data.encoderhero 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 = '1.0.0' -const singleton = true -const default = true +pub const version = '0.0.0' +const singleton = false +const default = false + +struct ConfigValues { +pub mut: + hostname string // The CryptPad hostname + backends string // The backends for the TFGW + namespace string // The namespace for the CryptPad deployment +} -// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED @[heap] pub struct CryptpadServer { pub mut: - name string = 'default' - hostname string - namespace string = 'collab' + name string = 'cryptpad' + hostname string + namespace string + cryptpad_path string = '/tmp/cryptpad.yaml' + tfgw_cryptpad_path string = '/tmp/tfgw-cryptpad.yaml' + kube_client kubernetes.KubeClient @[skip] } // your checking & initialization code if needed fn obj_init(mycfg_ CryptpadServer) !CryptpadServer { mut mycfg := mycfg_ - if mycfg.hostname == '' { - return error('hostname cannot be empty') - } + if mycfg.namespace == '' { - mycfg.namespace = 'collab' + mycfg.namespace = mycfg.name } + + if mycfg.hostname == '' { + mycfg.hostname = mycfg.name + } + + mycfg.kube_client = kubernetes.get(create: true)! + mycfg.kube_client.config.namespace = mycfg.namespace return mycfg } // called before start if done fn configure() ! { - // We will implement the configuration logic here, - // like generating the yaml files from templates. - console.print_debug('configuring cryptpad...') + 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"') + } + + config_values := ConfigValues{ + hostname: installer.hostname + backends: backends_str_builder.str() + namespace: installer.namespace + } + + console.print_info('Generating YAML files from templates...') + temp := $tmpl('./templates/tfgw-cryptpad.yaml') + mut temp_path := pathlib.get_file(path: installer.tfgw_cryptpad_path, create: true)! + temp_path.write(temp)! + + temp2 := $tmpl('./templates/cryptpad.yaml') + mut temp_path2 := pathlib.get_file(path: installer.cryptpad_path, create: true)! + temp_path2.write(temp2)! + + console.print_info('YAML 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_dumps(obj CryptpadServer) !string { - return encoderhero.encode[CryptpadServer](obj)! -} - pub fn heroscript_loads(heroscript string) !CryptpadServer { mut obj := encoderhero.decode[CryptpadServer](heroscript)! return obj diff --git a/lib/virt/kubernetes/kubernetes_client.v b/lib/virt/kubernetes/kubernetes_client.v index 25dc890d..1aabcd5a 100644 --- a/lib/virt/kubernetes/kubernetes_client.v +++ b/lib/virt/kubernetes/kubernetes_client.v @@ -3,6 +3,7 @@ module kubernetes import incubaid.herolib.osal.core as osal import incubaid.herolib.ui.console import json +import os @[params] pub struct KubectlExecArgs { @@ -46,55 +47,13 @@ pub fn (mut k KubeClient) kubectl_exec(args KubectlExecArgs) !KubectlResult { } cmd += ' ${args.command}' + result := os.execute(cmd) - console.print_debug('executing: ${cmd}') - - // Check if this is a command that might produce large output - is_large_output := args.command.contains('get nodes') || args.command.contains('get pods') - || args.command.contains('get deployments') || args.command.contains('get services') - - if is_large_output { - // Use exec_fast for large outputs (avoids 8KB buffer limit in osal.exec) - // exec_fast uses os.execute which doesn't have the pipe buffer limitation - result_output := osal.exec_fast( - cmd: cmd - ignore_error: true - ) or { return error('Failed to execute kubectl command: ${err}') } - - // Check if command succeeded by looking for error messages - if result_output.contains('Error from server') || result_output.contains('error:') - || result_output.contains('Unable to connect') { - return KubectlResult{ - exit_code: 1 - stdout: result_output - stderr: result_output - success: false - } - } - - return KubectlResult{ - exit_code: 0 - stdout: result_output - stderr: '' - success: result_output.len > 0 - } - } else { - // Use regular exec for normal commands (supports timeout and proper error handling) - // Note: stdout must be true to prevent process from hanging when output buffer fills - job := osal.exec( - cmd: cmd - timeout: args.timeout - retry: args.retry - raise_error: false - stdout: true - )! - - return KubectlResult{ - exit_code: job.exit_code - stdout: job.output - stderr: job.error - success: job.exit_code == 0 - } + return KubectlResult{ + exit_code: result.exit_code + stdout: result.output + stderr: result.output // os.execute combines stdout and stderr + success: result.exit_code == 0 } } @@ -401,7 +360,8 @@ pub fn (mut k KubeClient) apply_yaml(yaml_path string) !KubectlResult { // Delete resource pub fn (mut k KubeClient) delete_resource(kind string, name string, namespace string) !KubectlResult { - result := k.kubectl_exec(command: 'delete ${kind} ${name} -n ${namespace}')! + mut cmd := 'delete ${kind} ${name}' + result := k.kubectl_exec(command: cmd)! return result }