diff --git a/stack_orchestrator/deploy/images.py b/stack_orchestrator/deploy/images.py index 7ddcca33..8bfcc794 100644 --- a/stack_orchestrator/deploy/images.py +++ b/stack_orchestrator/deploy/images.py @@ -29,6 +29,15 @@ def _image_needs_pushed(image: str): return image.endswith(":local") +def remote_image_exists(local_tag: str, remote_repo_url: str): + docker = DockerClient() + try: + result = docker.manifest.inspect(remote_tag_for_image(local_tag, remote_repo_url)) + return True if result else False + except: + return False + + def remote_tag_for_image(image: str, remote_repo_url: str): # Turns image tags of the form: foo/bar:local into remote.repo/org/bar:deploy major_parts = image.split("/", 2) diff --git a/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py b/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py index 15e89d9e..e20d0e2d 100644 --- a/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py +++ b/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py @@ -24,6 +24,8 @@ import uuid import click +from stack_orchestrator import constants +from stack_orchestrator.deploy.images import remote_image_exists from stack_orchestrator.deploy.webapp import deploy_webapp from stack_orchestrator.deploy.webapp.util import (LaconicRegistryClient, build_container_image, push_container_image, @@ -43,7 +45,8 @@ def process_app_deployment_request( deployment_parent_dir, kube_config, image_registry, - log_file=None + force_rebuild = False, + log_file = None ): # 1. look up application app = laconic.get_record(app_deployment_request.attributes.application, require=True) @@ -91,7 +94,7 @@ def process_app_deployment_request( deployment_record = laconic.get_record(app_deployment_crn) deployment_dir = os.path.join(deployment_parent_dir, fqdn) deployment_config_file = os.path.join(deployment_dir, "config.env") - deployment_container_tag = "laconic-webapp/%s:local" % hashlib.md5(deployment_dir.encode()).hexdigest() + deployment_container_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: @@ -106,11 +109,14 @@ def process_app_deployment_request( needs_k8s_deploy = False # 6. build container (if needed) if not deployment_record or deployment_record.attributes.application != app.id: - # TODO: pull from request - extra_build_args = [] - build_container_image(app, deployment_container_tag, extra_build_args, log_file) - push_container_image(deployment_dir, log_file) - needs_k8s_deploy = True + print(ctx) + # check if the image already exists + if force_rebuild or not remote_image_exists(deployment_container_tag, ctx.spec[constants.image_registry_key]): + # TODO: pull from request + extra_build_args = [] + build_container_image(app, deployment_container_tag, extra_build_args, log_file) + push_container_image(deployment_dir, log_file) + needs_k8s_deploy = True # 7. update config (if needed) if not deployment_record or file_hash(deployment_config_file) != deployment_record.attributes.meta.config: @@ -171,12 +177,13 @@ def dump_known_requests(filename, requests, status="SEEN"): @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).", default="") @click.option("--exclude-tags", 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("--log-dir", help="Output build/deployment logs to directory.", default=None) @click.pass_context def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_dir, # noqa: C901 request_id, discover, state_file, only_update_state, dns_suffix, record_namespace_dns, record_namespace_deployments, dry_run, - include_tags, exclude_tags, log_dir): + include_tags, exclude_tags, force_rebuild, log_dir): if request_id and discover: print("Cannot specify both --request-id and --discover", file=sys.stderr) sys.exit(2) @@ -306,6 +313,7 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_ os.path.abspath(deployment_parent_dir), kube_config, image_registry, + force_rebuild, run_log_file ) status = "DEPLOYED"