mirror of
https://github.com/truenas/charts.git
synced 2026-04-14 10:40:31 +08:00
121 lines
4.0 KiB
Python
Executable File
121 lines
4.0 KiB
Python
Executable File
#!/usr/bin/python3
|
|
import json
|
|
import os
|
|
import sys
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
from middlewared.client import Client
|
|
from middlewared.service import ValidationErrors, CallError
|
|
|
|
|
|
def path_in_locked_datasets(path: str) -> bool:
|
|
with Client() as c:
|
|
return c.call('pool.dataset.path_in_locked_datasets', path)
|
|
|
|
|
|
def get_configured_user_group(path: str) -> dict:
|
|
with Client() as c:
|
|
return c.call('filesystem.stat', path)
|
|
|
|
|
|
def get_host_path_attachments(path: str) -> set:
|
|
with Client() as c:
|
|
return {
|
|
attachment['type']
|
|
for attachment in c.call('pool.dataset.attachments_with_path', path)
|
|
if attachment['type'].lower() not in ['kubernetes', 'chart releases']
|
|
}
|
|
|
|
|
|
def get_kubernetes_config() -> dict:
|
|
with Client() as c:
|
|
return c.call('kubernetes.config')
|
|
|
|
|
|
def validate_host_path(path: str, schema_name: str, verrors: ValidationErrors) -> None:
|
|
"""
|
|
These validations are taken from `FilesystemService._common_perm_path_validate`.
|
|
Including an additional validation that makes sure all the children under
|
|
a path are on same device.
|
|
"""
|
|
schema_name += ".migration.chown"
|
|
p = Path(path)
|
|
if not p.is_absolute():
|
|
verrors.add(schema_name, f"Must be an absolute path: {path}")
|
|
|
|
if p.is_file():
|
|
verrors.add(schema_name, f"Recursive operations on a file are invalid: {path}")
|
|
|
|
if not p.absolute().as_posix().startswith("/mnt/"):
|
|
verrors.add(
|
|
schema_name,
|
|
f"Changes to permissions on paths that are not beneath the directory /mnt are not permitted: {path}"
|
|
)
|
|
elif len(p.resolve().parents) == 2:
|
|
verrors.add(schema_name, f"The specified path is a ZFS pool mountpoint: {path}")
|
|
|
|
# Make sure that dataset is not locked
|
|
if path_in_locked_datasets(path):
|
|
verrors.add(schema_name, f"Dataset is locked at path: {path}.")
|
|
|
|
# Validate attachments
|
|
if attachments := get_host_path_attachments(path):
|
|
verrors.add(schema_name, f"The path '{path}' is already attached to service(s): {', '.join(attachments)}.")
|
|
|
|
# Make sure all the minio's data directory children are on same device.
|
|
device_id = os.stat(path).st_dev
|
|
for root, dirs, files in os.walk(path):
|
|
for child in dirs + files:
|
|
abs_path = os.path.join(root, child)
|
|
if os.stat(abs_path).st_dev != device_id:
|
|
verrors.add(
|
|
schema_name,
|
|
(f"All the children of MinIO data directory should be on "
|
|
f"same device as root: path={abs_path} device={os.stat(abs_path).st_dev}")
|
|
)
|
|
break
|
|
|
|
|
|
def migrate(values: dict) -> dict:
|
|
# minio user / group ID
|
|
uid = gid = 473
|
|
verrors = ValidationErrors()
|
|
k8s_config = get_kubernetes_config()
|
|
if values["appVolumeMounts"]["export"]["hostPathEnabled"]:
|
|
host_path = values["appVolumeMounts"]["export"]["hostPath"]
|
|
else:
|
|
app_dataset = values["appVolumeMounts"]["export"]["datasetName"]
|
|
host_path = os.path.join(
|
|
"/mnt", k8s_config['dataset'], "releases", values["release_name"], "volumes/ix_volumes", app_dataset
|
|
)
|
|
|
|
current_config = get_configured_user_group(host_path)
|
|
if current_config["uid"] == uid and current_config["gid"] == gid:
|
|
return values
|
|
|
|
validate_host_path(host_path, values['release_name'], verrors)
|
|
verrors.check()
|
|
# chown the host path
|
|
acltool = subprocess.run([
|
|
"/usr/bin/nfs4xdr_winacl",
|
|
"-a", "chown",
|
|
"-O", str(uid), "-G", str(gid),
|
|
"-r",
|
|
"-c", host_path,
|
|
"-p", host_path], check=False, capture_output=True
|
|
)
|
|
if acltool.returncode != 0:
|
|
raise CallError(f"acltool [chown] on path {host_path} failed with error: [{acltool.stderr.decode().strip()}]")
|
|
|
|
return 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(json.dumps(migrate(json.loads(f.read()))))
|