@@ -10,7 +10,10 @@ def _replace_colon_except_last_segment(input_string):
1010 output_string = "/" .join (segments )
1111 return output_string
1212
13- def _mirror_image_impl (ctx ):
13+ # Common implementation for mirror_image and mirror_image_test
14+ # Uses the following ctx attributes: src_image, digest, dst, dst_prefix
15+ # Returns the src_image, digest, and dst_without_hash
16+ def _impl_common (ctx ):
1417 digest = ctx .attr .digest
1518 src_image = ctx .attr .src_image
1619 v = src_image .split ("@" , 1 )
@@ -39,6 +42,11 @@ def _mirror_image_impl(ctx):
3942 dst_prefix = ctx .expand_make_variables ("dst_prefix" , ctx .attr .dst_prefix , {})
4043 dst_without_hash = dst_prefix .strip ("/" ) + "/" + src_repository
4144
45+ return src_image , digest , dst_without_hash
46+
47+ def _mirror_image_impl (ctx ):
48+ src_image , digest , dst_without_hash = _impl_common (ctx )
49+
4250 digest_file = ctx .actions .declare_file (ctx .label .name + ".digest" )
4351 ctx .actions .write (
4452 output = digest_file ,
@@ -81,9 +89,6 @@ mirror_image_rule = rule(
8189 mandatory = True ,
8290 doc = "The image to mirror" ,
8391 ),
84- "image_name" : attr .string (
85- doc = "The name that could be referred in manifests. This field is deprecated and unused." ,
86- ),
8792 "digest" : attr .string (
8893 mandatory = False ,
8994 doc = "The digest of the image. If not provided, it will be extracted from the src_image." ,
@@ -114,46 +119,63 @@ Implements GitopsPushInfo and K8sPushInfo providers so the returned image can be
114119""" ,
115120)
116121
117- def validate_image_test (name , image , digest , tags = [], ** kwargs ):
118- """
119- Create a test that validates the image existance using crane validate.
120- Image tag will be ignored if provided and only the digest will be used.
121- if digest is provided as a part of the image, it will be used.
122- It is an error to provide both digest and image with digest if they do not match.
123- """
124- src_image = image
125- v = src_image .split ("@" , 1 )
126- s = v [0 ]
127- if len (v ) > 1 :
128- # If the image has a digest, use that.
129- if digest and v [1 ] != digest :
130- fail ("digest mismatch: %s != %s" % (v [1 ], digest ))
131- digest = v [1 ]
132- else :
133- # If the image does not have a digest, use the one provided.
134- src_image = s + "@" + digest
135-
136- if not digest :
137- fail ("digest must be provided as an attribute to mirror_image or in the src_image" )
122+ def _validate_mirror_impl (ctx ):
123+ src_image , digest , dst_without_hash = _impl_common (ctx )
138124
139- native .sh_test (
140- name = name ,
141- size = "small" ,
142- srcs = ["@rules_gitops//mirror:validate_image.sh" ],
143- data = [
144- "@rules_gitops//vendor/github.com/google/go-containerregistry/cmd/crane:crane" ,
145- ],
146- args = [
147- src_image ,
148- ],
149- tags = ["requires-network" ] + tags ,
150- env = {
151- "CRANE_BIN" : "$(location @rules_gitops//vendor/github.com/google/go-containerregistry/cmd/crane:crane)" ,
125+ ctx .actions .expand_template (
126+ template = ctx .file ._validate_image_script ,
127+ output = ctx .outputs .executable ,
128+ substitutions = {
129+ "{crane_tool}" : ctx .executable .crane_tool .short_path ,
130+ "{src_image}" : src_image ,
131+ "{digest}" : digest ,
132+ "{dst_image}" : dst_without_hash ,
152133 },
153- ** kwargs
134+ is_executable = True ,
154135 )
155136
156- def mirror_image (name , src_image , digest , tags = [], ** kwargs ):
157- visibility = kwargs .pop ("visibility" , None )
158- mirror_image_rule (name = name , src_image = src_image , digest = digest , tags = tags , visibility = visibility , ** kwargs )
159- validate_image_test (name = name + "_validate_src" , image = src_image , digest = digest , visibility = visibility , tags = tags )
137+ runfiles = ctx .runfiles (files = [ctx .file ._validate_image_script ]).merge (ctx .attr .crane_tool [DefaultInfo ].default_runfiles )
138+
139+ return DefaultInfo (
140+ runfiles = runfiles ,
141+ executable = ctx .outputs .executable ,
142+ )
143+
144+ validate_mirror_test = rule (
145+ implementation = _validate_mirror_impl ,
146+ test = True ,
147+ attrs = {
148+ "src_image" : attr .string (
149+ mandatory = True ,
150+ doc = "The image to mirror" ,
151+ ),
152+ "digest" : attr .string (
153+ mandatory = False ,
154+ doc = "The digest of the image. If not provided, it will be extracted from the src_image." ,
155+ ),
156+ "dst_prefix" : attr .string (
157+ doc = "The prefix of the destination image, should include the registry and repository. Either dst_prefix or dst_image must be specified." ,
158+ ),
159+ "dst" : attr .string (
160+ doc = "The destination image location, should include the registry and repository. Either dst_prefix or dst_image must be specified." ,
161+ ),
162+ "crane_tool" : attr .label (
163+ default = Label ("//vendor/github.com/google/go-containerregistry/cmd/crane:crane" ),
164+ executable = True ,
165+ cfg = "exec" ,
166+ ),
167+ "_validate_image_script" : attr .label (
168+ default = ":validate_image.sh" ,
169+ allow_single_file = True ,
170+ ),
171+ },
172+ executable = True ,
173+ doc = """Validate a mirrored image. It checks if at least one of remote or local image exists.
174+ """ ,
175+ )
176+
177+ def mirror_image (name , image_name = None , push_timeout = "30s" , ** kwargs ):
178+ if image_name :
179+ fail ("image_name is deprecated and unused" )
180+ mirror_image_rule (name = name , push_timeout = push_timeout , ** kwargs )
181+ validate_mirror_test (name = name + "_validate_src" , ** kwargs )
0 commit comments