Skip to content

Commit

Permalink
update lib
Browse files Browse the repository at this point in the history
  • Loading branch information
stavros-k committed Jul 19, 2024
1 parent 94adce0 commit 6ff472b
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 16 deletions.
2 changes: 1 addition & 1 deletion ix-dev/community/clamav/app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ keywords:
- anti-virus
- clamav
lib_version: 1.0.0
lib_version_hash: f96fef83d5ec9d220fb8215e56da7125ff4a27a8ee7e08a44cf09e89f7fc9e49
lib_version_hash: 9c07d26150ab40c0f19ae3991ee02b338aecf50e6c3619414d114276d08e1c1f
maintainers:
- email: [email protected]
name: truenas
Expand Down
Empty file.
27 changes: 27 additions & 0 deletions ix-dev/community/clamav/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
48 changes: 48 additions & 0 deletions ix-dev/community/clamav/migrations/migration_helpers/memory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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)
result = result / 1024 / 1024
return f"{int(result)}M"
20 changes: 20 additions & 0 deletions ix-dev/community/clamav/migrations/migration_helpers/resources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from .memory import transform_memory, TOTAL_MEM
from .cpu import transform_cpu, CPU_COUNT


def migrate_resources(resources):
# Handle empty resources, with sane defaults
if not resources:
return {
"limits": {
"cpus": CPU_COUNT / 2,
"memory": f"{TOTAL_MEM / 1024 / 1024}M",
}
}

return {
"limits": {
"cpus": transform_cpu(resources.get("limits", {}).get("cpu", "")),
"memory": transform_memory(resources.get("limits", {}).get("memory", "")),
}
}
114 changes: 114 additions & 0 deletions ix-dev/community/clamav/migrations/migration_helpers/storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
def migrate_storage_item(storage_item):
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})

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": "anonymous"}


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({"host_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"],
}
21 changes: 6 additions & 15 deletions ix-dev/community/clamav/templates/library/base_v1_0_0/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,14 @@
PROPAGATION_TYPES = ["shared", "slave", "private", "rshared", "rslave", "rprivate"]


# Basic validation for a path (Expand later)
def valid_path(path=""):
if not path.startswith("/"):
utils.throw_error(f"Expected path [{path}] to start with /")

# There is no reason to allow / as a path, either on host or in a container
if path == "/":
utils.throw_error(f"Expected path [{path}] to not be /")

return path


# Returns a volume mount object (Used in container's "volumes" level)
def vol_mount(data, ix_volumes=None):
ix_volumes = ix_volumes or []
vol_type = _get_docker_vol_type(data)

volume = {
"type": vol_type,
"target": valid_path(data.get("mount_path", "")),
"target": utils.valid_path(data.get("mount_path", "")),
"read_only": data.get("read_only", False),
}
if vol_type == "bind": # Default create_host_path is true in short-syntax
Expand Down Expand Up @@ -194,7 +182,7 @@ def host_path(data, ix_volumes=None):
f"Expected [host_path()] to be called only for types [host_path, ix_volume], got [{data['type']}]"
)

return valid_path(path)
return utils.valid_path(path)


# Returns the type of storage as used in docker-compose
Expand Down Expand Up @@ -267,13 +255,16 @@ def _process_cifs(data):
f"user={data['cifs_config']['username']}",
f"password={data['cifs_config']['password']}",
]
if data["cifs_config"].get("domain"):
opts.append(f'domain={data["cifs_config"]["domain"]}')

if data["cifs_config"].get("options"):
if not isinstance(data["cifs_config"]["options"], list):
utils.throw_error(
"Expected [cifs_config.options] to be a list for [cifs] type"
)

disallowed_opts = ["user", "password"]
disallowed_opts = ["user", "password", "domain"]
for opt in data["cifs_config"]["options"]:
if not isinstance(opt, str):
utils.throw_error(
Expand Down
12 changes: 12 additions & 0 deletions ix-dev/community/clamav/templates/library/base_v1_0_0/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ def merge_dicts(*dicts):
return merged_dict


# Basic validation for a path (Expand later)
def valid_path(path=""):
if not path.startswith("/"):
throw_error(f"Expected path [{path}] to start with /")

# There is no reason to allow / as a path, either on host or in a container
if path == "/":
throw_error(f"Expected path [{path}] to not be /")

return path


def camel_case(string):
return string.title()

Expand Down

0 comments on commit 6ff472b

Please sign in to comment.