Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 65 additions & 43 deletions mirror/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ def _replace_colon_except_last_segment(input_string):
output_string = "/".join(segments)
return output_string

def _mirror_image_impl(ctx):
# Common implementation for mirror_image and mirror_image_test
# Uses the following ctx attributes: src_image, digest, dst, dst_prefix
# Returns the src_image, digest, and dst_without_hash
def _impl_common(ctx):
digest = ctx.attr.digest
src_image = ctx.attr.src_image
v = src_image.split("@", 1)
Expand Down Expand Up @@ -39,6 +42,11 @@ def _mirror_image_impl(ctx):
dst_prefix = ctx.expand_make_variables("dst_prefix", ctx.attr.dst_prefix, {})
dst_without_hash = dst_prefix.strip("/") + "/" + src_repository

return src_image, digest, dst_without_hash

def _mirror_image_impl(ctx):
src_image, digest, dst_without_hash = _impl_common(ctx)

digest_file = ctx.actions.declare_file(ctx.label.name + ".digest")
ctx.actions.write(
output = digest_file,
Expand Down Expand Up @@ -81,9 +89,6 @@ mirror_image_rule = rule(
mandatory = True,
doc = "The image to mirror",
),
"image_name": attr.string(
doc = "The name that could be referred in manifests. This field is deprecated and unused.",
),
"digest": attr.string(
mandatory = False,
doc = "The digest of the image. If not provided, it will be extracted from the src_image.",
Expand Down Expand Up @@ -114,46 +119,63 @@ Implements GitopsPushInfo and K8sPushInfo providers so the returned image can be
""",
)

def validate_image_test(name, image, digest, tags = [], **kwargs):
"""
Create a test that validates the image existance using crane validate.
Image tag will be ignored if provided and only the digest will be used.
if digest is provided as a part of the image, it will be used.
It is an error to provide both digest and image with digest if they do not match.
"""
src_image = image
v = src_image.split("@", 1)
s = v[0]
if len(v) > 1:
# If the image has a digest, use that.
if digest and v[1] != digest:
fail("digest mismatch: %s != %s" % (v[1], digest))
digest = v[1]
else:
# If the image does not have a digest, use the one provided.
src_image = s + "@" + digest

if not digest:
fail("digest must be provided as an attribute to mirror_image or in the src_image")
def _validate_mirror_impl(ctx):
src_image, digest, dst_without_hash = _impl_common(ctx)

native.sh_test(
name = name,
size = "small",
srcs = ["@rules_gitops//mirror:validate_image.sh"],
data = [
"@rules_gitops//vendor/github.com/google/go-containerregistry/cmd/crane:crane",
],
args = [
src_image,
],
tags = ["requires-network"] + tags,
env = {
"CRANE_BIN": "$(location @rules_gitops//vendor/github.com/google/go-containerregistry/cmd/crane:crane)",
ctx.actions.expand_template(
template = ctx.file._validate_image_script,
output = ctx.outputs.executable,
substitutions = {
"{crane_tool}": ctx.executable.crane_tool.short_path,
"{src_image}": src_image,
"{digest}": digest,
"{dst_image}": dst_without_hash,
},
**kwargs
is_executable = True,
)

def mirror_image(name, src_image, digest, tags = [], **kwargs):
visibility = kwargs.pop("visibility", None)
mirror_image_rule(name = name, src_image = src_image, digest = digest, tags = tags, visibility = visibility, **kwargs)
validate_image_test(name = name + "_validate_src", image = src_image, digest = digest, visibility = visibility, tags = tags)
runfiles = ctx.runfiles(files = [ctx.file._validate_image_script]).merge(ctx.attr.crane_tool[DefaultInfo].default_runfiles)

return DefaultInfo(
runfiles = runfiles,
executable = ctx.outputs.executable,
)

validate_mirror_test = rule(
implementation = _validate_mirror_impl,
test = True,
attrs = {
"src_image": attr.string(
mandatory = True,
doc = "The image to mirror",
),
"digest": attr.string(
mandatory = False,
doc = "The digest of the image. If not provided, it will be extracted from the src_image.",
),
"dst_prefix": attr.string(
doc = "The prefix of the destination image, should include the registry and repository. Either dst_prefix or dst_image must be specified.",
),
"dst": attr.string(
doc = "The destination image location, should include the registry and repository. Either dst_prefix or dst_image must be specified.",
),
"crane_tool": attr.label(
default = Label("//vendor/github.com/google/go-containerregistry/cmd/crane:crane"),
executable = True,
cfg = "exec",
),
"_validate_image_script": attr.label(
default = ":validate_image.sh",
allow_single_file = True,
),
},
executable = True,
doc = """Validate a mirrored image. It checks if at least one of remote or local image exists.
""",
)

def mirror_image(name, image_name = None, push_timeout = "30s", **kwargs):
if image_name:
fail("image_name is deprecated and unused")
mirror_image_rule(name = name, push_timeout = push_timeout, **kwargs)
validate_mirror_test(name = name + "_validate_src", **kwargs)
2 changes: 1 addition & 1 deletion mirror/mirror_image.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/bash -x
set -eu

function guess_runfiles() {
Expand Down
22 changes: 22 additions & 0 deletions mirror/tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,25 @@ sh_test(
},
tags = ["exclusive"], # this test starts a registry on fixed port 1338
)

sh_test(
name = "image_mirror_verify_test",
srcs = ["image_mirror_test.sh"],
data = [
":image_mirror",
":image_mirror_validate_src",
":push_image",
"//vendor/github.com/google/go-containerregistry/cmd/crane",
"//vendor/github.com/google/go-containerregistry/cmd/registry",
],
env = {
"LOCAL": "localhost:1338/mirror/localhost1338/image@sha256:b812c0570a7c369b2863c64e22760dc1b1dbc025a739f02db376bac62862f4cc",
"REMOTE": "localhost:1338/image@sha256:b812c0570a7c369b2863c64e22760dc1b1dbc025a739f02db376bac62862f4cc",
"PUSH_IMAGE": "$(location :push_image)",
"IMAGE_MIRROR": "$(location :image_mirror)",
"IMAGE_MIRROR_VALIDATE_SRC": "$(location :image_mirror_validate_src)",
"CRANE_BIN": "$(location //vendor/github.com/google/go-containerregistry/cmd/crane)",
"REGISTRY_BIN": "$(location //vendor/github.com/google/go-containerregistry/cmd/registry)",
},
tags = ["exclusive"], # this test starts a registry on fixed port 1338
)
111 changes: 111 additions & 0 deletions mirror/tests/image_mirror_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#!/bin/bash -x
#
# This test runs the script passed as the first argument and verifies the image is pushed to the registry
#
${REGISTRY_BIN}&
registry_pid=$!
trap "kill -9 $registry_pid" EXIT
Comment on lines +5 to +7

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script starts a background process for ${REGISTRY_BIN} and sets a trap to kill it on exit. However, using kill -9 is generally discouraged because it does not allow the process to clean up. A gentler signal like TERM should be used first.

Recommendation: Replace kill -9 $registry_pid with kill $registry_pid to allow the process to terminate gracefully. If the process does not terminate, then consider using kill -9 as a fallback.


echo verifying image $REMOTE does not exist
${CRANE_BIN} validate -v --fast --remote $REMOTE
if [ $? -eq 0 ]; then
echo "Image $REMOTE should not exist"
exit 1
fi

echo verifying image $LOCAL does not exist
${CRANE_BIN} validate -v --fast --remote $LOCAL
if [ $? -eq 0 ]; then
echo "Image $LOCAL should not exist"
exit 1
fi

#test should fail before pushing the image (no src, no dst)
echo verifying mirror image validation fails
${IMAGE_MIRROR_VALIDATE_SRC}
if [ $? -eq 0 ]; then
echo "Image verification should fail"
exit 1
fi

echo pushing image $SRC_IMAGE
${PUSH_IMAGE}

echo verifying image $REMOTE exists
${CRANE_BIN} validate -v --fast --remote $REMOTE
if [ $? -ne 0 ]; then
echo "Image $REMOTE should exist"
exit 1
fi

echo verifying image $LOCAL does not exist
${CRANE_BIN} validate -v --fast --remote $LOCAL
if [ $? -eq 0 ]; then
echo "Image $LOCAL should not exist"
exit 1
fi

#test should succeed with src image only
echo verifying mirror image validation fails
${IMAGE_MIRROR_VALIDATE_SRC}
if [ $? -ne 0 ]; then
echo "Image verification should succeed"
exit 1
fi

echo running image mirror
${IMAGE_MIRROR}
if [ $? -ne 0 ]; then
echo "Image mirroring should succeed"
exit 1
fi

echo verifying image $LOCAL exists
${CRANE_BIN} validate -v --fast --remote $LOCAL
if [ $? -ne 0 ]; then
echo "Image $LOCAL should exist"
exit 1
fi

echo verifying image $REMOTE exists
${CRANE_BIN} validate -v --fast --remote $REMOTE
if [ $? -ne 0 ]; then
echo "Image $REMOTE should exist"
exit 1
fi

#test should succeed with src and dst images
echo verifying mirror image validation succeeds
${IMAGE_MIRROR_VALIDATE_SRC}
if [ $? -ne 0 ]; then
echo "Image verification should succeed"
exit 1
fi

echo removing image $REMOTE
${CRANE_BIN} delete $REMOTE || exit 1

echo verifying image $REMOTE does not exist
${CRANE_BIN} validate -v --fast --remote $REMOTE
if [ $? -eq 0 ]; then
echo "Image $REMOTE should not exist"
exit 1
fi

echo verifying image $LOCAL exists
${CRANE_BIN} validate -v --fast --remote $LOCAL
if [ $? -ne 0 ]; then
echo "Image $LOCAL should exist"
exit 1
fi

#test should succeed with dst image only
echo verifying mirror image validation succeeds
${IMAGE_MIRROR_VALIDATE_SRC}
if [ $? -ne 0 ]; then
echo "Image verification should succeed"
exit 1
fi


# exit 1
21 changes: 19 additions & 2 deletions mirror/validate_image.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
#!/bin/bash
echo "Validating image $1"
$CRANE_BIN validate -v --fast --remote $1
REMOTE="{src_image}"
LOCAL="{dst_image}@{digest}"
echo "Validating images ${REMOTE} and ${LOCAL}"
{crane_tool} validate -v --fast --remote ${REMOTE}
if [ $? -eq 0 ]; then
echo "Image ${REMOTE} exist"
exit 0
fi

echo "Image ${REMOTE} does not exist, checking ${LOCAL}"

{crane_tool} validate -v --fast --remote ${LOCAL}
if [ $? -eq 0 ]; then
echo "Image ${LOCAL} exist"
exit 0
fi

echo "Image ${LOCAL} does not exist"
exit 1