metropolis: implement and use A/B preloader
This switches over from using the EFI built-in bootloader for A/B
updates to using our own EFI preloader due to significant issues with
in-the-wild EFI implementations. It is a very minimal design relying
on a single Protobuf state file instead of EFI variables.
Change-Id: Ieebd0a8172ebe3f44c69b3e8c278c53d3fe2eeb4
Reviewed-on: https://review.monogon.dev/c/monogon/+/2203
Tested-by: Jenkins CI
Reviewed-by: Serge Bazanski <serge@monogon.tech>
diff --git a/metropolis/node/build/def.bzl b/metropolis/node/build/def.bzl
index 7baa16b..d71279c 100644
--- a/metropolis/node/build/def.bzl
+++ b/metropolis/node/build/def.bzl
@@ -13,6 +13,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+load("@bazel_skylib//lib:paths.bzl", "paths")
def _build_pure_transition_impl(settings, attr):
"""
@@ -338,3 +339,75 @@
),
},
)
+
+# From Aspect's bazel-lib under Apache 2.0
+def _transition_platform_impl(_, attr):
+ return {"//command_line_option:platforms": str(attr.target_platform)}
+
+# Transition from any input configuration to one that includes the
+# --platforms command-line flag.
+_transition_platform = transition(
+ implementation = _transition_platform_impl,
+ inputs = [],
+ outputs = ["//command_line_option:platforms"],
+)
+
+
+def _platform_transition_binary_impl(ctx):
+ # We need to forward the DefaultInfo provider from the underlying rule.
+ # Unfortunately, we can't do this directly, because Bazel requires that the executable to run
+ # is actually generated by this rule, so we need to symlink to it, and generate a synthetic
+ # forwarding DefaultInfo.
+
+ result = []
+ binary = ctx.attr.binary[0]
+
+ default_info = binary[DefaultInfo]
+ files = default_info.files
+ new_executable = None
+ original_executable = default_info.files_to_run.executable
+ runfiles = default_info.default_runfiles
+
+ if not original_executable:
+ fail("Cannot transition a 'binary' that is not executable")
+
+ new_executable_name = ctx.attr.basename if ctx.attr.basename else original_executable.basename
+
+ # In order for the symlink to have the same basename as the original
+ # executable (important in the case of proto plugins), put it in a
+ # subdirectory named after the label to prevent collisions.
+ new_executable = ctx.actions.declare_file(paths.join(ctx.label.name, new_executable_name))
+ ctx.actions.symlink(
+ output = new_executable,
+ target_file = original_executable,
+ is_executable = True,
+ )
+ files = depset(direct = [new_executable], transitive = [files])
+ runfiles = runfiles.merge(ctx.runfiles([new_executable]))
+
+ result.append(
+ DefaultInfo(
+ files = files,
+ runfiles = runfiles,
+ executable = new_executable,
+ ),
+ )
+
+ return result
+
+platform_transition_binary = rule(
+ implementation = _platform_transition_binary_impl,
+ attrs = {
+ "basename": attr.string(),
+ "binary": attr.label(allow_files = True, cfg = _transition_platform),
+ "target_platform": attr.label(
+ doc = "The target platform to transition the binary.",
+ mandatory = True,
+ ),
+ "_allowlist_function_transition": attr.label(
+ default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
+ ),
+ },
+ executable = True,
+ doc = "Transitions the binary to use the provided platform.",
+)
\ No newline at end of file