forked from cerc-io/stack-orchestrator
fix: add pre-commit hooks and fix all lint/type/format errors
Process bug fix: no pre-commit existed for this repo's Python code. Added pyproject.toml with unified dependencies (ruff, mypy, ansible-lint), .pre-commit-config.yaml with repo-based hooks (ruff) and local uv-run hooks (mypy, ansible-lint). Fixed 249 ruff errors (B023, B904, B006, B007, UP008, UP031, C408), ~13 mypy type errors, 11 ansible-lint violations, and ruff-format across all Python files including stack-orchestrator subtree. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
7f12270939
commit
fdde3be5c8
@@ -13,23 +13,22 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
||||
|
||||
import click
|
||||
import os
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
from tempfile import NamedTemporaryFile
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import click
|
||||
|
||||
from stack_orchestrator.util import error_exit, global_options2
|
||||
from stack_orchestrator.deploy.deployment_create import init_operation, create_operation
|
||||
from stack_orchestrator.deploy.deploy import create_deploy_context
|
||||
from stack_orchestrator.deploy.deploy_types import DeployCommandContext
|
||||
from stack_orchestrator.deploy.deployment_create import create_operation, init_operation
|
||||
from stack_orchestrator.util import error_exit, global_options2
|
||||
|
||||
|
||||
def _fixup_container_tag(deployment_dir: str, image: str):
|
||||
deployment_dir_path = Path(deployment_dir)
|
||||
compose_file = deployment_dir_path.joinpath(
|
||||
"compose", "docker-compose-webapp-template.yml"
|
||||
)
|
||||
compose_file = deployment_dir_path.joinpath("compose", "docker-compose-webapp-template.yml")
|
||||
# replace "cerc/webapp-container:local" in the file with our image tag
|
||||
with open(compose_file) as rfile:
|
||||
contents = rfile.read()
|
||||
@@ -56,9 +55,7 @@ def _fixup_url_spec(spec_file_name: str, url: str):
|
||||
wfile.write(contents)
|
||||
|
||||
|
||||
def create_deployment(
|
||||
ctx, deployment_dir, image, url, kube_config, image_registry, env_file
|
||||
):
|
||||
def create_deployment(ctx, deployment_dir, image, url, kube_config, image_registry, env_file):
|
||||
# Do the equivalent of:
|
||||
# 1. laconic-so --stack webapp-template deploy --deploy-to k8s init \
|
||||
# --output webapp-spec.yml
|
||||
@@ -117,9 +114,7 @@ def command(ctx):
|
||||
"--image-registry",
|
||||
help="Provide a container image registry url for this k8s cluster",
|
||||
)
|
||||
@click.option(
|
||||
"--deployment-dir", help="Create deployment files in this directory", required=True
|
||||
)
|
||||
@click.option("--deployment-dir", help="Create deployment files in this directory", required=True)
|
||||
@click.option("--image", help="image to deploy", required=True)
|
||||
@click.option("--url", help="url to serve", required=True)
|
||||
@click.option("--env-file", help="environment file for webapp")
|
||||
@@ -127,6 +122,4 @@ def command(ctx):
|
||||
def create(ctx, deployment_dir, image, url, kube_config, image_registry, env_file):
|
||||
"""create a deployment for the specified webapp container"""
|
||||
|
||||
return create_deployment(
|
||||
ctx, deployment_dir, image, url, kube_config, image_registry, env_file
|
||||
)
|
||||
return create_deployment(ctx, deployment_dir, image, url, kube_config, image_registry, env_file)
|
||||
|
||||
@@ -21,10 +21,10 @@ import sys
|
||||
import tempfile
|
||||
import time
|
||||
import uuid
|
||||
import yaml
|
||||
|
||||
import click
|
||||
import gnupg
|
||||
import yaml
|
||||
|
||||
from stack_orchestrator.deploy.images import remote_image_exists
|
||||
from stack_orchestrator.deploy.webapp import deploy_webapp
|
||||
@@ -34,16 +34,16 @@ from stack_orchestrator.deploy.webapp.util import (
|
||||
TimedLogger,
|
||||
build_container_image,
|
||||
confirm_auction,
|
||||
push_container_image,
|
||||
file_hash,
|
||||
deploy_to_k8s,
|
||||
publish_deployment,
|
||||
hostname_for_deployment_request,
|
||||
generate_hostname_for_app,
|
||||
match_owner,
|
||||
skip_by_tag,
|
||||
confirm_payment,
|
||||
deploy_to_k8s,
|
||||
file_hash,
|
||||
generate_hostname_for_app,
|
||||
hostname_for_deployment_request,
|
||||
load_known_requests,
|
||||
match_owner,
|
||||
publish_deployment,
|
||||
push_container_image,
|
||||
skip_by_tag,
|
||||
)
|
||||
|
||||
|
||||
@@ -70,9 +70,7 @@ def process_app_deployment_request(
|
||||
logger.log("BEGIN - process_app_deployment_request")
|
||||
|
||||
# 1. look up application
|
||||
app = laconic.get_record(
|
||||
app_deployment_request.attributes.application, require=True
|
||||
)
|
||||
app = laconic.get_record(app_deployment_request.attributes.application, require=True)
|
||||
assert app is not None # require=True ensures this
|
||||
logger.log(f"Retrieved app record {app_deployment_request.attributes.application}")
|
||||
|
||||
@@ -84,9 +82,7 @@ def process_app_deployment_request(
|
||||
if "allow" == fqdn_policy or "preexisting" == fqdn_policy:
|
||||
fqdn = requested_name
|
||||
else:
|
||||
raise Exception(
|
||||
f"{requested_name} is invalid: only unqualified hostnames are allowed."
|
||||
)
|
||||
raise Exception(f"{requested_name} is invalid: only unqualified hostnames are allowed.")
|
||||
else:
|
||||
fqdn = f"{requested_name}.{default_dns_suffix}"
|
||||
|
||||
@@ -108,8 +104,7 @@ def process_app_deployment_request(
|
||||
logger.log(f"Matched DnsRecord ownership: {matched_owner}")
|
||||
else:
|
||||
raise Exception(
|
||||
"Unable to confirm ownership of DnsRecord %s for request %s"
|
||||
% (dns_lrn, app_deployment_request.id)
|
||||
f"Unable to confirm ownership of DnsRecord {dns_lrn} for request {app_deployment_request.id}"
|
||||
)
|
||||
elif "preexisting" == fqdn_policy:
|
||||
raise Exception(
|
||||
@@ -144,7 +139,7 @@ def process_app_deployment_request(
|
||||
env_filename = tempfile.mktemp()
|
||||
with open(env_filename, "w") as file:
|
||||
for k, v in env.items():
|
||||
file.write("%s=%s\n" % (k, shlex.quote(str(v))))
|
||||
file.write(f"{k}={shlex.quote(str(v))}\n")
|
||||
|
||||
# 5. determine new or existing deployment
|
||||
# a. check for deployment lrn
|
||||
@@ -153,8 +148,7 @@ def process_app_deployment_request(
|
||||
app_deployment_lrn = app_deployment_request.attributes.deployment
|
||||
if not app_deployment_lrn.startswith(deployment_record_namespace):
|
||||
raise Exception(
|
||||
"Deployment LRN %s is not in a supported namespace"
|
||||
% app_deployment_request.attributes.deployment
|
||||
f"Deployment LRN {app_deployment_request.attributes.deployment} is not in a supported namespace"
|
||||
)
|
||||
|
||||
deployment_record = laconic.get_record(app_deployment_lrn)
|
||||
@@ -165,14 +159,14 @@ def process_app_deployment_request(
|
||||
# already-unique deployment id
|
||||
unique_deployment_id = hashlib.md5(fqdn.encode()).hexdigest()[:16]
|
||||
deployment_config_file = os.path.join(deployment_dir, "config.env")
|
||||
deployment_container_tag = "laconic-webapp/%s:local" % unique_deployment_id
|
||||
deployment_container_tag = f"laconic-webapp/{unique_deployment_id}:local"
|
||||
app_image_shared_tag = f"laconic-webapp/{app.id}:local"
|
||||
# b. check for deployment directory (create if necessary)
|
||||
if not os.path.exists(deployment_dir):
|
||||
if deployment_record:
|
||||
raise Exception(
|
||||
"Deployment record %s exists, but not deployment dir %s. "
|
||||
"Please remove name." % (app_deployment_lrn, deployment_dir)
|
||||
f"Deployment record {app_deployment_lrn} exists, but not deployment dir {deployment_dir}. "
|
||||
"Please remove name."
|
||||
)
|
||||
logger.log(
|
||||
f"Creating webapp deployment in: {deployment_dir} "
|
||||
@@ -198,11 +192,7 @@ def process_app_deployment_request(
|
||||
)
|
||||
# 6. build container (if needed)
|
||||
# TODO: add a comment that explains what this code is doing (not clear to me)
|
||||
if (
|
||||
not deployment_record
|
||||
or deployment_record.attributes.application != app.id
|
||||
or force_rebuild
|
||||
):
|
||||
if not deployment_record or deployment_record.attributes.application != app.id or force_rebuild:
|
||||
needs_k8s_deploy = True
|
||||
# check if the image already exists
|
||||
shared_tag_exists = remote_image_exists(image_registry, app_image_shared_tag)
|
||||
@@ -224,11 +214,9 @@ def process_app_deployment_request(
|
||||
# )
|
||||
logger.log("Tag complete")
|
||||
else:
|
||||
extra_build_args = [] # TODO: pull from request
|
||||
extra_build_args: list[str] = [] # TODO: pull from request
|
||||
logger.log(f"Building container image: {deployment_container_tag}")
|
||||
build_container_image(
|
||||
app, deployment_container_tag, extra_build_args, logger
|
||||
)
|
||||
build_container_image(app, deployment_container_tag, extra_build_args, logger)
|
||||
logger.log("Build complete")
|
||||
logger.log(f"Pushing container image: {deployment_container_tag}")
|
||||
push_container_image(deployment_dir, logger)
|
||||
@@ -287,9 +275,7 @@ def dump_known_requests(filename, requests, status="SEEN"):
|
||||
|
||||
@click.command()
|
||||
@click.option("--kube-config", help="Provide a config file for a k8s deployment")
|
||||
@click.option(
|
||||
"--laconic-config", help="Provide a config file for laconicd", required=True
|
||||
)
|
||||
@click.option("--laconic-config", help="Provide a config file for laconicd", required=True)
|
||||
@click.option(
|
||||
"--image-registry",
|
||||
help="Provide a container image registry url for this k8s cluster",
|
||||
@@ -306,9 +292,7 @@ def dump_known_requests(filename, requests, status="SEEN"):
|
||||
is_flag=True,
|
||||
default=False,
|
||||
)
|
||||
@click.option(
|
||||
"--state-file", help="File to store state about previously seen requests."
|
||||
)
|
||||
@click.option("--state-file", help="File to store state about previously seen requests.")
|
||||
@click.option(
|
||||
"--only-update-state",
|
||||
help="Only update the state file, don't process any requests anything.",
|
||||
@@ -331,9 +315,7 @@ def dump_known_requests(filename, requests, status="SEEN"):
|
||||
help="eg, lrn://laconic/deployments",
|
||||
required=True,
|
||||
)
|
||||
@click.option(
|
||||
"--dry-run", help="Don't do anything, just report what would be done.", is_flag=True
|
||||
)
|
||||
@click.option("--dry-run", help="Don't do anything, just report what would be done.", is_flag=True)
|
||||
@click.option(
|
||||
"--include-tags",
|
||||
help="Only include requests with matching tags (comma-separated).",
|
||||
@@ -344,17 +326,13 @@ def dump_known_requests(filename, requests, status="SEEN"):
|
||||
help="Exclude requests with matching tags (comma-separated).",
|
||||
default="",
|
||||
)
|
||||
@click.option(
|
||||
"--force-rebuild", help="Rebuild even if the image already exists.", is_flag=True
|
||||
)
|
||||
@click.option("--force-rebuild", help="Rebuild even if the image already exists.", is_flag=True)
|
||||
@click.option(
|
||||
"--recreate-on-deploy",
|
||||
help="Remove and recreate deployments instead of updating them.",
|
||||
is_flag=True,
|
||||
)
|
||||
@click.option(
|
||||
"--log-dir", help="Output build/deployment logs to directory.", default=None
|
||||
)
|
||||
@click.option("--log-dir", help="Output build/deployment logs to directory.", default=None)
|
||||
@click.option(
|
||||
"--min-required-payment",
|
||||
help="Requests must have a minimum payment to be processed (in alnt)",
|
||||
@@ -378,9 +356,7 @@ def dump_known_requests(filename, requests, status="SEEN"):
|
||||
help="The directory containing uploaded config.",
|
||||
required=True,
|
||||
)
|
||||
@click.option(
|
||||
"--private-key-file", help="The private key for decrypting config.", required=True
|
||||
)
|
||||
@click.option("--private-key-file", help="The private key for decrypting config.", required=True)
|
||||
@click.option(
|
||||
"--registry-lock-file",
|
||||
help="File path to use for registry mutex lock",
|
||||
@@ -435,11 +411,7 @@ def command( # noqa: C901
|
||||
sys.exit(2)
|
||||
|
||||
if not only_update_state:
|
||||
if (
|
||||
not record_namespace_dns
|
||||
or not record_namespace_deployments
|
||||
or not dns_suffix
|
||||
):
|
||||
if not record_namespace_dns or not record_namespace_deployments or not dns_suffix:
|
||||
print(
|
||||
"--dns-suffix, --record-namespace-dns, and "
|
||||
"--record-namespace-deployments are all required",
|
||||
@@ -491,8 +463,7 @@ def command( # noqa: C901
|
||||
|
||||
if min_required_payment and not payment_address:
|
||||
print(
|
||||
f"Minimum payment required, but no payment address listed "
|
||||
f"for deployer: {lrn}.",
|
||||
f"Minimum payment required, but no payment address listed " f"for deployer: {lrn}.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(2)
|
||||
@@ -557,26 +528,18 @@ def command( # noqa: C901
|
||||
requested_name = r.attributes.dns
|
||||
if not requested_name:
|
||||
requested_name = generate_hostname_for_app(app)
|
||||
main_logger.log(
|
||||
"Generating name %s for request %s." % (requested_name, r_id)
|
||||
)
|
||||
main_logger.log(f"Generating name {requested_name} for request {r_id}.")
|
||||
|
||||
if (
|
||||
requested_name in skipped_by_name
|
||||
or requested_name in requests_by_name
|
||||
):
|
||||
main_logger.log(
|
||||
"Ignoring request %s, it has been superseded." % r_id
|
||||
)
|
||||
if requested_name in skipped_by_name or requested_name in requests_by_name:
|
||||
main_logger.log(f"Ignoring request {r_id}, it has been superseded.")
|
||||
result = "SKIP"
|
||||
continue
|
||||
|
||||
if skip_by_tag(r, include_tags, exclude_tags):
|
||||
r_tags = r.attributes.tags if r.attributes else None
|
||||
main_logger.log(
|
||||
"Skipping request %s, filtered by tag "
|
||||
"(include %s, exclude %s, present %s)"
|
||||
% (r_id, include_tags, exclude_tags, r_tags)
|
||||
f"Skipping request {r_id}, filtered by tag "
|
||||
f"(include {include_tags}, exclude {exclude_tags}, present {r_tags})"
|
||||
)
|
||||
skipped_by_name[requested_name] = r
|
||||
result = "SKIP"
|
||||
@@ -584,8 +547,7 @@ def command( # noqa: C901
|
||||
|
||||
r_app = r.attributes.application if r.attributes else "unknown"
|
||||
main_logger.log(
|
||||
"Found pending request %s to run application %s on %s."
|
||||
% (r_id, r_app, requested_name)
|
||||
f"Found pending request {r_id} to run application {r_app} on {requested_name}."
|
||||
)
|
||||
requests_by_name[requested_name] = r
|
||||
except Exception as e:
|
||||
@@ -617,17 +579,14 @@ def command( # noqa: C901
|
||||
|
||||
requests_to_check_for_payment = []
|
||||
for r in requests_by_name.values():
|
||||
if r.id in cancellation_requests and match_owner(
|
||||
cancellation_requests[r.id], r
|
||||
):
|
||||
if r.id in cancellation_requests and match_owner(cancellation_requests[r.id], r):
|
||||
main_logger.log(
|
||||
f"Found deployment cancellation request for {r.id} "
|
||||
f"at {cancellation_requests[r.id].id}"
|
||||
)
|
||||
elif r.id in deployments_by_request:
|
||||
main_logger.log(
|
||||
f"Found satisfied request for {r.id} "
|
||||
f"at {deployments_by_request[r.id].id}"
|
||||
f"Found satisfied request for {r.id} " f"at {deployments_by_request[r.id].id}"
|
||||
)
|
||||
else:
|
||||
if (
|
||||
@@ -635,8 +594,7 @@ def command( # noqa: C901
|
||||
and previous_requests[r.id].get("status", "") != "RETRY"
|
||||
):
|
||||
main_logger.log(
|
||||
f"Skipping unsatisfied request {r.id} "
|
||||
"because we have seen it before."
|
||||
f"Skipping unsatisfied request {r.id} " "because we have seen it before."
|
||||
)
|
||||
else:
|
||||
main_logger.log(f"Request {r.id} needs to processed.")
|
||||
@@ -650,14 +608,10 @@ def command( # noqa: C901
|
||||
main_logger.log(f"{r.id}: Auction confirmed.")
|
||||
requests_to_execute.append(r)
|
||||
else:
|
||||
main_logger.log(
|
||||
f"Skipping request {r.id}: unable to verify auction."
|
||||
)
|
||||
main_logger.log(f"Skipping request {r.id}: unable to verify auction.")
|
||||
dump_known_requests(state_file, [r], status="SKIP")
|
||||
else:
|
||||
main_logger.log(
|
||||
f"Skipping request {r.id}: not handling requests with auction."
|
||||
)
|
||||
main_logger.log(f"Skipping request {r.id}: not handling requests with auction.")
|
||||
dump_known_requests(state_file, [r], status="SKIP")
|
||||
elif min_required_payment:
|
||||
main_logger.log(f"{r.id}: Confirming payment...")
|
||||
@@ -671,16 +625,12 @@ def command( # noqa: C901
|
||||
main_logger.log(f"{r.id}: Payment confirmed.")
|
||||
requests_to_execute.append(r)
|
||||
else:
|
||||
main_logger.log(
|
||||
f"Skipping request {r.id}: unable to verify payment."
|
||||
)
|
||||
main_logger.log(f"Skipping request {r.id}: unable to verify payment.")
|
||||
dump_known_requests(state_file, [r], status="UNPAID")
|
||||
else:
|
||||
requests_to_execute.append(r)
|
||||
|
||||
main_logger.log(
|
||||
"Found %d unsatisfied request(s) to process." % len(requests_to_execute)
|
||||
)
|
||||
main_logger.log(f"Found {len(requests_to_execute)} unsatisfied request(s) to process.")
|
||||
|
||||
if not dry_run:
|
||||
for r in requests_to_execute:
|
||||
@@ -700,10 +650,8 @@ def command( # noqa: C901
|
||||
if not os.path.exists(run_log_dir):
|
||||
os.mkdir(run_log_dir)
|
||||
run_log_file_path = os.path.join(run_log_dir, f"{run_id}.log")
|
||||
main_logger.log(
|
||||
f"Directing deployment logs to: {run_log_file_path}"
|
||||
)
|
||||
run_log_file = open(run_log_file_path, "wt")
|
||||
main_logger.log(f"Directing deployment logs to: {run_log_file_path}")
|
||||
run_log_file = open(run_log_file_path, "w")
|
||||
run_reg_client = LaconicRegistryClient(
|
||||
laconic_config,
|
||||
log_file=run_log_file,
|
||||
|
||||
@@ -12,18 +12,18 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
import json
|
||||
import sys
|
||||
|
||||
import click
|
||||
|
||||
from stack_orchestrator.deploy.webapp.util import (
|
||||
AUCTION_KIND_PROVIDER,
|
||||
AttrDict,
|
||||
AuctionStatus,
|
||||
LaconicRegistryClient,
|
||||
TimedLogger,
|
||||
load_known_requests,
|
||||
AUCTION_KIND_PROVIDER,
|
||||
AuctionStatus,
|
||||
)
|
||||
|
||||
|
||||
@@ -44,16 +44,13 @@ def process_app_deployment_auction(
|
||||
|
||||
# Check auction kind
|
||||
if auction.kind != AUCTION_KIND_PROVIDER:
|
||||
raise Exception(
|
||||
f"Auction kind needs to be ${AUCTION_KIND_PROVIDER}, got {auction.kind}"
|
||||
)
|
||||
raise Exception(f"Auction kind needs to be ${AUCTION_KIND_PROVIDER}, got {auction.kind}")
|
||||
|
||||
if current_status == "PENDING":
|
||||
# Skip if pending auction not in commit state
|
||||
if auction.status != AuctionStatus.COMMIT:
|
||||
logger.log(
|
||||
f"Skipping pending request, auction {auction_id} "
|
||||
f"status: {auction.status}"
|
||||
f"Skipping pending request, auction {auction_id} " f"status: {auction.status}"
|
||||
)
|
||||
return "SKIP", ""
|
||||
|
||||
@@ -115,9 +112,7 @@ def dump_known_auction_requests(filename, requests, status="SEEN"):
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option(
|
||||
"--laconic-config", help="Provide a config file for laconicd", required=True
|
||||
)
|
||||
@click.option("--laconic-config", help="Provide a config file for laconicd", required=True)
|
||||
@click.option(
|
||||
"--state-file",
|
||||
help="File to store state about previously seen auction requests.",
|
||||
@@ -133,9 +128,7 @@ def dump_known_auction_requests(filename, requests, status="SEEN"):
|
||||
help="File path to use for registry mutex lock",
|
||||
default=None,
|
||||
)
|
||||
@click.option(
|
||||
"--dry-run", help="Don't do anything, just report what would be done.", is_flag=True
|
||||
)
|
||||
@click.option("--dry-run", help="Don't do anything, just report what would be done.", is_flag=True)
|
||||
@click.pass_context
|
||||
def command(
|
||||
ctx,
|
||||
@@ -198,8 +191,7 @@ def command(
|
||||
continue
|
||||
|
||||
logger.log(
|
||||
f"Found pending auction request {r.id} for application "
|
||||
f"{application}."
|
||||
f"Found pending auction request {r.id} for application " f"{application}."
|
||||
)
|
||||
|
||||
# Add requests to be processed
|
||||
@@ -209,9 +201,7 @@ def command(
|
||||
result_status = "ERROR"
|
||||
logger.log(f"ERROR: examining request {r.id}: " + str(e))
|
||||
finally:
|
||||
logger.log(
|
||||
f"DONE: Examining request {r.id} with result {result_status}."
|
||||
)
|
||||
logger.log(f"DONE: Examining request {r.id} with result {result_status}.")
|
||||
if result_status in ["ERROR"]:
|
||||
dump_known_auction_requests(
|
||||
state_file,
|
||||
|
||||
@@ -30,9 +30,7 @@ def fatal(msg: str):
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option(
|
||||
"--laconic-config", help="Provide a config file for laconicd", required=True
|
||||
)
|
||||
@click.option("--laconic-config", help="Provide a config file for laconicd", required=True)
|
||||
@click.option(
|
||||
"--app",
|
||||
help="The LRN of the application to deploy.",
|
||||
|
||||
@@ -13,28 +13,24 @@
|
||||
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
||||
|
||||
import base64
|
||||
import click
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import click
|
||||
import yaml
|
||||
|
||||
from stack_orchestrator.deploy.webapp.util import LaconicRegistryClient
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option(
|
||||
"--laconic-config", help="Provide a config file for laconicd", required=True
|
||||
)
|
||||
@click.option("--laconic-config", help="Provide a config file for laconicd", required=True)
|
||||
@click.option("--api-url", help="The API URL of the deployer.", required=True)
|
||||
@click.option(
|
||||
"--public-key-file",
|
||||
help="The public key to use. This should be a binary file.",
|
||||
required=True,
|
||||
)
|
||||
@click.option(
|
||||
"--lrn", help="eg, lrn://laconic/deployers/my.deployer.name", required=True
|
||||
)
|
||||
@click.option("--lrn", help="eg, lrn://laconic/deployers/my.deployer.name", required=True)
|
||||
@click.option(
|
||||
"--payment-address",
|
||||
help="The address to which payments should be made. "
|
||||
@@ -84,9 +80,7 @@ def command( # noqa: C901
|
||||
}
|
||||
|
||||
if min_required_payment:
|
||||
webapp_deployer_record["record"][
|
||||
"minimumPayment"
|
||||
] = f"{min_required_payment}alnt"
|
||||
webapp_deployer_record["record"]["minimumPayment"] = f"{min_required_payment}alnt"
|
||||
|
||||
if dry_run:
|
||||
yaml.dump(webapp_deployer_record, sys.stdout)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from functools import wraps
|
||||
import os
|
||||
import time
|
||||
from functools import wraps
|
||||
|
||||
# Define default file path for the lock
|
||||
DEFAULT_LOCK_FILE_PATH = "/tmp/registry_mutex_lock_file"
|
||||
@@ -17,7 +17,7 @@ def acquire_lock(client, lock_file_path, timeout):
|
||||
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:
|
||||
with open(lock_file_path) as lock_file:
|
||||
timestamp = float(lock_file.read().strip())
|
||||
|
||||
# If lock is stale, remove the lock file
|
||||
@@ -25,9 +25,7 @@ def acquire_lock(client, lock_file_path, 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..."
|
||||
)
|
||||
print(f"Lock file {lock_file_path} exists and is recent, waiting...")
|
||||
time.sleep(LOCK_RETRY_INTERVAL)
|
||||
continue
|
||||
|
||||
|
||||
@@ -12,24 +12,24 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
||||
|
||||
import base64
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
from datetime import datetime
|
||||
from typing import NoReturn
|
||||
import base64
|
||||
|
||||
import gnupg
|
||||
import click
|
||||
import gnupg
|
||||
import requests
|
||||
import yaml
|
||||
from dotenv import dotenv_values
|
||||
|
||||
from stack_orchestrator.deploy.webapp.util import (
|
||||
AUCTION_KIND_PROVIDER,
|
||||
AuctionStatus,
|
||||
LaconicRegistryClient,
|
||||
)
|
||||
from dotenv import dotenv_values
|
||||
|
||||
|
||||
def fatal(msg: str) -> NoReturn:
|
||||
@@ -38,9 +38,7 @@ def fatal(msg: str) -> NoReturn:
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option(
|
||||
"--laconic-config", help="Provide a config file for laconicd", required=True
|
||||
)
|
||||
@click.option("--laconic-config", help="Provide a config file for laconicd", required=True)
|
||||
@click.option(
|
||||
"--app",
|
||||
help="The LRN of the application to deploy.",
|
||||
@@ -63,9 +61,7 @@ def fatal(msg: str) -> NoReturn:
|
||||
"'auto' to use the deployer's minimum required payment."
|
||||
),
|
||||
)
|
||||
@click.option(
|
||||
"--use-payment", help="The TX id of an existing, unused payment", default=None
|
||||
)
|
||||
@click.option("--use-payment", help="The TX id of an existing, unused payment", default=None)
|
||||
@click.option("--dns", help="the DNS name to request (default is autogenerated)")
|
||||
@click.option(
|
||||
"--dry-run",
|
||||
@@ -144,9 +140,7 @@ def command( # noqa: C901
|
||||
# Check auction kind
|
||||
auction_kind = auction.kind if auction else None
|
||||
if auction_kind != AUCTION_KIND_PROVIDER:
|
||||
fatal(
|
||||
f"Auction kind needs to be ${AUCTION_KIND_PROVIDER}, got {auction_kind}"
|
||||
)
|
||||
fatal(f"Auction kind needs to be ${AUCTION_KIND_PROVIDER}, got {auction_kind}")
|
||||
|
||||
# Check auction status
|
||||
auction_status = auction.status if auction else None
|
||||
@@ -163,14 +157,9 @@ def command( # noqa: C901
|
||||
# Get deployer record for all the auction winners
|
||||
for auction_winner in auction_winners:
|
||||
# TODO: Match auction winner address with provider address?
|
||||
deployer_records_by_owner = laconic.webapp_deployers(
|
||||
{"paymentAddress": auction_winner}
|
||||
)
|
||||
deployer_records_by_owner = laconic.webapp_deployers({"paymentAddress": auction_winner})
|
||||
if len(deployer_records_by_owner) == 0:
|
||||
print(
|
||||
f"WARNING: Unable to locate deployer for auction winner "
|
||||
f"{auction_winner}"
|
||||
)
|
||||
print(f"WARNING: Unable to locate deployer for auction winner " f"{auction_winner}")
|
||||
|
||||
# Take first record with name set
|
||||
target_deployer_record = deployer_records_by_owner[0]
|
||||
@@ -196,9 +185,7 @@ def command( # noqa: C901
|
||||
gpg = gnupg.GPG(gnupghome=tempdir)
|
||||
|
||||
# Import the deployer's public key
|
||||
result = gpg.import_keys(
|
||||
base64.b64decode(deployer_record.attributes.publicKey)
|
||||
)
|
||||
result = gpg.import_keys(base64.b64decode(deployer_record.attributes.publicKey))
|
||||
if 1 != result.imported:
|
||||
fatal("Failed to import deployer's public key.")
|
||||
|
||||
@@ -237,15 +224,9 @@ def command( # noqa: C901
|
||||
if (not deployer) and len(deployer_record.names):
|
||||
target_deployer = deployer_record.names[0]
|
||||
|
||||
app_name = (
|
||||
app_record.attributes.name
|
||||
if app_record and app_record.attributes
|
||||
else "unknown"
|
||||
)
|
||||
app_name = app_record.attributes.name if app_record and app_record.attributes else "unknown"
|
||||
app_version = (
|
||||
app_record.attributes.version
|
||||
if app_record and app_record.attributes
|
||||
else "unknown"
|
||||
app_record.attributes.version if app_record and app_record.attributes else "unknown"
|
||||
)
|
||||
deployment_request = {
|
||||
"record": {
|
||||
@@ -273,15 +254,11 @@ def command( # noqa: C901
|
||||
deployment_request["record"]["payment"] = "DRY_RUN"
|
||||
elif "auto" == make_payment:
|
||||
if "minimumPayment" in deployer_record.attributes:
|
||||
amount = int(
|
||||
deployer_record.attributes.minimumPayment.replace("alnt", "")
|
||||
)
|
||||
amount = int(deployer_record.attributes.minimumPayment.replace("alnt", ""))
|
||||
else:
|
||||
amount = make_payment
|
||||
if amount:
|
||||
receipt = laconic.send_tokens(
|
||||
deployer_record.attributes.paymentAddress, amount
|
||||
)
|
||||
receipt = laconic.send_tokens(deployer_record.attributes.paymentAddress, amount)
|
||||
deployment_request["record"]["payment"] = receipt.tx.hash
|
||||
print("Payment TX:", receipt.tx.hash)
|
||||
elif use_payment:
|
||||
|
||||
@@ -26,12 +26,8 @@ def fatal(msg: str) -> None:
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option(
|
||||
"--laconic-config", help="Provide a config file for laconicd", required=True
|
||||
)
|
||||
@click.option(
|
||||
"--deployer", help="The LRN of the deployer to process this request.", required=True
|
||||
)
|
||||
@click.option("--laconic-config", help="Provide a config file for laconicd", required=True)
|
||||
@click.option("--deployer", help="The LRN of the deployer to process this request.", required=True)
|
||||
@click.option(
|
||||
"--deployment",
|
||||
help="Deployment record (ApplicationDeploymentRecord) id of the deployment.",
|
||||
@@ -44,9 +40,7 @@ def fatal(msg: str) -> None:
|
||||
"'auto' to use the deployer's minimum required payment."
|
||||
),
|
||||
)
|
||||
@click.option(
|
||||
"--use-payment", help="The TX id of an existing, unused payment", default=None
|
||||
)
|
||||
@click.option("--use-payment", help="The TX id of an existing, unused payment", default=None)
|
||||
@click.option(
|
||||
"--dry-run",
|
||||
help="Don't publish anything, just report what would be done.",
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
# all or specific containers
|
||||
|
||||
import hashlib
|
||||
|
||||
import click
|
||||
from dotenv import dotenv_values
|
||||
|
||||
|
||||
@@ -21,11 +21,11 @@ import sys
|
||||
import click
|
||||
|
||||
from stack_orchestrator.deploy.webapp.util import (
|
||||
TimedLogger,
|
||||
LaconicRegistryClient,
|
||||
TimedLogger,
|
||||
confirm_payment,
|
||||
match_owner,
|
||||
skip_by_tag,
|
||||
confirm_payment,
|
||||
)
|
||||
|
||||
main_logger = TimedLogger(file=sys.stderr)
|
||||
@@ -40,9 +40,7 @@ def process_app_removal_request(
|
||||
delete_names,
|
||||
webapp_deployer_record,
|
||||
):
|
||||
deployment_record = laconic.get_record(
|
||||
app_removal_request.attributes.deployment, require=True
|
||||
)
|
||||
deployment_record = laconic.get_record(app_removal_request.attributes.deployment, require=True)
|
||||
assert deployment_record is not None # require=True ensures this
|
||||
assert deployment_record.attributes is not None
|
||||
|
||||
@@ -50,12 +48,10 @@ def process_app_removal_request(
|
||||
assert dns_record is not None # require=True ensures this
|
||||
assert dns_record.attributes is not None
|
||||
|
||||
deployment_dir = os.path.join(
|
||||
deployment_parent_dir, dns_record.attributes.name.lower()
|
||||
)
|
||||
deployment_dir = os.path.join(deployment_parent_dir, dns_record.attributes.name.lower())
|
||||
|
||||
if not os.path.exists(deployment_dir):
|
||||
raise Exception("Deployment directory %s does not exist." % deployment_dir)
|
||||
raise Exception(f"Deployment directory {deployment_dir} does not exist.")
|
||||
|
||||
# Check if the removal request is from the owner of the DnsRecord or
|
||||
# deployment record.
|
||||
@@ -63,9 +59,7 @@ def process_app_removal_request(
|
||||
|
||||
# Or of the original deployment request.
|
||||
if not matched_owner and deployment_record.attributes.request:
|
||||
original_request = laconic.get_record(
|
||||
deployment_record.attributes.request, require=True
|
||||
)
|
||||
original_request = laconic.get_record(deployment_record.attributes.request, require=True)
|
||||
assert original_request is not None # require=True ensures this
|
||||
matched_owner = match_owner(app_removal_request, original_request)
|
||||
|
||||
@@ -75,8 +69,7 @@ def process_app_removal_request(
|
||||
deployment_id = deployment_record.id if deployment_record else "unknown"
|
||||
request_id = app_removal_request.id if app_removal_request else "unknown"
|
||||
raise Exception(
|
||||
"Unable to confirm ownership of deployment %s for removal request %s"
|
||||
% (deployment_id, request_id)
|
||||
f"Unable to confirm ownership of deployment {deployment_id} for removal request {request_id}"
|
||||
)
|
||||
|
||||
# TODO(telackey): Call the function directly. The easiest way to build
|
||||
@@ -124,7 +117,7 @@ def process_app_removal_request(
|
||||
|
||||
def load_known_requests(filename):
|
||||
if filename and os.path.exists(filename):
|
||||
return json.load(open(filename, "r"))
|
||||
return json.load(open(filename))
|
||||
return {}
|
||||
|
||||
|
||||
@@ -138,9 +131,7 @@ def dump_known_requests(filename, requests):
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option(
|
||||
"--laconic-config", help="Provide a config file for laconicd", required=True
|
||||
)
|
||||
@click.option("--laconic-config", help="Provide a config file for laconicd", required=True)
|
||||
@click.option(
|
||||
"--deployment-parent-dir",
|
||||
help="Create deployment directories beneath this directory",
|
||||
@@ -153,9 +144,7 @@ def dump_known_requests(filename, requests):
|
||||
is_flag=True,
|
||||
default=False,
|
||||
)
|
||||
@click.option(
|
||||
"--state-file", help="File to store state about previously seen requests."
|
||||
)
|
||||
@click.option("--state-file", help="File to store state about previously seen requests.")
|
||||
@click.option(
|
||||
"--only-update-state",
|
||||
help="Only update the state file, don't process any requests anything.",
|
||||
@@ -166,12 +155,8 @@ def dump_known_requests(filename, requests):
|
||||
help="Delete all names associated with removed deployments.",
|
||||
default=True,
|
||||
)
|
||||
@click.option(
|
||||
"--delete-volumes/--preserve-volumes", default=True, help="delete data volumes"
|
||||
)
|
||||
@click.option(
|
||||
"--dry-run", help="Don't do anything, just report what would be done.", is_flag=True
|
||||
)
|
||||
@click.option("--delete-volumes/--preserve-volumes", default=True, help="delete data volumes")
|
||||
@click.option("--dry-run", help="Don't do anything, just report what would be done.", is_flag=True)
|
||||
@click.option(
|
||||
"--include-tags",
|
||||
help="Only include requests with matching tags (comma-separated).",
|
||||
@@ -245,8 +230,7 @@ def command( # noqa: C901
|
||||
|
||||
if min_required_payment and not payment_address:
|
||||
print(
|
||||
f"Minimum payment required, but no payment address listed "
|
||||
f"for deployer: {lrn}.",
|
||||
f"Minimum payment required, but no payment address listed " f"for deployer: {lrn}.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(2)
|
||||
@@ -303,9 +287,7 @@ def command( # noqa: C901
|
||||
continue
|
||||
if not r.attributes.deployment:
|
||||
r_id = r.id if r else "unknown"
|
||||
main_logger.log(
|
||||
f"Skipping removal request {r_id} since it was a cancellation."
|
||||
)
|
||||
main_logger.log(f"Skipping removal request {r_id} since it was a cancellation.")
|
||||
elif r.attributes.deployment in one_per_deployment:
|
||||
r_id = r.id if r else "unknown"
|
||||
main_logger.log(f"Skipping removal request {r_id} since it was superseded.")
|
||||
@@ -323,14 +305,12 @@ def command( # noqa: C901
|
||||
)
|
||||
elif skip_by_tag(r, include_tags, exclude_tags):
|
||||
main_logger.log(
|
||||
"Skipping removal request %s, filtered by tag "
|
||||
"(include %s, exclude %s, present %s)"
|
||||
% (r.id, include_tags, exclude_tags, r.attributes.tags)
|
||||
f"Skipping removal request {r.id}, filtered by tag "
|
||||
f"(include {include_tags}, exclude {exclude_tags}, present {r.attributes.tags})"
|
||||
)
|
||||
elif r.id in removals_by_request:
|
||||
main_logger.log(
|
||||
f"Found satisfied request for {r.id} "
|
||||
f"at {removals_by_request[r.id].id}"
|
||||
f"Found satisfied request for {r.id} " f"at {removals_by_request[r.id].id}"
|
||||
)
|
||||
elif r.attributes.deployment in removals_by_deployment:
|
||||
main_logger.log(
|
||||
@@ -344,8 +324,7 @@ def command( # noqa: C901
|
||||
requests_to_check_for_payment.append(r)
|
||||
else:
|
||||
main_logger.log(
|
||||
f"Skipping unsatisfied request {r.id} "
|
||||
"because we have seen it before."
|
||||
f"Skipping unsatisfied request {r.id} " "because we have seen it before."
|
||||
)
|
||||
except Exception as e:
|
||||
main_logger.log(f"ERROR examining {r.id}: {e}")
|
||||
@@ -370,9 +349,7 @@ def command( # noqa: C901
|
||||
else:
|
||||
requests_to_execute = requests_to_check_for_payment
|
||||
|
||||
main_logger.log(
|
||||
"Found %d unsatisfied request(s) to process." % len(requests_to_execute)
|
||||
)
|
||||
main_logger.log(f"Found {len(requests_to_execute)} unsatisfied request(s) to process.")
|
||||
|
||||
if not dry_run:
|
||||
for r in requests_to_execute:
|
||||
|
||||
@@ -22,10 +22,10 @@ import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import uuid
|
||||
import yaml
|
||||
|
||||
from enum import Enum
|
||||
from typing import Any, List, Optional, TextIO
|
||||
from typing import Any, TextIO
|
||||
|
||||
import yaml
|
||||
|
||||
from stack_orchestrator.deploy.webapp.registry_mutex import registry_mutex
|
||||
|
||||
@@ -43,17 +43,17 @@ AUCTION_KIND_PROVIDER = "provider"
|
||||
|
||||
class AttrDict(dict):
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
super(AttrDict, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.__dict__ = self
|
||||
|
||||
def __getattribute__(self, attr: str) -> Any:
|
||||
__dict__ = super(AttrDict, self).__getattribute__("__dict__")
|
||||
__dict__ = super().__getattribute__("__dict__")
|
||||
if attr in __dict__:
|
||||
v = super(AttrDict, self).__getattribute__(attr)
|
||||
v = super().__getattribute__(attr)
|
||||
if isinstance(v, dict):
|
||||
return AttrDict(v)
|
||||
return v
|
||||
return super(AttrDict, self).__getattribute__(attr)
|
||||
return super().__getattribute__(attr)
|
||||
|
||||
def __getattr__(self, attr: str) -> Any:
|
||||
# This method is called when attribute is not found
|
||||
@@ -62,15 +62,13 @@ class AttrDict(dict):
|
||||
|
||||
|
||||
class TimedLogger:
|
||||
def __init__(self, id: str = "", file: Optional[TextIO] = None) -> None:
|
||||
def __init__(self, id: str = "", file: TextIO | None = None) -> None:
|
||||
self.start = datetime.datetime.now()
|
||||
self.last = self.start
|
||||
self.id = id
|
||||
self.file = file
|
||||
|
||||
def log(
|
||||
self, msg: str, show_step_time: bool = True, show_total_time: bool = False
|
||||
) -> None:
|
||||
def log(self, msg: str, show_step_time: bool = True, show_total_time: bool = False) -> None:
|
||||
prefix = f"{datetime.datetime.utcnow()} - {self.id}"
|
||||
if show_step_time:
|
||||
prefix += f" - {datetime.datetime.now() - self.last} (step)"
|
||||
@@ -84,11 +82,11 @@ class TimedLogger:
|
||||
|
||||
def load_known_requests(filename):
|
||||
if filename and os.path.exists(filename):
|
||||
return json.load(open(filename, "r"))
|
||||
return json.load(open(filename))
|
||||
return {}
|
||||
|
||||
|
||||
def logged_cmd(log_file: Optional[TextIO], *vargs: str) -> str:
|
||||
def logged_cmd(log_file: TextIO | None, *vargs: str) -> str:
|
||||
result = None
|
||||
try:
|
||||
if log_file:
|
||||
@@ -105,15 +103,14 @@ def logged_cmd(log_file: Optional[TextIO], *vargs: str) -> str:
|
||||
raise err
|
||||
|
||||
|
||||
def match_owner(
|
||||
recordA: Optional[AttrDict], *records: Optional[AttrDict]
|
||||
) -> Optional[str]:
|
||||
def match_owner(recordA: AttrDict | None, *records: AttrDict | None) -> str | None:
|
||||
if not recordA or not recordA.owners:
|
||||
return None
|
||||
for owner in recordA.owners:
|
||||
for otherRecord in records:
|
||||
if otherRecord and otherRecord.owners and owner in otherRecord.owners:
|
||||
return owner
|
||||
result: str | None = owner
|
||||
return result
|
||||
return None
|
||||
|
||||
|
||||
@@ -147,9 +144,7 @@ class LaconicRegistryClient:
|
||||
return self.cache["whoami"]
|
||||
|
||||
args = ["laconic", "-c", self.config_file, "registry", "account", "get"]
|
||||
results = [
|
||||
AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r
|
||||
]
|
||||
results = [AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r]
|
||||
|
||||
if len(results):
|
||||
self.cache["whoami"] = results[0]
|
||||
@@ -178,9 +173,7 @@ class LaconicRegistryClient:
|
||||
"--address",
|
||||
address,
|
||||
]
|
||||
results = [
|
||||
AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r
|
||||
]
|
||||
results = [AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r]
|
||||
if len(results):
|
||||
self.cache["accounts"][address] = results[0]
|
||||
return results[0]
|
||||
@@ -203,9 +196,7 @@ class LaconicRegistryClient:
|
||||
"--id",
|
||||
id,
|
||||
]
|
||||
results = [
|
||||
AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r
|
||||
]
|
||||
results = [AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r]
|
||||
self._add_to_cache(results)
|
||||
if len(results):
|
||||
return results[0]
|
||||
@@ -216,9 +207,7 @@ class LaconicRegistryClient:
|
||||
|
||||
def list_bonds(self):
|
||||
args = ["laconic", "-c", self.config_file, "registry", "bond", "list"]
|
||||
results = [
|
||||
AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r
|
||||
]
|
||||
results = [AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r]
|
||||
self._add_to_cache(results)
|
||||
return results
|
||||
|
||||
@@ -232,12 +221,10 @@ class LaconicRegistryClient:
|
||||
|
||||
if criteria:
|
||||
for k, v in criteria.items():
|
||||
args.append("--%s" % k)
|
||||
args.append(f"--{k}")
|
||||
args.append(str(v))
|
||||
|
||||
results = [
|
||||
AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r
|
||||
]
|
||||
results = [AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r]
|
||||
|
||||
# Most recent records first
|
||||
results.sort(key=lambda r: r.createTime or "")
|
||||
@@ -246,7 +233,7 @@ class LaconicRegistryClient:
|
||||
|
||||
return results
|
||||
|
||||
def _add_to_cache(self, records: List[AttrDict]) -> None:
|
||||
def _add_to_cache(self, records: list[AttrDict]) -> None:
|
||||
if not records:
|
||||
return
|
||||
|
||||
@@ -271,9 +258,7 @@ class LaconicRegistryClient:
|
||||
|
||||
args = ["laconic", "-c", self.config_file, "registry", "name", "resolve", name]
|
||||
|
||||
parsed = [
|
||||
AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r
|
||||
]
|
||||
parsed = [AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r]
|
||||
if parsed:
|
||||
self._add_to_cache(parsed)
|
||||
return parsed[0]
|
||||
@@ -303,9 +288,7 @@ class LaconicRegistryClient:
|
||||
name_or_id,
|
||||
]
|
||||
|
||||
parsed = [
|
||||
AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r
|
||||
]
|
||||
parsed = [AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r]
|
||||
if len(parsed):
|
||||
self._add_to_cache(parsed)
|
||||
return parsed[0]
|
||||
@@ -356,9 +339,7 @@ class LaconicRegistryClient:
|
||||
|
||||
results = None
|
||||
try:
|
||||
results = [
|
||||
AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r
|
||||
]
|
||||
results = [AttrDict(r) for r in json.loads(logged_cmd(self.log_file, *args)) if r]
|
||||
except: # noqa: E722
|
||||
pass
|
||||
|
||||
@@ -422,7 +403,7 @@ class LaconicRegistryClient:
|
||||
record_file = open(record_fname, "w")
|
||||
yaml.dump(record, record_file)
|
||||
record_file.close()
|
||||
print(open(record_fname, "r").read(), file=self.log_file)
|
||||
print(open(record_fname).read(), file=self.log_file)
|
||||
|
||||
new_record_id = json.loads(
|
||||
logged_cmd(
|
||||
@@ -573,10 +554,10 @@ def determine_base_container(clone_dir, app_type="webapp"):
|
||||
|
||||
|
||||
def build_container_image(
|
||||
app_record: Optional[AttrDict],
|
||||
app_record: AttrDict | None,
|
||||
tag: str,
|
||||
extra_build_args: Optional[List[str]] = None,
|
||||
logger: Optional[TimedLogger] = None,
|
||||
extra_build_args: list[str] | None = None,
|
||||
logger: TimedLogger | None = None,
|
||||
) -> None:
|
||||
if app_record is None:
|
||||
raise ValueError("app_record cannot be None")
|
||||
@@ -649,9 +630,7 @@ def build_container_image(
|
||||
)
|
||||
result.check_returncode()
|
||||
|
||||
base_container = determine_base_container(
|
||||
clone_dir, app_record.attributes.app_type
|
||||
)
|
||||
base_container = determine_base_container(clone_dir, app_record.attributes.app_type)
|
||||
|
||||
if logger:
|
||||
logger.log("Building webapp ...")
|
||||
@@ -727,14 +706,12 @@ def publish_deployment(
|
||||
if not deploy_record:
|
||||
deploy_ver = "0.0.1"
|
||||
else:
|
||||
deploy_ver = "0.0.%d" % (
|
||||
int(deploy_record.attributes.version.split(".")[-1]) + 1
|
||||
)
|
||||
deploy_ver = f"0.0.{int(deploy_record.attributes.version.split('.')[-1]) + 1}"
|
||||
|
||||
if not dns_record:
|
||||
dns_ver = "0.0.1"
|
||||
else:
|
||||
dns_ver = "0.0.%d" % (int(dns_record.attributes.version.split(".")[-1]) + 1)
|
||||
dns_ver = f"0.0.{int(dns_record.attributes.version.split('.')[-1]) + 1}"
|
||||
|
||||
spec = yaml.full_load(open(os.path.join(deployment_dir, "spec.yml")))
|
||||
fqdn = spec["network"]["http-proxy"][0]["host-name"]
|
||||
@@ -779,13 +756,9 @@ def publish_deployment(
|
||||
|
||||
# Set auction or payment id from request
|
||||
if app_deployment_request.attributes.auction:
|
||||
new_deployment_record["record"][
|
||||
"auction"
|
||||
] = app_deployment_request.attributes.auction
|
||||
new_deployment_record["record"]["auction"] = app_deployment_request.attributes.auction
|
||||
elif app_deployment_request.attributes.payment:
|
||||
new_deployment_record["record"][
|
||||
"payment"
|
||||
] = app_deployment_request.attributes.payment
|
||||
new_deployment_record["record"]["payment"] = app_deployment_request.attributes.payment
|
||||
|
||||
if webapp_deployer_record:
|
||||
new_deployment_record["record"]["deployer"] = webapp_deployer_record.names[0]
|
||||
@@ -799,9 +772,7 @@ def publish_deployment(
|
||||
def hostname_for_deployment_request(app_deployment_request, laconic):
|
||||
dns_name = app_deployment_request.attributes.dns
|
||||
if not dns_name:
|
||||
app = laconic.get_record(
|
||||
app_deployment_request.attributes.application, require=True
|
||||
)
|
||||
app = laconic.get_record(app_deployment_request.attributes.application, require=True)
|
||||
dns_name = generate_hostname_for_app(app)
|
||||
elif dns_name.startswith("lrn://"):
|
||||
record = laconic.get_record(dns_name, require=True)
|
||||
@@ -818,7 +789,7 @@ def generate_hostname_for_app(app):
|
||||
m.update(app.attributes.repository[0].encode())
|
||||
else:
|
||||
m.update(app.attributes.repository.encode())
|
||||
return "%s-%s" % (last_part, m.hexdigest()[0:10])
|
||||
return f"{last_part}-{m.hexdigest()[0:10]}"
|
||||
|
||||
|
||||
def skip_by_tag(r, include_tags, exclude_tags):
|
||||
@@ -881,16 +852,13 @@ def confirm_payment(
|
||||
pay_denom = "".join([i for i in tx_amount if not i.isdigit()])
|
||||
if pay_denom != "alnt":
|
||||
logger.log(
|
||||
f"{record.id}: {pay_denom} in tx {tx.hash} is not an expected "
|
||||
"payment denomination"
|
||||
f"{record.id}: {pay_denom} in tx {tx.hash} is not an expected " "payment denomination"
|
||||
)
|
||||
return False
|
||||
|
||||
pay_amount = int("".join([i for i in tx_amount if i.isdigit()]) or "0")
|
||||
if pay_amount < min_amount:
|
||||
logger.log(
|
||||
f"{record.id}: payment amount {tx.amount} is less than minimum {min_amount}"
|
||||
)
|
||||
logger.log(f"{record.id}: payment amount {tx.amount} is less than minimum {min_amount}")
|
||||
return False
|
||||
|
||||
# Check if the payment was already used on a deployment
|
||||
@@ -914,9 +882,7 @@ def confirm_payment(
|
||||
{"deployer": record.attributes.deployer, "payment": tx.hash}, all=True
|
||||
)
|
||||
if len(used):
|
||||
logger.log(
|
||||
f"{record.id}: payment {tx.hash} already used on deployment removal {used}"
|
||||
)
|
||||
logger.log(f"{record.id}: payment {tx.hash} already used on deployment removal {used}")
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -940,9 +906,7 @@ def confirm_auction(
|
||||
|
||||
# Cross check app against application in the auction record
|
||||
requested_app = laconic.get_record(record.attributes.application, require=True)
|
||||
auction_app = laconic.get_record(
|
||||
auction_records_by_id[0].attributes.application, require=True
|
||||
)
|
||||
auction_app = laconic.get_record(auction_records_by_id[0].attributes.application, require=True)
|
||||
requested_app_id = requested_app.id if requested_app else None
|
||||
auction_app_id = auction_app.id if auction_app else None
|
||||
if requested_app_id != auction_app_id:
|
||||
|
||||
Reference in New Issue
Block a user