core: build initramfs using generic initramfs rule
This chips away at three different things:
- it brings us closer to hermetic and cross-platform builds by not
depending on genrule/shell and lz4-the-tool
- it generalizes initramfs building (allowing for more than one to be
built, if necessary)
- sets the stage to use Bazel transitions [1] to force all included Go
binaries to be built in pure/static mode while allowing host Go
binaries to use cgo/dynamic linking if necessary, and hopefully also
allowing us to get rid of some BUILD patches that set pure='on' in
go_binary calls (notably needed in Cilium and some existing
third_party dependencies).
[1] - https://docs.bazel.build/versions/master/skylark/config.html#user-defined-transitions
Test Plan: build machinery change, covered by existing tests
X-Origin-Diff: phab/D554
GitOrigin-RevId: a5561eb5ca16e6529b9a4a2b98352f579c424222
diff --git a/core/BUILD b/core/BUILD
index 03a5153..1229388 100644
--- a/core/BUILD
+++ b/core/BUILD
@@ -1,67 +1,40 @@
-genrule(
+load("//core/build:def.bzl", "smalltown_initramfs")
+
+smalltown_initramfs(
name = "initramfs",
- srcs = [
- "//core/cmd/init",
- "//core/cmd/kube",
- "//third_party/xfsprogs:mkfs.xfs",
- "@io_k8s_kubernetes//cmd/kubelet:_kubelet-pure",
- "@com_github_containerd_containerd//cmd/containerd",
- "@com_github_containerd_containerd//cmd/containerd-shim-runc-v2",
- "@com_github_containernetworking_plugins//plugins/main/loopback",
- "@com_github_containernetworking_plugins//plugins/main/ptp",
- "@com_github_containernetworking_plugins//plugins/ipam/host-local",
- "@com_github_google_gvisor//runsc",
- "@com_github_google_gvisor_containerd_shim//cmd/containerd-shim-runsc-v1",
- "//core/internal/containerd:ptp.json",
- "//core/internal/containerd:loopback.json",
- "//core/internal/containerd:config.toml",
- "//core/internal/containerd:runsc.toml",
- "@cacerts//file",
- ":os-release-info",
+ extra_dirs = [
+ "/kubernetes/conf/flexvolume-plugins",
+ "/containerd/run",
],
- outs = [
- "initramfs.cpio.lz4",
- ],
- cmd = """
- $(location @linux//:gen_init_cpio) - <<- 'EOF' | lz4 -l > \"$@\"
-dir /dev 0755 0 0
-nod /dev/console 0600 0 0 c 5 1
-nod /dev/null 0644 0 0 c 1 3
-nod /dev/kmsg 0644 0 0 c 1 11
-nod /dev/ptmx 0644 0 0 c 5 2
-file /init $(location //core/cmd/init) 0755 0 0
-dir /etc 0755 0 0
-file /etc/os-release $(location :os-release-info) 0644 0 0
-dir /etc/ssl 0755 0 0
-file /etc/ssl/cert.pem $(location @cacerts//file) 0444 0 0
-dir /bin 0755 0 0
-file /bin/mkfs.xfs $(location //third_party/xfsprogs:mkfs.xfs) 0755 0 0
-dir /kubernetes 0755 0 0
-dir /kubernetes/bin 0755 0 0
-file /kubernetes/bin/kube $(location //core/cmd/kube) 0755 0 0
-dir /kubernetes/conf 0755 0 0
-dir /kubernetes/conf/flexvolume-plugins 0755 0 0
-dir /containerd 0755 0 0
-dir /containerd/bin 0755 0 0
-file /containerd/bin/containerd $(location @com_github_containerd_containerd//cmd/containerd) 0755 0 0
-file /containerd/bin/containerd-shim-runsc-v1 $(location @com_github_google_gvisor_containerd_shim//cmd/containerd-shim-runsc-v1) 0755 0 0
-file /containerd/bin/runsc $(location @com_github_google_gvisor//runsc) 0755 0 0
-dir /containerd/bin/cni 0755 0 0
-file /containerd/bin/cni/loopback $(location @com_github_containernetworking_plugins//plugins/main/loopback) 0755 0 0
-file /containerd/bin/cni/ptp $(location @com_github_containernetworking_plugins//plugins/main/ptp) 0755 0 0
-file /containerd/bin/cni/host-local $(location @com_github_containernetworking_plugins//plugins/ipam/host-local) 0755 0 0
-dir /containerd/run 0755 0 0
-dir /containerd/conf 0755 0 0
-dir /containerd/conf/cni 0755 0 0
-file /containerd/conf/cni/10-ptp.conf $(location //core/internal/containerd:ptp.json) 0444 0 0
-file /containerd/conf/cni/99-loopback.conf $(location //core/internal/containerd:loopback.json) 0444 0 0
-file /containerd/conf/config.toml $(location //core/internal/containerd:config.toml) 0444 0 0
-file /containerd/conf/runsc.toml $(location //core/internal/containerd:runsc.toml) 0444 0 0
-EOF
- """,
- tools = [
- "@linux//:gen_init_cpio",
- ],
+ files = {
+ "//core/cmd/init": "/init",
+ "//third_party/xfsprogs:mkfs.xfs": "/bin/mkfs.xfs",
+
+ # CA Certificate bundle & os-release
+ "@cacerts//file": "/etc/ssl/cert.pem",
+ ":os-release-info": "/etc/os-release",
+
+ # Hyperkube
+ "//core/cmd/kube": "/kubernetes/bin/kube",
+
+ # runsc/gVisor
+ "@com_github_google_gvisor//runsc": "/containerd/bin/runsc",
+ "@com_github_google_gvisor_containerd_shim//cmd/containerd-shim-runsc-v1": "/containerd/bin/containerd-shim-runsc-v1",
+
+ # Containerd
+ "@com_github_containerd_containerd//cmd/containerd": "/containerd/bin/containerd",
+
+ # Containerd config files
+ "//core/internal/containerd:runsc.toml": "/containerd/conf/runsc.toml",
+ "//core/internal/containerd:config.toml": "/containerd/conf/config.toml",
+ "//core/internal/containerd:loopback.json": "/containerd/conf/cni/99-loopback.conf",
+ "//core/internal/containerd:ptp.json": "/containerd/conf/cni/10-ptp.conf",
+
+ # CNI Plugins
+ "@com_github_containernetworking_plugins//plugins/main/loopback": "/containerd/bin/cni/loopback",
+ "@com_github_containernetworking_plugins//plugins/main/ptp": "/containerd/bin/cni/ptp",
+ "@com_github_containernetworking_plugins//plugins/ipam/host-local": "/containerd/bin/cni/host-local",
+ },
)
genrule(
diff --git a/core/build/BUILD b/core/build/BUILD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/core/build/BUILD
diff --git a/core/build/def.bzl b/core/build/def.bzl
new file mode 100644
index 0000000..69994fc
--- /dev/null
+++ b/core/build/def.bzl
@@ -0,0 +1,172 @@
+# Copyright 2020 The Monogon Project Authors.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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.
+
+def _smalltown_initramfs_impl(ctx):
+ """
+ Generate an lz4-compressed initramfs based on a label/file list.
+ """
+
+ # Generate config file for gen_init_cpio that describes the initramfs to build.
+ cpio_list_name = ctx.label.name + ".cpio_list"
+ cpio_list = ctx.actions.declare_file(cpio_list_name)
+
+ # Start out with some standard initramfs device files.
+ cpio_list_content = [
+ "dir /dev 0755 0 0",
+ "nod /dev/console 0600 0 0 c 5 1",
+ "nod /dev/null 0644 0 0 c 1 3",
+ "nod /dev/kmsg 0644 0 0 c 1 11",
+ "nod /dev/ptmx 0644 0 0 c 5 2",
+ ]
+
+ # Find all directories that need to be created.
+ directories_needed = []
+ for _, p in ctx.attr.files.items():
+ if not p.startswith("/"):
+ fail("file {} invalid: must begin with /".format(p))
+
+ # Get all intermediate directories on path to file
+ parts = p.split("/")[1:-1]
+ directories_needed.append(parts)
+
+ # Extend with extra directories defined by user.
+ for p in ctx.attr.extra_dirs:
+ if not p.startswith("/"):
+ fail("directory {} invalid: must begin with /".format(p))
+
+ parts = p.split("/")[1:]
+ directories_needed.append(parts)
+
+ directories = []
+ for parts in directories_needed:
+ # Turn directory parts [usr, local, bin] into successive subpaths [/usr, /usr/local, /usr/local/bin].
+ last = ""
+ for part in parts:
+ last += "/" + part
+
+ # TODO(q3k): this is slow - this should be a set instead, but starlark doesn't implement them.
+ # For the amount of files we're dealing with this doesn't matter, but all stars are pointing towards this
+ # becoming accidentally quadratic at some point in the future.
+ if last not in directories:
+ directories.append(last)
+
+ # Append instructions to create directories.
+ # Serendipitously, the directories should already be in the right order due to us not using a set to create the
+ # list. They might not be in an elegant order (ie, if files [/foo/one/one, /bar, /foo/two/two] are request, the
+ # order will be [/foo, /foo/one, /bar, /foo/two]), but that's fine.
+ for d in directories:
+ cpio_list_content.append("dir {} 0755 0 0".format(d))
+
+ # Append instructions to add files.
+ inputs = []
+ for label, p in ctx.attr.files.items():
+ # Figure out if this is an executable.
+ is_executable = True
+
+ di = label[DefaultInfo]
+ if di.files_to_run.executable == None:
+ # Generated non-executable files will have DefaultInfo.files_to_run.executable == None
+ is_executable = False
+ elif di.files_to_run.executable.is_source:
+ # Source files will have executable.is_source == True
+ is_executable = False
+
+ # Ensure only single output is declared.
+ # If you hit this error, figure out a better logic to find what file you need, maybe looking at providers other
+ # than DefaultInfo.
+ files = di.files.to_list()
+ if len(files) > 1:
+ fail("file {} has more than one output: {}", p, files)
+ src = files[0]
+ inputs.append(src)
+
+ mode = "0755" if is_executable else "0444"
+
+ cpio_list_content.append("file {} {} {} 0 0".format(p, src.path, mode))
+
+ # Write cpio_list.
+ ctx.actions.write(cpio_list, "\n".join(cpio_list_content))
+
+ gen_init_cpio = ctx.executable._gen_init_cpio
+ savestdout = ctx.executable._savestdout
+ lz4 = ctx.executable._lz4
+
+ # Generate 'raw' (uncompressed) initramfs
+ initramfs_raw_name = ctx.label.name
+ initramfs_raw = ctx.actions.declare_file(initramfs_raw_name)
+ ctx.actions.run(
+ outputs = [initramfs_raw],
+ inputs = [cpio_list] + inputs,
+ tools = [savestdout, gen_init_cpio],
+ executable = savestdout,
+ arguments = [initramfs_raw.path, gen_init_cpio.path, cpio_list.path],
+ )
+
+ # Compress raw initramfs using lz4c.
+ initramfs_name = ctx.label.name + ".lz4"
+ initramfs = ctx.actions.declare_file(initramfs_name)
+ ctx.actions.run(
+ outputs = [initramfs],
+ inputs = [initramfs_raw],
+ tools = [savestdout, lz4],
+ executable = lz4.path,
+ arguments = ["-l", initramfs_raw.path, initramfs.path],
+ )
+
+ return [DefaultInfo(files = depset([initramfs]))]
+
+smalltown_initramfs = rule(
+ implementation = _smalltown_initramfs_impl,
+ doc = """
+ Build a Smalltown initramfs. The initramfs will contain a basic /dev directory and all the files specified by the
+ `files` attribute. Executable files will have their permissions set to 0755, non-executable files will have
+ their permissions set to 0444. All parent directories will be created with 0755 permissions.
+ """,
+ attrs = {
+ "files": attr.label_keyed_string_dict(
+ mandatory = True,
+ allow_files = True,
+ doc = """
+ Dictionary of Labels to String, placing a given Label's output file in the initramfs at the location
+ specified by the String value. The specified labels must only have a single output.
+ """,
+ ),
+ "extra_dirs": attr.string_list(
+ default = [],
+ doc = """
+ Extra directories to create. These will be created in addition to all the directories required to
+ contain the files specified in the `files` attribute.
+ """,
+ ),
+
+ # Tools, implicit dependencies.
+ "_gen_init_cpio": attr.label(
+ default = Label("@linux//:gen_init_cpio"),
+ executable = True,
+ cfg = "host",
+ ),
+ "_lz4": attr.label(
+ default = Label("@com_github_lz4_lz4//programs:lz4"),
+ executable = True,
+ cfg = "host",
+ ),
+ "_savestdout": attr.label(
+ default = Label("//build/savestdout"),
+ executable = True,
+ cfg = "host",
+ ),
+ },
+)