Skip to content

Add komodo app #1524

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

Draft
wants to merge 17 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions ix-dev/community/komodo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Komodo

[Komodo](https://komo.do/) - A tool to build and deploy software on many servers
47 changes: 47 additions & 0 deletions ix-dev/community/komodo/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
app_version: 1.16.12
capabilities:
- description: Immich Proxy is able to chown files.
name: CHOWN
- description: Immich Proxy and Web is able to set user ID for it's sub-processes.
name: SETUID
- description: Immich Proxy and Web is able to set group ID for it's sub-processes.
name: SETGID
categories:
- management
description: Komodo is a tool to build and deploy software on many servers
home: https://komo.do
host_mounts: []
icon: https://media.sys.truenas.net/apps/immich/icons/icon.svg
keywords:
- docker
- compose
- orchestration
lib_version: 2.1.14
lib_version_hash: 982057eeec3024ccecbeaa70e9ee59d948523a3b29d9fca6b39f127a42caa1cc
maintainers:
- email: [email protected]
name: truenas
url: https://www.truenas.com/
name: komodo
run_as_context:
- description: Immich runs as root user.
gid: 0
group_name: root
uid: 0
user_name: root
- description: Postgres runs as non-root user.
gid: 999
group_name: postgres
uid: 999
user_name: postgres
- description: Redis runs as a non-root user and root group.
gid: 0
group_name: root
uid: 1001
user_name: redis
screenshots: []
sources:
- https://komo.do/docs/setup/postgres
title: IKomodo
train: community
version: 1.7.25
8 changes: 8 additions & 0 deletions ix-dev/community/komodo/item.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
categories:
- management
icon_url: https://media.sys.truenas.net/apps/immich/icons/icon.svg
screenshots: []
tags:
- docker
- compose
- orchestration
25 changes: 25 additions & 0 deletions ix-dev/community/komodo/ix_values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
images:
core_image:
repository: ghcr.io/mbecker20/komodo
tag: 1.16.12
periphery_image:
repository: ghcr.io/mbecker20/periphery
tag: 1.16.12
ferretdb_image:
repository: ghcr.io/ferretdb/ferretdb
tag: 1.24.0

consts:
core_container_name: komodo
periphery_container_name: komodo_periphery
ferretdb_container_name: komodo_ferretdb
perms_container_name: permissions
db_user: komodo
db_name: komodo
core_repo_cache_path: /repo-cache
periphery_ssl_path: /etc/komodo/ssl
periphery_repos_path: /etc/komodo/repos
periphery_stacks_path: /etc/komodo/stacks
ferretdb_sqlite_path: /state
ferretdb_gid: 1000
ferretdb_uid: 1000
71 changes: 71 additions & 0 deletions ix-dev/community/komodo/migrations/migrate_from_kubernetes
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/python3

import os
import sys
import yaml

from migration_helpers.resources import migrate_resources
from migration_helpers.storage import migrate_storage_item
from migration_helpers.kubernetes_secrets import get_value_from_secret

ml_image_mapping = {
"mlImage": "ml_image",
"mlCudaImage": "ml_cuda_image",
"mlOpenvinoImage": "ml_openvino_image",
}


def migrate(values):
config = values.get("helm_secret", {}).get("config", {})
k8s_secrets = values.get("release_secrets", {})
if not config:
raise ValueError("No config found in values")

new_values = {
"immich": {
"additional_envs": config["immichConfig"].get("additionalEnvs", []),
"enable_ml": config["immichConfig"].get("enableML", False),
"log_level": config["immichConfig"].get("logLevel", "log"),
"hugging_face_endpoint": config["immichConfig"].get(
"huggingFaceEndpoint", ""
),
"ml_image_selector": ml_image_mapping.get(
config["immichConfig"]["mlImageType"], "ml_image"
),
"db_password": get_value_from_secret(
k8s_secrets, "postgres-creds", "POSTGRES_PASSWORD"
),
"redis_password": get_value_from_secret(
k8s_secrets, "redis-creds", "REDIS_PASSWORD"
),
},
"network": {
"web_port": config["immichNetwork"].get("webuiPort", 32400),
},
"storage": {
"library": migrate_storage_item(config["immichStorage"]["library"]),
"uploads": migrate_storage_item(config["immichStorage"]["uploads"]),
"thumbs": migrate_storage_item(config["immichStorage"]["thumbs"]),
"profile": migrate_storage_item(config["immichStorage"]["profile"]),
"video": migrate_storage_item(config["immichStorage"]["video"]),
"postgres_data": migrate_storage_item(config["immichStorage"]["pgData"]),
"additional_storage": [
migrate_storage_item(item, include_read_only=True)
for item in config["immichStorage"]["additionalStorages"]
],
},
"resources": migrate_resources(
config["resources"], config["immichGPU"], values.get("gpu_choices", {})
),
}

return new_values


if __name__ == "__main__":
if len(sys.argv) != 2:
exit(1)

if os.path.exists(sys.argv[1]):
with open(sys.argv[1], "r") as f:
print(yaml.dump(migrate(yaml.safe_load(f.read()))))
Empty file.
30 changes: 30 additions & 0 deletions ix-dev/community/komodo/migrations/migration_helpers/cpu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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)

if int(result) == 0:
result = CPU_COUNT if CPU_COUNT else 2

return int(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,16 @@
def get_value_from_secret(secrets=None, secret_name=None, key=None):
secrets = secrets if secrets else dict()
secret_name = secret_name if secret_name else ""
key = key if key else ""

if not secrets or not secret_name or not key:
raise ValueError("Expected [secrets], [secret_name] and [key] to be set")
for curr_secret_name, curr_data in secrets.items():
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")
61 changes: 61 additions & 0 deletions ix-dev/community/komodo/migrations/migration_helpers/memory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import re
import math


def get_total_memory():
with open("/proc/meminfo") as f:
for line in filter(lambda x: "MemTotal" in x, f):
return int(line.split()[1]) * 1024

return 0


TOTAL_MEM = get_total_memory()

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

if int(result) == 0:
result = TOTAL_MEM if TOTAL_MEM else 4096

return int(result)
59 changes: 59 additions & 0 deletions ix-dev/community/komodo/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": int((CPU_COUNT or 2) / 2),
"memory": int(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
Loading