Skip to content

Commit

Permalink
Merge pull request #170 from flyingcircusio/oci-verify-speed
Browse files Browse the repository at this point in the history
OCI: cache validation result during deployment.
  • Loading branch information
zagy authored May 8, 2024
2 parents c1f7179 + 2988bb8 commit ae9e7af
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 35 deletions.
3 changes: 3 additions & 0 deletions CHANGES.d/20240503_141117_cz_oci_verify_speed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- OCI: cache validation result during deployment.

Caching results speeds up deployments where multiple containers with the same image are deployed.
106 changes: 71 additions & 35 deletions src/batou_ext/oci.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ class Container(Component):
"image": "mysql",
}

# cache spanning multiple components deploying the same container
_remote_manifest_cache = {}

def configure(self):
if (
self.registry_user or self.registry_password
Expand Down Expand Up @@ -136,52 +139,85 @@ def verify(self):
self.assert_no_changes()
self.envfile.assert_no_changes()

if self.registry_address:
logintxt, _ = self.cmd(
self.expand(
dedent(
"""\
docker login \\
{%- if component.registry_user and component.registry_password %}
-u {{component.registry_user}} \\
-p {{component.registry_password}} \\
{%- endif %}
{{component.registry_address}}
"""
)
)
)
valid = False

local_digest, stderr = self.cmd(
container_image_id, stderr = self.cmd(
dedent(
"""\
docker container insepct {{component.container_name}} \
| jq -r '.[0].Image'
"""
)
)
local_image_id, stderr = self.cmd(
dedent(
"""\
docker image inspect {{component.image}}:{{component.version}} \
| jq -r 'first | .RepoDigests | first | split("@") | last' \
| jq -r '.[0].Id' \
|| echo image not available locally
"""
)
)
try:
self.cmd(
"docker manifest inspect"
f" {self.image}:{self.version}@{local_digest}"
if local_image_id != container_image_id:
# If the container is not running the image we expect, we need to
# restart it. If its the same, we need to dig further
error = (
"Container is running different image. "
"({container_image_id} vs. {local_image_id})"
)
except CmdExecutionError as e:
valid = False
error = e.stderr
if error.startswith("unsupported manifest format"): # gitlab
batou.output.annotate(error, debug=True)
error = error[:50]
else:
valid = True

# `docker manifest inspect` silently raises an error, returns code 0
# when unathorized
if stderr == "unauthorized":
raise RuntimeError(
"Wrong credentials for remote container registry"
local_digest, stderr = self.cmd(
dedent(
"""\
docker image inspect {{component.image}}:{{component.version}} \
| jq -r 'first | .RepoDigests | first | split("@") | last' \
|| echo image not available locally
"""
)
)

image_ident = f"{self.image}:{self.version}@{local_digest}"
try:
valid = self._remote_manifest_cache[image_ident]
error = "(cached)"
except KeyError:
if self.registry_address:
logintxt, _ = self.cmd(
self.expand(
dedent(
"""\
docker login \\
{%- if component.registry_user and component.registry_password %}
-u {{component.registry_user}} \\
-p {{component.registry_password}} \\
{%- endif %}
{{component.registry_address}}
"""
)
)
)

try:
stdout, stderr = self.cmd(
f"docker manifest inspect {image_ident}"
)
except CmdExecutionError as e:
valid = False
error = e.stderr
if error.startswith(
"unsupported manifest format"
): # gitlab
batou.output.annotate(error, debug=True)
error = error[:50]
else:
# `docker manifest inspect` silently raises an error,
# returns code 0 when unathorized
if stderr == "unauthorized":
raise RuntimeError(
"Wrong credentials for remote container registry"
)
valid = True
self._remote_manifest_cache[image_ident] = valid

if not valid:
self.log("Update due digest verification error: %r", error)
raise UpdateNeeded()
Expand Down

0 comments on commit ae9e7af

Please sign in to comment.