import logging
import subprocess
from pathlib import Path
from yr.cli.component.base import ComponentLauncher
logger = logging.getLogger(__name__)
class ETCDLauncher(ComponentLauncher):
def prestart_hook(self) -> None:
logger.info(f"{self.name}: prestart hook executing")
data_dir = self.resolver.rendered_config[self.component_config.name]["args"]["data-dir"]
Path(data_dir).mkdir(parents=True, exist_ok=True)
def health_check(self) -> bool:
etcdctl_path = self.resolver.rendered_config["etcd"]["health_check"]["etcdctl_path"]
endpoint = self.resolver.rendered_config["etcd"]["health_check"]["endpoint"]
if etcdctl_path == "":
logger.error("etcd: etcdctl_path is not set for health check.")
return False
try:
args = [str(etcdctl_path)]
if (
self.resolver.rendered_config["etcd"]["health_check"]["auth_type"] == "TLS"
and self.resolver.rendered_config["etcd"]["health_check"]["client_cert_file"] != ""
and self.resolver.rendered_config["etcd"]["health_check"]["client_key_file"] != ""
and self.resolver.rendered_config["etcd"]["args"]["trusted-ca-file"] != ""
):
args.extend(
[
"--cacert",
self.resolver.rendered_config["etcd"]["args"]["trusted-ca-file"],
"--cert",
self.resolver.rendered_config["etcd"]["health_check"]["client_cert_file"],
"--key",
self.resolver.rendered_config["etcd"]["health_check"]["client_key_file"],
],
)
args.extend(["--endpoints", endpoint, "endpoint", "health"])
logger.info(f"{self.name}: health check command: {' '.join(args)}")
result = subprocess.run(
args,
check=False,
capture_output=True,
timeout=2,
)
if result.returncode != 0:
logger.warning(
f"{self.name}: health check failed: {result.stderr.decode().strip()}",
)
return False
except subprocess.TimeoutExpired:
logger.warning(f"{self.name}: health check timed out")
return False
except Exception:
logger.exception("Exception during etcd health check")
return False
else:
return True
def poststart_hook(self) -> None:
logger.info(f"{self.name}: poststart hook executing")
endpoint = self.resolver.rendered_config["etcd"]["args"]["listen-client-urls"]
if self.resolver.rendered_config["etcd"]["health_check"]["auth_type"] == "Noauth":
etcdctl_path = self.resolver.rendered_config["etcd"]["health_check"]["etcdctl_path"]
if etcdctl_path == "":
logger.error("etcd: etcdctl_path is not set for disabling auth.")
return
args = [str(etcdctl_path), "--endpoints", endpoint, "auth", "disable"]
logger.info(f"etcd: disabling etcd auth with command: {' '.join(args)}")
try:
result = subprocess.run(
args,
check=False,
capture_output=True,
timeout=5,
)
if result.returncode != 0:
logger.warning(
f"Failed to disable etcd auth: {result.stderr.decode().strip()}",
)
else:
logger.info("etcd: auth disabled successfully.")
except Exception:
logger.exception("etcd: exception while disabling etcd auth")