Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add collabora #45

Merged
merged 31 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cspell.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ words:
- htpasswd
- ipaddr
- dockge
- collabora
- healtcheck
- isready
- lidarr
- jellyfin
Expand Down
3 changes: 3 additions & 0 deletions ix-dev/stable/collabora/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Collabora

[Collabora](https://www.collaboraoffice.com/) is a collaborative online office suite based on LibreOffice technology
56 changes: 56 additions & 0 deletions ix-dev/stable/collabora/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
app_version: 24.04.5.1.1
capabilities:
- description: Collabora and Nginx are able to chown files.
name: CHOWN
- description: Collabora is able to bypass permission checks for it's sub-processes.
name: FOWNER
- description: Collabora is able to bypass permission checks for it's sub-processes.
name: DAC_OVERRIDE
- description: NGINX is able to bypass permission checks.
name: SETGID
- description: NGINX is able to bypass permission checks.
name: SETUID
- description: Collabora is able to use chroot.
name: SYS_CHROOT
- description: Collabora is able to create device nodes.
name: MKNOD
- description: Collabora is able to set file capabilities.
name: SETFCAP
categories:
- productivity
description: Collabora is a collaborative online office suite based on LibreOffice
technology
home: https://www.collaboraoffice.com/
host_mounts: []
icon: https://media.sys.truenas.net/apps/collabora/icons/icon.png
keywords:
- office
- documents
- productivity
lib_version: 1.0.0
lib_version_hash: 66c98111180da566a3bcc9ee1d1be4f673356f453b5d97ee7c784c9a38ee9999
maintainers:
- email: [email protected]
name: truenas
url: https://www.truenas.com/
name: collabora
run_as_context:
- description: Collabora runs as non-root user.
gid: 101
group_name: cool
uid: 100
user_name: cool
- description: Nginx runs as root user.
gid: 0
group_name: root
uid: 0
user_name: root
screenshots:
- https://media.sys.truenas.net/apps/collabora/screenshots/screenshot1.png
sources:
- https://www.collaboraoffice.com/
- https://github.com/CollaboraOnline/online
- https://hub.docker.com/r/collabora/code
title: Collabora
train: stable
version: 1.0.0
9 changes: 9 additions & 0 deletions ix-dev/stable/collabora/item.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
categories:
- productivity
icon_url: https://media.sys.truenas.net/apps/collabora/icons/icon.png
screenshots:
- https://media.sys.truenas.net/apps/collabora/screenshots/screenshot1.png
tags:
- office
- documents
- productivity
13 changes: 13 additions & 0 deletions ix-dev/stable/collabora/ix_values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
images:
image:
repository: collabora/code
tag: 24.04.5.1.1
nginx_image:
repository: nginx
tag: 1.23.3

consts:
collabora_container_name: collabora
nginx_container_name: nginx
nginx_ssl_cert_path: /etc/certs/server.crt
nginx_ssl_key_path: /etc/certs/server.key
Empty file.
27 changes: 27 additions & 0 deletions ix-dev/stable/collabora/migrations/migration_helpers/cpu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import math
import re
import os

CPU_COUNT = os.cpu_count()

NUMBER_REGEX = re.compile(r"^[1-9][0-9]$")
FLOAT_REGEX = re.compile(r"^[0-9]+\.[0-9]+$")
MILI_CPU_REGEX = re.compile(r"^[0-9]+m$")


def transform_cpu(cpu) -> int:
result = 2
if NUMBER_REGEX.match(cpu):
result = int(cpu)
elif FLOAT_REGEX.match(cpu):
result = int(math.ceil(float(cpu)))
elif MILI_CPU_REGEX.match(cpu):
num = int(cpu[:-1])
num = num / 1000
result = int(math.ceil(num))

if CPU_COUNT is not None:
# Do not exceed the actual CPU count
result = min(result, CPU_COUNT)

return result
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
def migrate_dns_config(dns_config):
if not dns_config:
return []

dns_opts = []
for opt in dns_config.get("options", []):
dns_opts.append(f"{opt['name']}:{opt['value']}")

return dns_opts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
def get_value_from_secret(secrets={}, secret_name="", key=""):
if not secrets or not secret_name or not key:
raise ValueError("Expected [secrets], [secret_name] and [key] to be set")
for secret in secrets.items():
curr_secret_name = secret[0]
curr_data = secret[1]

if curr_secret_name.endswith(secret_name):
if not curr_data.get(key, None):
raise ValueError(
f"Expected [{key}] to be set in secret [{curr_secret_name}]"
)
return curr_data[key]

raise ValueError(f"Secret [{secret_name}] not found")
49 changes: 49 additions & 0 deletions ix-dev/stable/collabora/migrations/migration_helpers/memory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import re
import math
import psutil

TOTAL_MEM = psutil.virtual_memory().total

SINGLE_SUFFIX_REGEX = re.compile(r"^[1-9][0-9]*([EPTGMK])$")
DOUBLE_SUFFIX_REGEX = re.compile(r"^[1-9][0-9]*([EPTGMK])i$")
BYTES_INTEGER_REGEX = re.compile(r"^[1-9][0-9]*$")
EXPONENT_REGEX = re.compile(r"^[1-9][0-9]*e[0-9]+$")

SUFFIX_MULTIPLIERS = {
"K": 10**3,
"M": 10**6,
"G": 10**9,
"T": 10**12,
"P": 10**15,
"E": 10**18,
}

DOUBLE_SUFFIX_MULTIPLIERS = {
"Ki": 2**10,
"Mi": 2**20,
"Gi": 2**30,
"Ti": 2**40,
"Pi": 2**50,
"Ei": 2**60,
}


def transform_memory(memory):
result = 4096 # Default to 4GB

if re.match(SINGLE_SUFFIX_REGEX, memory):
suffix = memory[-1]
result = int(memory[:-1]) * SUFFIX_MULTIPLIERS[suffix]
elif re.match(DOUBLE_SUFFIX_REGEX, memory):
suffix = memory[-2:]
result = int(memory[:-2]) * DOUBLE_SUFFIX_MULTIPLIERS[suffix]
elif re.match(BYTES_INTEGER_REGEX, memory):
result = int(memory)
elif re.match(EXPONENT_REGEX, memory):
result = int(float(memory))

result = math.ceil(result)
result = min(result, TOTAL_MEM)
# Convert to Megabytes
result = result / 1024 / 1024
return int(result)
59 changes: 59 additions & 0 deletions ix-dev/stable/collabora/migrations/migration_helpers/resources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from .memory import transform_memory, TOTAL_MEM
from .cpu import transform_cpu, CPU_COUNT


def migrate_resources(resources, gpus=None, system_gpus=None):
gpus = gpus or {}
system_gpus = system_gpus or []

result = {
"limits": {
"cpus": (CPU_COUNT or 2) / 2,
"memory": {TOTAL_MEM / 1024 / 1024},
}
}

if resources.get("limits", {}).get("cpu", ""):
result["limits"].update(
{"cpus": transform_cpu(resources.get("limits", {}).get("cpu", ""))}
)
if resources.get("limits", {}).get("memory", ""):
result["limits"].update(
{"memory": transform_memory(resources.get("limits", {}).get("memory", ""))}
)

gpus_result = {}
for gpu in gpus.items() if gpus else []:
kind = gpu[0].lower() # Kind of gpu (amd, nvidia, intel)
count = gpu[1] # Number of gpus user requested

if count == 0:
continue

if "amd" in kind or "intel" in kind:
gpus_result.update({"use_all_gpus": True})
elif "nvidia" in kind:
sys_gpus = [
gpu_item
for gpu_item in system_gpus
if gpu_item.get("error") is None
and gpu_item.get("vendor", None) is not None
and gpu_item.get("vendor", "").upper() == "NVIDIA"
]
for sys_gpu in sys_gpus:
if count == 0: # We passed # of gpus that user previously requested
break
guid = sys_gpu.get("vendor_specific_config", {}).get("uuid", "")
pci_slot = sys_gpu.get("pci_slot", "")
if not guid or not pci_slot:
continue

gpus_result.update(
{"nvidia_gpu_selection": {pci_slot: {"uuid": guid, "use_gpu": True}}}
)
count -= 1

if gpus_result:
result.update({"gpus": gpus_result})

return result
115 changes: 115 additions & 0 deletions ix-dev/stable/collabora/migrations/migration_helpers/storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
def migrate_storage_item(storage_item, include_read_only=False):
if not storage_item:
raise ValueError("Expected [storage_item] to be set")

result = {}
if storage_item["type"] == "ixVolume":
result = migrate_ix_volume_type(storage_item)
elif storage_item["type"] == "hostPath":
result = migrate_host_path_type(storage_item)
elif storage_item["type"] == "emptyDir":
result = migrate_empty_dir_type(storage_item)
elif storage_item["type"] == "smb-pv-pvc":
result = migrate_smb_pv_pvc_type(storage_item)

mount_path = storage_item.get("mountPath", "")
if mount_path:
result.update({"mount_path": mount_path})

if include_read_only:
result.update({"read_only": storage_item.get("readOnly", False)})
return result


def migrate_smb_pv_pvc_type(smb_pv_pvc):
smb_config = smb_pv_pvc.get("smbConfig", {})
if not smb_config:
raise ValueError("Expected [smb_pv_pvc] to have [smbConfig] set")

return {
"type": "cifs",
"cifs_config": {
"server": smb_config["server"],
"share": smb_config["share"],
"domain": smb_config.get("domain", ""),
"username": smb_config["username"],
"password": smb_config["password"],
},
}


def migrate_empty_dir_type(empty_dir):
empty_dir_config = empty_dir.get("emptyDirConfig", {})
if not empty_dir_config:
raise ValueError("Expected [empty_dir] to have [emptyDirConfig] set")

if empty_dir_config.get("medium", "") == "Memory":
# Convert Gi to Mi
size = empty_dir_config.get("size", 0.5) * 1024
return {
"type": "tmpfs",
"tmpfs_config": {"size": size},
}

return {"type": "temporary"}


def migrate_ix_volume_type(ix_volume):
vol_config = ix_volume.get("ixVolumeConfig", {})
if not vol_config:
raise ValueError("Expected [ix_volume] to have [ixVolumeConfig] set")

result = {
"type": "ix_volume",
"ix_volume_config": {
"acl_enable": vol_config.get("aclEnable", False),
"dataset_name": vol_config.get("datasetName", ""),
},
}

if vol_config.get("aclEnable", False):
result["ix_volume_config"].update(
{"acl_entries": migrate_acl_entries(vol_config["aclEntries"])}
)

return result


def migrate_host_path_type(host_path):
path_config = host_path.get("hostPathConfig", {})
if not path_config:
raise ValueError("Expected [host_path] to have [hostPathConfig] set")

result = {
"type": "host_path",
"host_path_config": {
"acl_enable": path_config.get("aclEnable", False),
},
}

if path_config.get("aclEnable", False):
result["host_path_config"].update(
{"acl": migrate_acl_entries(path_config.get("acl", {}))}
)
else:
result["host_path_config"].update({"path": path_config["hostPath"]})

return result


def migrate_acl_entries(acl_entries: dict) -> dict:
entries = []
for entry in acl_entries.get("entries", []):
entries.append(
{
"access": entry["access"],
"id": entry["id"],
"id_type": entry["id_type"],
}
)

return {
"entries": entries,
"options": {"force": acl_entries.get("force", False)},
"path": acl_entries["path"],
}
Loading
Loading