Compare commits

..
Author SHA1 Message Date
prathamesh ed4ed48375 Add mutex lock file to registry CLI wrapper class 2024-10-28 11:00:44 +05:30
4 changed files with 40 additions and 115 deletions
@@ -54,7 +54,6 @@ def process_app_deployment_request(
deployment_record_namespace, deployment_record_namespace,
dns_record_namespace, dns_record_namespace,
default_dns_suffix, default_dns_suffix,
dns_value,
deployment_parent_dir, deployment_parent_dir,
kube_config, kube_config,
image_registry, image_registry,
@@ -172,7 +171,6 @@ def process_app_deployment_request(
logger.log( logger.log(
f"Creating webapp deployment in: {deployment_dir} with container id: {deployment_container_tag}" f"Creating webapp deployment in: {deployment_dir} with container id: {deployment_container_tag}"
) )
# CREATES DEPLOYMENT DIR, NOT SKIPPING FOR TESTING
deploy_webapp.create_deployment( deploy_webapp.create_deployment(
ctx, ctx,
deployment_dir, deployment_dir,
@@ -215,24 +213,21 @@ def process_app_deployment_request(
# add_tags_to_image(image_registry, app_image_shared_tag, deployment_container_tag) # add_tags_to_image(image_registry, app_image_shared_tag, deployment_container_tag)
logger.log("Tag complete") logger.log("Tag complete")
else: else:
# SKIP BUILD extra_build_args = [] # TODO: pull from request
logger.log("TESTING: Skipping container build.") logger.log(f"Building container image: {deployment_container_tag}")
build_container_image(
# extra_build_args = [] # TODO: pull from request app, deployment_container_tag, extra_build_args, logger
# logger.log(f"Building container image: {deployment_container_tag}") )
# build_container_image( logger.log("Build complete")
# app, deployment_container_tag, extra_build_args, logger logger.log(f"Pushing container image: {deployment_container_tag}")
# ) push_container_image(deployment_dir, logger)
# logger.log("Build complete") logger.log("Push complete")
# logger.log(f"Pushing container image: {deployment_container_tag}") # The build/push commands above will use the unique deployment tag, so now we need to add the shared tag.
# push_container_image(deployment_dir, logger) logger.log(
# logger.log("Push complete") f"(SKIPPED) Adding global app image tag: {app_image_shared_tag} to newly built image: {deployment_container_tag}"
# # The build/push commands above will use the unique deployment tag, so now we need to add the shared tag. )
# logger.log( # add_tags_to_image(image_registry, deployment_container_tag, app_image_shared_tag)
# f"(SKIPPED) Adding global app image tag: {app_image_shared_tag} to newly built image: {deployment_container_tag}" logger.log("Tag complete")
# )
# # add_tags_to_image(image_registry, deployment_container_tag, app_image_shared_tag)
# logger.log("Tag complete")
else: else:
logger.log("Requested app is already deployed, skipping build and image push") logger.log("Requested app is already deployed, skipping build and image push")
@@ -245,9 +240,7 @@ def process_app_deployment_request(
# 8. update k8s deployment # 8. update k8s deployment
if needs_k8s_deploy: if needs_k8s_deploy:
# SKIP DEPLOY deploy_to_k8s(deployment_record, deployment_dir, recreate_on_deploy, logger)
logger.log("TESTING: Skipping deployment to k8s.")
# deploy_to_k8s(deployment_record, deployment_dir, recreate_on_deploy, logger)
logger.log("Publishing deployment to registry.") logger.log("Publishing deployment to registry.")
publish_deployment( publish_deployment(
@@ -258,7 +251,6 @@ def process_app_deployment_request(
dns_record, dns_record,
dns_lrn, dns_lrn,
deployment_dir, deployment_dir,
dns_value,
app_deployment_request, app_deployment_request,
webapp_deployer_record, webapp_deployer_record,
logger, logger,
@@ -312,7 +304,6 @@ def dump_known_requests(filename, requests, status="SEEN"):
help="How to handle requests with an FQDN: prohibit, allow, preexisting", help="How to handle requests with an FQDN: prohibit, allow, preexisting",
default="prohibit", default="prohibit",
) )
@click.option("--ip", help="IP address of the k8s deployment (to be set in DNS record)", default=None)
@click.option("--record-namespace-dns", help="eg, lrn://laconic/dns", required=True) @click.option("--record-namespace-dns", help="eg, lrn://laconic/dns", required=True)
@click.option( @click.option(
"--record-namespace-deployments", "--record-namespace-deployments",
@@ -390,7 +381,6 @@ def command( # noqa: C901
only_update_state, only_update_state,
dns_suffix, dns_suffix,
fqdn_policy, fqdn_policy,
ip,
record_namespace_dns, record_namespace_dns,
record_namespace_deployments, record_namespace_deployments,
dry_run, dry_run,
@@ -439,13 +429,6 @@ def command( # noqa: C901
) )
sys.exit(2) sys.exit(2)
if fqdn_policy == "allow" and not ip:
print(
"--ip is required with 'allow' fqdn-policy",
file=sys.stderr,
)
sys.exit(2)
tempdir = tempfile.mkdtemp() tempdir = tempfile.mkdtemp()
gpg = gnupg.GPG(gnupghome=tempdir) gpg = gnupg.GPG(gnupghome=tempdir)
@@ -682,7 +665,6 @@ def command( # noqa: C901
record_namespace_deployments, record_namespace_deployments,
record_namespace_dns, record_namespace_dns,
dns_suffix, dns_suffix,
ip,
os.path.abspath(deployment_parent_dir), os.path.abspath(deployment_parent_dir),
kube_config, kube_config,
image_registry, image_registry,
@@ -1,59 +1,8 @@
import fcntl
from functools import wraps from functools import wraps
import os
import time
# Define default file path for the lock # Define default file path for the lock
DEFAULT_LOCK_FILE_PATH = "/tmp/registry_mutex_lock_file" DEFAULT_LOCK_FILE_PATH = "/tmp/registry_mutex_lock_file"
LOCK_TIMEOUT = 30
LOCK_RETRY_INTERVAL = 3
def acquire_lock(client, lock_file_path, timeout):
# Lock alreay acquired by the current client
if client.mutex_lock_acquired:
return
while True:
try:
# Check if lock file exists and is potentially stale
if os.path.exists(lock_file_path):
with open(lock_file_path, 'r') as lock_file:
timestamp = float(lock_file.read().strip())
# If lock is stale, remove the lock file
if time.time() - timestamp > timeout:
print(f"Stale lock detected, removing lock file {lock_file_path}")
os.remove(lock_file_path)
else:
print(f"Lock file {lock_file_path} exists and is recent, waiting...")
time.sleep(LOCK_RETRY_INTERVAL)
continue
# Try to create a new lock file with the current timestamp
fd = os.open(lock_file_path, os.O_CREAT | os.O_EXCL | os.O_RDWR)
with os.fdopen(fd, 'w') as lock_file:
lock_file.write(str(time.time()))
client.mutex_lock_acquired = True
print(f"Registry lock acquired, {lock_file_path}")
# Lock successfully acquired
return
except FileExistsError:
print(f"Lock file {lock_file_path} exists, waiting...")
time.sleep(LOCK_RETRY_INTERVAL)
def release_lock(client, lock_file_path):
try:
os.remove(lock_file_path)
client.mutex_lock_acquired = False
print(f"Registry lock released, {lock_file_path}")
except FileNotFoundError:
# Lock file already removed
pass
def registry_mutex(): def registry_mutex():
@@ -64,13 +13,18 @@ def registry_mutex():
if self.mutex_lock_file: if self.mutex_lock_file:
lock_file_path = self.mutex_lock_file lock_file_path = self.mutex_lock_file
# Acquire the lock before running the function with open(lock_file_path, 'w') as lock_file:
acquire_lock(self, lock_file_path, LOCK_TIMEOUT)
try: try:
return func(self, *args, **kwargs) # Try to acquire the lock
fcntl.flock(lock_file, fcntl.LOCK_EX)
# Call the actual function
result = func(self, *args, **kwargs)
finally: finally:
# Release the lock after the function completes # Always release the lock
release_lock(self, lock_file_path) fcntl.flock(lock_file, fcntl.LOCK_UN)
return result
return wrapper return wrapper
@@ -72,12 +72,11 @@ def process_app_removal_request(
# TODO(telackey): Call the function directly. The easiest way to build the correct click context is to # TODO(telackey): Call the function directly. The easiest way to build the correct click context is to
# exec the process, but it would be better to refactor so we could just call down_operation with the # exec the process, but it would be better to refactor so we could just call down_operation with the
# necessary parameters # necessary parameters
main_logger.log("TESTING: Skipping stopping deployment.") down_command = [sys.argv[0], "deployment", "--dir", deployment_dir, "down"]
# down_command = [sys.argv[0], "deployment", "--dir", deployment_dir, "down"] if delete_volumes:
# if delete_volumes: down_command.append("--delete-volumes")
# down_command.append("--delete-volumes") result = subprocess.run(down_command)
# result = subprocess.run(down_command) result.check_returncode()
# result.check_returncode()
removal_record = { removal_record = {
"record": { "record": {
+5 -15
View File
@@ -117,6 +117,7 @@ class LaconicRegistryClient:
def __init__(self, config_file, log_file=None, mutex_lock_file=None): def __init__(self, config_file, log_file=None, mutex_lock_file=None):
self.config_file = config_file self.config_file = config_file
self.log_file = log_file self.log_file = log_file
self.mutex_lock_file = mutex_lock_file
self.cache = AttrDict( self.cache = AttrDict(
{ {
"name_or_id": {}, "name_or_id": {},
@@ -125,9 +126,6 @@ class LaconicRegistryClient:
} }
) )
self.mutex_lock_file = mutex_lock_file
self.mutex_lock_acquired = False
def whoami(self, refresh=False): def whoami(self, refresh=False):
if not refresh and "whoami" in self.cache: if not refresh and "whoami" in self.cache:
return self.cache["whoami"] return self.cache["whoami"]
@@ -689,7 +687,6 @@ def publish_deployment(
dns_record, dns_record,
dns_lrn, dns_lrn,
deployment_dir, deployment_dir,
dns_value=None,
app_deployment_request=None, app_deployment_request=None,
webapp_deployer_record=None, webapp_deployer_record=None,
logger=None, logger=None,
@@ -722,8 +719,6 @@ def publish_deployment(
} }
if app_deployment_request: if app_deployment_request:
new_dns_record["record"]["request"] = app_deployment_request.id new_dns_record["record"]["request"] = app_deployment_request.id
if dns_value:
new_dns_record["record"]["value"] = dns_value
if logger: if logger:
logger.log("Publishing DnsRecord.") logger.log("Publishing DnsRecord.")
@@ -849,21 +844,16 @@ def confirm_payment(laconic: LaconicRegistryClient, record, payment_address, min
) )
return False return False
# Check if the payment was already used on a deployment # Check if the payment was already used on a
used = laconic.app_deployments( used = laconic.app_deployments(
{"deployer": record.attributes.deployer, "payment": tx.hash}, all=True {"deployer": payment_address, "payment": tx.hash}, all=True
) )
if len(used): if len(used):
# Fetch the app name from request record logger.log(f"{record.id}: payment {tx.hash} already used on deployment {used}")
used_request = laconic.get_record(used[0].attributes.request, require=True)
# Check that payment was used for deployment of same application
if record.attributes.application != used_request.attributes.application:
logger.log(f"{record.id}: payment {tx.hash} already used on a different application deployment {used}")
return False return False
used = laconic.app_deployment_removals( used = laconic.app_deployment_removals(
{"deployer": record.attributes.deployer, "payment": tx.hash}, all=True {"deployer": payment_address, "payment": tx.hash}, all=True
) )
if len(used): if len(used):
logger.log( logger.log(