1"""Implementation for rules_kustomize"""
2
3load("//hack/build/rules/container:push_info.bzl", "OCIPushInfo")
4load("@aspect_bazel_lib//lib:utils.bzl", "propagate_common_rule_attributes")
5
6KustomizeInfo = provider(
7 doc = "Contains information about the built Kustomization",
8 fields = {
9 "deps": "Depset of source files for dependencies of this Kustomization.",
10 "image_labels": "A list of pusher references from images",
11 "images": "Dict of image references present in the dependencies of this Kustomization keyed by their container_push targets.",
12 },
13)
14
15def _kustomization_impl(ctx):
16 kyaml = ctx.file.kustomization_yaml
17 runfiles = ctx.runfiles()
18
19 # Kustomized manifests
20 out = ctx.actions.declare_file("{0}_manifests.yaml".format(ctx.label.name))
21
22 # Script that will cat output of the manifests
23 printer = ctx.actions.declare_file(ctx.label.name)
24
25 # Instantiate container_push label:image ref dictionary to collect pairings
26 # found in the source files of the implementing kustomization and its deps
27 image_dict = {}
28
29 image_labels = []
30
31 # Collect images from deps
32 for dep in ctx.attr.deps:
33 if dep[KustomizeInfo].images:
34 dep_images = dep[KustomizeInfo].images
35 for image in dep_images:
36 if OCIPushInfo in image:
37 image_dict[image] = dep_images[image]
38 image_labels.append(dep_images[image])
39
40 # Collect images from the implementing kustomization
41 if ctx.attr.images:
42 for image in ctx.attr.images:
43 if OCIPushInfo in image:
44 image_dict[image] = ctx.attr.images[image]
45 image_labels.append(ctx.attr.images[image])
46
47 # Instantiate list of input files for go helper
48 input_files = [kyaml] + ctx.files.srcs
49
50 # Instantiate go helper flags and args
51 helper_args = ctx.actions.args()
52 helper_args.add_joined(["-src-kustomization", ctx.file.kustomization_yaml], join_with = "=")
53 helper_args.add_joined(["-out-manifests", out.path], join_with = "=")
54
55 # If any images are found, leverage OCIPushInfo for updated references.
56 if len(image_dict) > 0:
57 for image in image_dict:
58 # Add images entry to helper arguments as a tuple
59 existing_ref = image_dict.get(image)
60 helper_args.add_joined([existing_ref, image[OCIPushInfo].ref.path], join_with = "=")
61
62 # Include captured output with soruce kustomization.yaml for use in the go helper
63 input_files.append(image[OCIPushInfo].ref)
64
65 # Inputs for the go helper
66 input_deps = depset(
67 direct = input_files,
68 transitive = [dep[KustomizeInfo].deps for dep in ctx.attr.deps],
69 )
70
71 # Run go helper which modifies kustomization to include spec.images
72 ctx.actions.run(
73 inputs = input_deps,
74 outputs = [out],
75 arguments = [helper_args],
76 executable = ctx.executable._go_helper,
77 progress_message = "Substituting images for %s kustomization..." % ctx.label.name,
78 )
79
80 # Generate script to invoke all pushers & write manifests to stdout
81 # The script changes the directory to that which contains the kustomized manifests
82 ctx.actions.write(
83 output = printer,
84 content = """#!/usr/bin/env bash
85cd ../..
86echo \"$(cat {manifests})\"
87 """.format(
88 manifests = out.basename,
89 ),
90 is_executable = True,
91 )
92
93 return [
94 DefaultInfo(
95 files = depset([out]),
96 executable = printer,
97 runfiles = runfiles,
98 ),
99 KustomizeInfo(
100 deps = input_deps,
101 images = ctx.attr.images if ctx.attr.images else {},
102 image_labels = image_dict,
103 ),
104 ]
105
106_kustomization = rule(
107 implementation = _kustomization_impl,
108 attrs = {
109 "deps": attr.label_list(
110 doc = "Direct dependencies of the Kustomization",
111 providers = [KustomizeInfo],
112 ),
113 "images": attr.label_keyed_string_dict(
114 doc = "Image references present in the srcs of this Kustomization paired with their container_push targets",
115 providers = [OCIPushInfo],
116 ),
117 "kustomization_yaml": attr.label(
118 doc = "Name of the file containing the Kustomization",
119 allow_single_file = True,
120 mandatory = True,
121 ),
122 "srcs": attr.label_list(
123 doc = "YAML files included in this Kustomization",
124 allow_files = True,
125 ),
126 "_go_helper": attr.label(
127 default = Label("//hack/build/rules/kustomize:kustomize_bin"),
128 executable = True,
129 cfg = "exec",
130 ),
131 "_kustomize": attr.label(
132 default = Label("//hack/tools:kustomize"),
133 allow_single_file = True,
134 executable = True,
135 cfg = "exec",
136 ),
137 },
138 executable = True,
139)
140
141def _kustomization_push_impl(ctx):
142 # If any images are found, leverage OCIPushInfo for updated references.
143 image_labels = ctx.attr.kustomization[KustomizeInfo].image_labels
144
145 pushers = []
146
147 runfiles = ctx.runfiles()
148 for image in image_labels:
149 runfiles = runfiles.merge(image[OCIPushInfo].runfiles)
150 pushers.append(image[OCIPushInfo].pusher)
151
152 # Join all pusher executables collected
153 push_commands = "\n".join([pusher.short_path for pusher in pushers])
154
155 # Script that will invoke any pushers
156 pusher = ctx.actions.declare_file(ctx.label.name)
157
158 # Generate script to invoke all pushers & write manifests to stdout
159 # The script changes the directory to that which contains the kustomized manifests
160 ctx.actions.write(
161 output = pusher,
162 content = """#!/usr/bin/env bash
163{pushers}
164""".format(
165 pushers = push_commands,
166 ),
167 is_executable = True,
168 )
169 return [
170 DefaultInfo(
171 runfiles = runfiles,
172 executable = pusher,
173 ),
174 ]
175
176_kustomization_push = rule(
177 implementation = _kustomization_push_impl,
178 attrs = {
179 "kustomization": attr.label(
180 doc = "YAML files included in this Kustomization",
181 providers = [KustomizeInfo],
182 allow_files = True,
183 ),
184 },
185 executable = True,
186)
187
188def kustomization(
189 name,
190 kustomization_yaml,
191 srcs = None,
192 deps = None,
193 images = {},
194 **kwargs):
195 """Creates a kustomization from a source files and other kustomization rules as dependencies.
196
197 The kustomization macro takes inputs and creats a _kustomization rule that generates
198 the resolved manifests of the kustomization and any other dependent kustomizations.
199
200 The macro also generates a _kustomization_push rule if any image resolutions are included
201 in the _kustomization generated by a macro. The push rule can be used to push any
202 images to GAR.
203
204 Args:
205 name: name of the generated kustomization.
206 Gazelle defaults this to the name of the bazel package
207 srcs: source files to be used in the kustomization
208 deps: a list of labels pointing to other _kustomization rules
209 images: a dict of label to GAR url key: vals that can be used to resolve the
210 digest/pusher of a container_push target
211 kustomization_yaml: the kustomization.yaml source file for the kustomization
212 **kwargs: kwargs
213 """
214 forwarded_args = propagate_common_rule_attributes(kwargs)
215
216 # Build the kustomization
217 _kustomization(
218 name = name,
219 srcs = srcs,
220 deps = deps,
221 images = images,
222 kustomization_yaml = kustomization_yaml,
223 **forwarded_args
224 )
225
226 if len(images) > 0:
227 # Add the pusher
228 _kustomization_push(
229 name = "{0}_push".format(name),
230 kustomization = name,
231 **forwarded_args
232 )
233
234 return
View as plain text