2023-11-21 03:23:55 +00:00
|
|
|
# Copyright © 2023 Vulcanize
|
|
|
|
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU Affero General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU Affero General Public License for more details.
|
|
|
|
|
|
|
|
# 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/>.
|
|
|
|
|
|
|
|
from typing import Set
|
|
|
|
|
|
|
|
from python_on_whales import DockerClient
|
|
|
|
|
|
|
|
from stack_orchestrator import constants
|
|
|
|
from stack_orchestrator.opts import opts
|
|
|
|
from stack_orchestrator.deploy.deployment_context import DeploymentContext
|
|
|
|
from stack_orchestrator.deploy.deploy_types import DeployCommandContext
|
|
|
|
from stack_orchestrator.deploy.deploy_util import images_for_deployment
|
|
|
|
|
|
|
|
|
|
|
|
def _image_needs_pushed(image: str):
|
|
|
|
# TODO: this needs to be more intelligent
|
|
|
|
return image.endswith(":local")
|
|
|
|
|
|
|
|
|
2024-06-13 03:26:58 +00:00
|
|
|
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)
|
|
|
|
image_name_with_version = major_parts[1] if 2 == len(major_parts) else major_parts[0]
|
|
|
|
(image_name, image_version) = image_name_with_version.split(":")
|
|
|
|
if image_version == "local":
|
|
|
|
return f"{remote_repo_url}/{image_name}:deploy"
|
|
|
|
else:
|
|
|
|
return image
|
|
|
|
|
|
|
|
|
|
|
|
# Note: do not add any calls this function
|
2024-02-24 03:22:49 +00:00
|
|
|
def remote_image_exists(remote_repo_url: str, local_tag: str):
|
|
|
|
docker = DockerClient()
|
|
|
|
try:
|
2024-06-13 03:26:58 +00:00
|
|
|
remote_tag = _remote_tag_for_image(local_tag, remote_repo_url)
|
2024-02-24 03:22:49 +00:00
|
|
|
result = docker.manifest.inspect(remote_tag)
|
|
|
|
return True if result else False
|
|
|
|
except Exception: # noqa: E722
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
2024-06-13 03:26:58 +00:00
|
|
|
# Note: do not add any calls this function
|
2024-02-24 03:22:49 +00:00
|
|
|
def add_tags_to_image(remote_repo_url: str, local_tag: str, *additional_tags):
|
|
|
|
if not additional_tags:
|
|
|
|
return
|
|
|
|
|
|
|
|
if not remote_image_exists(remote_repo_url, local_tag):
|
|
|
|
raise Exception(f"{local_tag} does not exist in {remote_repo_url}")
|
|
|
|
|
|
|
|
docker = DockerClient()
|
2024-06-13 03:26:58 +00:00
|
|
|
remote_tag = _remote_tag_for_image(local_tag, remote_repo_url)
|
|
|
|
new_remote_tags = [_remote_tag_for_image(tag, remote_repo_url) for tag in additional_tags]
|
2024-02-24 03:22:49 +00:00
|
|
|
docker.buildx.imagetools.create(sources=[remote_tag], tags=new_remote_tags)
|
|
|
|
|
|
|
|
|
2024-06-13 03:26:58 +00:00
|
|
|
def remote_tag_for_image_unique(image: str, remote_repo_url: str, deployment_id: str):
|
2023-11-21 03:23:55 +00:00
|
|
|
# Turns image tags of the form: foo/bar:local into remote.repo/org/bar:deploy
|
2024-01-31 05:09:48 +00:00
|
|
|
major_parts = image.split("/", 2)
|
|
|
|
image_name_with_version = major_parts[1] if 2 == len(major_parts) else major_parts[0]
|
2023-11-21 03:23:55 +00:00
|
|
|
(image_name, image_version) = image_name_with_version.split(":")
|
|
|
|
if image_version == "local":
|
2024-06-13 03:26:58 +00:00
|
|
|
# Salt the tag with part of the deployment id to make it unique to this deployment
|
2024-06-13 14:31:45 +00:00
|
|
|
deployment_tag = deployment_id[-8:]
|
2024-06-13 03:26:58 +00:00
|
|
|
return f"{remote_repo_url}/{image_name}:deploy-{deployment_tag}"
|
2023-11-21 03:23:55 +00:00
|
|
|
else:
|
|
|
|
return image
|
|
|
|
|
|
|
|
|
|
|
|
# TODO: needs lots of error handling
|
|
|
|
def push_images_operation(command_context: DeployCommandContext, deployment_context: DeploymentContext):
|
|
|
|
# Get the list of images for the stack
|
|
|
|
cluster_context = command_context.cluster_context
|
|
|
|
images: Set[str] = images_for_deployment(cluster_context.compose_files)
|
|
|
|
# Tag the images for the remote repo
|
2024-02-14 21:45:01 +00:00
|
|
|
remote_repo_url = deployment_context.spec.obj[constants.image_registry_key]
|
2023-11-21 03:23:55 +00:00
|
|
|
docker = DockerClient()
|
|
|
|
for image in images:
|
|
|
|
if _image_needs_pushed(image):
|
2024-06-13 03:26:58 +00:00
|
|
|
remote_tag = remote_tag_for_image_unique(image, remote_repo_url, deployment_context.id)
|
2023-11-21 03:23:55 +00:00
|
|
|
if opts.o.verbose:
|
|
|
|
print(f"Tagging {image} to {remote_tag}")
|
|
|
|
docker.image.tag(image, remote_tag)
|
|
|
|
# Run docker push commands to upload
|
|
|
|
for image in images:
|
|
|
|
if _image_needs_pushed(image):
|
2024-06-13 03:26:58 +00:00
|
|
|
remote_tag = remote_tag_for_image_unique(image, remote_repo_url, deployment_context.id)
|
2023-11-21 03:23:55 +00:00
|
|
|
if opts.o.verbose:
|
|
|
|
print(f"Pushing image {remote_tag}")
|
|
|
|
docker.image.push(remote_tag)
|