1
0
mirror of https://github.com/kata-containers/kata-containers.git synced 2025-05-08 08:27:24 +00:00

repo: Merge osbuilder/1.11.0-rc0 into kata-containers/2.0-dev

Merge osbuilder into kata-containers for 2.0 development cycle.

Signed-off-by: Salvador Fuentes <salvador.fuentes@intel.com>
This commit is contained in:
Salvador Fuentes 2020-04-29 16:46:43 -05:00
commit 21c830c7da
56 changed files with 5068 additions and 0 deletions

2
tools/osbuilder/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
image-builder/nsdax
dracut/Dockerfile

View File

@ -0,0 +1,3 @@
## Kata Containers osbuilder Code of Conduct
Kata Containers follows the [OpenStack Foundation Code of Conduct](https://www.openstack.org/legal/community-code-of-conduct/).

View File

@ -0,0 +1,5 @@
# Contributing
## This repo is part of [Kata Containers](https://katacontainers.io)
For details on how to contribute to the Kata Containers project, please see the main [contributing document](https://github.com/kata-containers/community/blob/master/CONTRIBUTING.md).

201
tools/osbuilder/LICENSE Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

211
tools/osbuilder/Makefile Normal file
View File

@ -0,0 +1,211 @@
#
# Copyright (c) 2017 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
MK_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
TEST_RUNNER := $(MK_DIR)/tests/test_images.sh
ROOTFS_BUILDER := $(MK_DIR)/rootfs-builder/rootfs.sh
INITRD_BUILDER := $(MK_DIR)/initrd-builder/initrd_builder.sh
IMAGE_BUILDER := $(MK_DIR)/image-builder/image_builder.sh
DISTRO := centos
BUILD_METHOD := distro
BUILD_METHOD_LIST := distro dracut
AGENT_INIT ?= no
ROOTFS_BUILD_DEST := $(shell pwd)
IMAGES_BUILD_DEST := $(shell pwd)
ROOTFS_MARKER_SUFFIX := _rootfs.done
TARGET_ROOTFS := $(ROOTFS_BUILD_DEST)/$(DISTRO)_rootfs
TARGET_ROOTFS_MARKER := $(ROOTFS_BUILD_DEST)/.$(DISTRO)$(ROOTFS_MARKER_SUFFIX)
TARGET_IMAGE := $(IMAGES_BUILD_DEST)/kata-containers.img
TARGET_INITRD := $(IMAGES_BUILD_DEST)/kata-containers-initrd.img
VERSION_FILE := ./VERSION
VERSION := $(shell grep -v ^\# $(VERSION_FILE))
COMMIT_NO := $(shell git rev-parse HEAD 2> /dev/null || true)
COMMIT := $(if $(shell git status --porcelain --untracked-files=no),${COMMIT_NO}-dirty,${COMMIT_NO})
VERSION_COMMIT := $(if $(COMMIT),$(VERSION)-$(COMMIT),$(VERSION))
ifeq ($(filter $(BUILD_METHOD),$(BUILD_METHOD_LIST)),)
$(error Invalid BUILD_METHOD value '$(BUILD_METHOD)'. Supported values: $(BUILD_METHOD_LIST))
endif
ifeq (dracut,$(BUILD_METHOD))
DISTRO :=
TARGET_ROOTFS := dracut_rootfs
TARGET_ROOTFS_MARKER := $(ROOTFS_BUILD_DEST)/.dracut$(ROOTFS_MARKER_SUFFIX)
# dracut specific variables
DRACUT_KVERSION :=
DRACUT_OVERLAY_DIR := $(MK_DIR)/dracut_overlay
DRACUT_DIR := $(MK_DIR)/dracut
DRACUT_CONF_DIR := $(DRACUT_DIR)/dracut.conf.d
DRACUT_OPTIONS := --no-compress --conf /dev/null --confdir $(DRACUT_CONF_DIR)
ifneq (,$(DRACUT_KVERSION))
# Explicitly use bash, which is what dracut uses to process conf files
DRACUT_KMODULES := $(shell bash -c 'source $(DRACUT_CONF_DIR)/10-drivers.conf; echo "$$drivers"')
else
# If a kernel version is not specified, do not make systemd load modules
# at startup
DRACUT_OPTIONS += --no-kernel
endif
ifeq (,$(DRACUT_OVERLAY_DIR))
$(error DRACUT_OVERLAY_DIR cannot be empty)
endif
endif
# Set the variable to silent logs using chronic
OSBUILDER_USE_CHRONIC :=
# silent_run allows running make recipes using the chronic wrapper, so logs are
# muted if the recipe command succeeds.
# Arguments:
# - Message
# - Command to run
ifeq (,$(OSBUILDER_USE_CHRONIC))
define silent_run
@echo $(1)
$(2)
endef
else
define silent_run
@echo $(1) with command: $(2)
@chronic $(2)
endef
endif
################################################################################
rootfs-%: $(ROOTFS_BUILD_DEST)/.%$(ROOTFS_MARKER_SUFFIX)
@ # DONT remove. This is not cancellation rule.
.PRECIOUS: $(ROOTFS_BUILD_DEST)/.%$(ROOTFS_MARKER_SUFFIX)
$(ROOTFS_BUILD_DEST)/.%$(ROOTFS_MARKER_SUFFIX):: rootfs-builder/%
$(call silent_run,Creating rootfs for "$*",$(ROOTFS_BUILDER) -o $(VERSION_COMMIT) -r $(ROOTFS_BUILD_DEST)/$*_rootfs $*)
@touch $@
# To generate a dracut rootfs, we first generate a dracut initrd and then
# extract it in a local folder.
# Notes:
# - assuming a not compressed initrd.
.PRECIOUS: $(ROOTFS_BUILD_DEST)/.dracut$(ROOTFS_MARKER_SUFFIX)
$(ROOTFS_BUILD_DEST)/.dracut$(ROOTFS_MARKER_SUFFIX): $(TARGET_INITRD)
mkdir -p $(TARGET_ROOTFS)
(cd $(TARGET_ROOTFS); cat $< | cpio --extract --preserve-modification-time --make-directories)
@touch $@
image-%: $(IMAGES_BUILD_DEST)/kata-containers-image-%.img
@ # DONT remove. This is not cancellation rule.
.PRECIOUS: $(IMAGES_BUILD_DEST)/kata-containers-image-%.img
$(IMAGES_BUILD_DEST)/kata-containers-image-%.img: rootfs-%
$(call silent_run,Creating image based on $^,$(IMAGE_BUILDER) -o $@ $(ROOTFS_BUILD_DEST)/$*_rootfs)
initrd-%: $(IMAGES_BUILD_DEST)/kata-containers-initrd-%.img
@ # DONT remove. This is not cancellation rule.
.PRECIOUS: $(IMAGES_BUILD_DEST)/kata-containers-initrd-%.img
$(IMAGES_BUILD_DEST)/kata-containers-initrd-%.img: rootfs-%
$(call silent_run,Creating initrd image for $*,$(INITRD_BUILDER) -o $@ $(ROOTFS_BUILD_DEST)/$*_rootfs)
.PHONY: all
all: image initrd
.PHONY: rootfs
rootfs: $(TARGET_ROOTFS_MARKER)
.PHONY: image
image: $(TARGET_IMAGE)
$(TARGET_IMAGE): $(TARGET_ROOTFS_MARKER)
$(call silent_run,Creating image based on "$(TARGET_ROOTFS)",$(IMAGE_BUILDER) -o $@ "$(TARGET_ROOTFS)")
.PHONY: initrd
initrd: $(TARGET_INITRD)
ifeq (distro,$(BUILD_METHOD))
$(TARGET_INITRD): $(TARGET_ROOTFS_MARKER)
$(call silent_run,Creating initrd image based on "$(TARGET_ROOTFS)",$(INITRD_BUILDER) "$(TARGET_ROOTFS)")
else
$(TARGET_INITRD): $(DRACUT_OVERLAY_DIR)
@echo Creating initrd image based on the host OS using dracut
dracut $(DRACUT_OPTIONS) --include $< / $@ $(DRACUT_KVERSION)
endif
# Notes on overlay dir:
# - If user specified any kernel module in the dracut conf file,
# we need to make sure these are pre-loaded at startup using
# systemd modules-load.d
$(DRACUT_OVERLAY_DIR):
mkdir -p $@
# Modules preload
$(ROOTFS_BUILDER) -o $(VERSION_COMMIT) -r $@
mkdir -p $@/etc/modules-load.d
echo $(DRACUT_KMODULES) | tr " " "\n" > $@/etc/modules-load.d/kata-modules.conf
.PHONY: test
test:
$(TEST_RUNNER) "$(DISTRO)"
.PHONY: test-image-only
test-image-only:
$(TEST_RUNNER) --test-images-only "$(DISTRO)"
.PHONY: test-initrd-only
test-initrd-only:
$(TEST_RUNNER) --test-initrds-only "$(DISTRO)"
.PHONY: list-distros
list-distros:
@ $(ROOTFS_BUILDER) -l
DESTDIR := /
KATADIR := /usr/libexec/kata-containers
OSBUILDER_DIR := $(KATADIR)/osbuilder
INSTALL_DIR :=$(DESTDIR)/$(OSBUILDER_DIR)
DIST_CONFIGS:= $(wildcard rootfs-builder/*/config.sh)
SCRIPTS :=
SCRIPTS += rootfs-builder/rootfs.sh
SCRIPTS += image-builder/image_builder.sh
SCRIPTS += initrd-builder/initrd_builder.sh
HELPER_FILES :=
HELPER_FILES += rootfs-builder/versions.txt
HELPER_FILES += scripts/lib.sh
HELPER_FILES += image-builder/nsdax.gpl.c
define INSTALL_FILE
echo "Installing $(abspath $2/$1)";
install -m 644 -D $1 $2/$1;
endef
define INSTALL_SCRIPT
echo "Installing $(abspath $2/$1)";
install -m 755 -D $1 $(abspath $2/$1);
endef
.PHONY: install-scripts
install-scripts:
@echo "Installing scripts"
@$(foreach f,$(SCRIPTS),$(call INSTALL_SCRIPT,$f,$(INSTALL_DIR)))
@echo "Installing helper files"
@$(foreach f,$(HELPER_FILES),$(call INSTALL_FILE,$f,$(INSTALL_DIR)))
@echo "Installing installing config files"
@$(foreach f,$(DIST_CONFIGS),$(call INSTALL_FILE,$f,$(INSTALL_DIR)))
.PHONY: clean
clean:
rm -rf $(TARGET_ROOTFS_MARKER) $(TARGET_ROOTFS) $(TARGET_IMAGE) $(TARGET_INITRD) $(DRACUT_OVERLAY_DIR)
# Prints the name of the variable passed as suffix to the print- target,
# E.g., if Makefile contains:
# MY_MAKE_VAR := foobar
# Then:
# $ make printf-MY_MAKE_VAR
# Will print "foobar"
print-%:
@echo $($*)

212
tools/osbuilder/README.md Normal file
View File

@ -0,0 +1,212 @@
[![Build Status](https://travis-ci.org/kata-containers/osbuilder.svg?branch=master)](https://travis-ci.org/kata-containers/osbuilder)
# osbuilder
* [osbuilder](#osbuilder)
* [Introduction](#introduction)
* [Terms](#terms)
* [Building](#building)
* [Rootfs creation](#rootfs-creation)
* [Rootfs with systemd as init](#rootfs-with-systemd-as-init)
* [Rootfs with the agent as init](#rootfs-with-the-agent-as-init)
* [dracut based rootfs](#dracut-based-rootfs)
* [Image creation](#image-creation)
* [Image with systemd as init](#image-with-systemd-as-init)
* [Image with the agent as init](#image-with-the-agent-as-init)
* [dracut based image](#dracut-based-image)
* [Initrd creation](#initrd-creation)
* [Rootfs based initrd](#rootfs-based-initrd)
* [dracut based initrd](#dracut-based-initrd)
* [dracut options](#dracut-options)
* [Add kernel modules](#add-kernel-modules)
* [Testing](#testing)
* [Platform-Distro Compatibility Matrix](#platform-distro-compatibility-matrix)
## Introduction
The Kata Containers runtime creates a virtual machine (VM) to isolate a set of
container workloads. The VM requires a guest kernel and a guest operating system
("guest OS") to boot and create containers inside the guest
environment.
This repository contains tools to create a guest OS disk image.
## Terms
This section describes the terms used for all documentation in this repository.
- rootfs
The root filesystem or "rootfs" is a slight misnomer as it is not a true filesystem. It is a tree of files contained in a particular directory, which represents the root disk layout. A rootfs can be turned into either an image or an initrd.
See the [rootfs creation](#rootfs-creation) section.
- "Guest OS" (or "Guest Image")
A "virtual disk" or "disk image" built from a rootfs. It contains a
filesystem that is used by the VM, in conjunction with a guest kernel, to
create an environment to host the container. Neither the guest OS nor the
guest kernel need to be the same as the host operating system.
See the [image creation](#image-creation) section.
- initrd (or "initramfs")
A compressed `cpio(1)` archive, created from a rootfs which is loaded into memory and used as part of the Linux startup process. During startup, the kernel unpacks it into a special instance of a `tmpfs` that becomes the initial root filesystem.
See the [initrd creation](#initrd-creation) section.
- "Base OS"
A particular version of a Linux distribution used to create a rootfs from.
- dracut
A guest OS build method where the building host is used as the Base OS.
For more information refer to the [dracut homepage](https://dracut.wiki.kernel.org/index.php/Main_Page).
## Building
The top-level `Makefile` contains an example of how to use the available components.
Set `DEBUG=true` to execute build scripts in debug mode.
Two build methods are available, `distro` and `dracut`.
By default, the `distro` build method is used, and this creates a rootfs using
distro specific commands (e.g.: `debootstrap` for Debian or `yum` for CentOS).
The `dracut` build method uses the distro-agnostic tool `dracut` to obtain the same goal.
By default components are run on the host system. However, some components
offer the ability to run from within a container (for ease of setup) by setting the
`USE_DOCKER=true` or `USE_PODMAN=true` variable. If both are set, `USE_DOCKER=true`
takes precedence over `USE_PODMAN=true`.
For more detailed information, consult the documentation for a particular component.
When invoking the appropriate make target as showed below, a single command is used
to generate an initrd or an image. This is what happens in details:
1. A rootfs is generated based on the specified target distribution.
2. The rootfs is provisioned with Kata-specific components and configuration files.
3. The rootfs is used as a base to generate an initrd or an image.
When using the dracut build method however, the build sequence is different:
1. An overlay directory is populated with Kata-specific components.
2. dracut is instructed to merge the overlay directory with the required host-side
filesystem components to generate an initrd.
3. When generating an image, the initrd is extracted to obtain the base rootfs for
the image.
### Rootfs creation
This section shows how to build a basic rootfs using the default distribution.
For further details, see
[the rootfs builder documentation](rootfs-builder/README.md).
#### Rootfs with systemd as init
```
$ sudo -E PATH=$PATH make USE_DOCKER=true rootfs
```
#### Rootfs with the agent as init
```
$ sudo -E PATH=$PATH make USE_DOCKER=true AGENT_INIT=yes rootfs
```
#### dracut based rootfs
> **Note**: the dracut build method does not need a rootfs as a base for an image or initrd.
However, a rootfs can be generated by extracting the generated initrd.
```
$ sudo -E PATH=$PATH make BUILD_METHOD=dracut rootfs
```
### Image creation
This section shows how to create an image from the already-created rootfs. For
further details, see
[the image builder documentation](image-builder/README.md).
#### Image with systemd as init
```
$ sudo -E PATH=$PATH make USE_DOCKER=true image
```
#### Image with the agent as init
```
$ sudo -E PATH=$PATH make USE_DOCKER=true AGENT_INIT=yes image
```
#### dracut based image
> Note: the dracut build method generates an image by first building an initrd,
and then using the rootfs extracted from it.
```
$ sudo -E PATH=$PATH make BUILD_METHOD=dracut image
```
### Initrd creation
#### Rootfs based initrd
Create an initrd from the already-created rootfs and with the agent acting as the init daemon
using:
```
$ sudo -E PATH=$PATH make AGENT_INIT=yes initrd
```
#### dracut based initrd
Create an initrd using the dracut build method with:
```
$ sudo -E PATH=$PATH make BUILD_METHOD=dracut AGENT_INIT=yes initrd
```
For further details,
see [the initrd builder documentation](initrd-builder/README.md).
### dracut options
#### Add kernel modules
If the initrd or image needs to contain kernel modules, this can be done by:
1. Specify the name of the modules (as reported by `modinfo MODULE-NAME`) in
`dracut/dracut.conf.d/10-drivers.conf`. For example this file can contain:
```
drivers="9p 9pnet 9pnet_virtio"
```
2. Set the `DRACUT_KVERSION` make variable to the release name of the kernel that
is paired with the built image or initrd, using the `uname -r` format. For example:
```
$ make BUILD_METHOD=dracut DRACUT_KVERSION=5.2.1-23-kata AGENT_INIT=yes initrd
```
## Testing
```
$ make test
```
For further details, see [the tests documentation](tests/README.md).
## Platform-Distro Compatibility Matrix
The following table illustrates what target architecture is supported for each
of the the osbuilder distributions.
> Note: this table is not relevant for the dracut build method, since it supports
any Linux distribution and architecture where dracut is available.
| |Alpine |CentOS |Clear Linux |Debian/Ubuntu |EulerOS |Fedora |openSUSE |
|-- |-- |-- |-- |-- |-- |-- |-- |
|**ARM64** |:heavy_check_mark:|:heavy_check_mark:| | |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
|**PPC64le**|:heavy_check_mark:|:heavy_check_mark:| |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|
|**s390x** |:heavy_check_mark:| | |:heavy_check_mark:| |:heavy_check_mark:| |
|**x86_64** |:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|

1
tools/osbuilder/VERSION Normal file
View File

@ -0,0 +1 @@
1.11.0-rc0

View File

@ -0,0 +1,38 @@
#
# Copyright (c) 2019 SUSE LLC
#
# SPDX-License-Identifier: Apache-2.0
from opensuse/tumbleweed
RUN zypper --non-interactive refresh; \
zypper --non-interactive install --no-recommends --force-resolution \
autoconf \
automake \
binutils \
cmake \
coreutils \
cpio \
curl \
dracut \
gcc \
gcc-c++ \
git-core \
glibc-devel \
glibc-devel-static \
glibc-utils \
libstdc++-devel \
linux-glibc-devel \
m4 \
make \
sed \
tar \
vim \
which; \
zypper --non-interactive clean --all;
# This will install the proper golang to build Kata components
@INSTALL_MUSL@
@INSTALL_GO@
@INSTALL_RUST@

View File

@ -0,0 +1,17 @@
#
# Copyright (c) 2019 SUSE LLC
#
# SPDX-License-Identifier: Apache-2.0
# Main dracut config for Kata Containers
# do NOT combine early microcode with ramdisk
early_microcode="no"
# do NOT install only what's needed to boot the local host
hostonly="no"
# do NOT store the kernel command line arguments in the initramfs
hostonly_cmdline="no"
# create reproducible images
reproducible="yes"
# dracut modules to include (NOTE: these are NOT kernel modules)
dracutmodules="kernel-modules udev-rules syslog systemd"

View File

@ -0,0 +1,9 @@
#
# Copyright (c) 2019 SUSE LLC
#
# SPDX-License-Identifier: Apache-2.0
# Specify a space-separated set of kernel modules to copy from the host to
# the initramfs image. For example:
# drivers="9p 9pnet 9pnet_virtio"
drivers=""

View File

@ -0,0 +1,10 @@
#
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
From docker.io/fedora:latest
RUN [ -n "$http_proxy" ] && sed -i '$ a proxy='$http_proxy /etc/dnf/dnf.conf ; true
RUN dnf install -y qemu-img parted gdisk e2fsprogs gcc xfsprogs findutils

View File

@ -0,0 +1,29 @@
* [Creating a guest OS image](#creating-a-guest-os-image)
* [Further information](#further-information)
# Kata Containers image generation
A Kata Containers disk image is generated using the `image_builder.sh` script.
This uses a rootfs directory created by the `rootfs-builder/rootfs.sh` script.
## Creating a guest OS image
To create a guest OS image run:
```
$ sudo ./image_builder.sh path/to/rootfs
```
Where `path/to/rootfs` is the directory populated by `rootfs.sh`.
> **Note**: If you are building an image from an Alpine rootfs, see
> the important note [here](/rootfs-builder/README.md#rootfs-requirements).
## Further information
For more information about usage (including how to adjust the size of the
image), run:
```
$ ./image_builder.sh -h
```

View File

@ -0,0 +1,513 @@
#!/usr/bin/env bash
#
# Copyright (c) 2017-2019 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
set -e
[ -n "${DEBUG}" ] && set -x
DOCKER_RUNTIME=${DOCKER_RUNTIME:-runc}
readonly script_name="${0##*/}"
readonly script_dir=$(dirname "$(readlink -f "$0")")
readonly lib_file="${script_dir}/../scripts/lib.sh"
readonly ext4_format="ext4"
readonly xfs_format="xfs"
# ext4: percentage of the filesystem which may only be allocated by privileged processes.
readonly reserved_blocks_percentage=3
# Where the rootfs starts in MB
readonly rootfs_start=1
# Where the rootfs ends in MB
readonly rootfs_end=-1
# DAX header size
# * NVDIMM driver reads the device namespace information from nvdimm namespace (4K offset).
# The MBR #1 + DAX metadata are saved in the first 2MB of the image.
readonly dax_header_sz=2
# DAX aligment
# * DAX huge pages [2]: 2MB alignment
# [2] - https://nvdimm.wiki.kernel.org/2mib_fs_dax
readonly dax_alignment=2
# The list of systemd units and files that are not needed in Kata Containers
readonly -a systemd_units=(
"systemd-coredump@"
"systemd-journald"
"systemd-journald-dev-log"
"systemd-journal-flush"
"systemd-random-seed"
"systemd-timesyncd"
"systemd-tmpfiles-setup"
"systemd-udevd"
"systemd-udevd-control"
"systemd-udevd-kernel"
"systemd-udev-trigger"
"systemd-update-utmp"
)
readonly -a systemd_files=(
"systemd-bless-boot-generator"
"systemd-fstab-generator"
"systemd-getty-generator"
"systemd-gpt-auto-generator"
"systemd-tmpfiles-cleanup.timer"
)
# Set a default value
AGENT_INIT=${AGENT_INIT:-no}
# Align image to (size in MB) according to different architecture.
case "$(uname -m)" in
aarch64) readonly mem_boundary_mb=16 ;;
*) readonly mem_boundary_mb=128 ;;
esac
# shellcheck source=../scripts/lib.sh
source "${lib_file}"
usage() {
cat <<EOT
Usage: ${script_name} [options] <rootfs-dir>
This script will create a Kata Containers image file of
an adequate size based on the <rootfs-dir> directory.
Options:
-h Show this help
-o path to generate image file ENV: IMAGE
-r Free space of the root partition in MB ENV: ROOT_FREE_SPACE
Extra environment variables:
AGENT_BIN: Use it to change the expected agent binary name
AGENT_INIT: Use kata agent as init process
NSDAX_BIN: Use to specify path to pre-compiled 'nsdax' tool.
FS_TYPE: Filesystem type to use. Only xfs and ext4 are supported.
USE_DOCKER: If set will build image in a Docker Container (requries docker)
DEFAULT: not set
USE_PODMAN: If set and USE_DOCKER not set, will build image in a Podman Container (requries podman)
DEFAULT: not set
Following diagram shows how the resulting image will look like
.-----------.----------.---------------.-----------.
| 0 - 512 B | 4 - 8 Kb | 2M - 2M+512B | 3M |
|-----------+----------+---------------+-----------+
| MBR #1 | DAX | MBR #2 | Rootfs |
'-----------'----------'---------------'-----------+
| | ^ | ^
| '-data-' '--------'
| |
'--------rootfs-partition---------'
MBR: Master boot record.
DAX: Metadata required by the NVDIMM driver to enable DAX in the guest [1][2] (struct nd_pfn_sb).
Rootfs: partition that contains the root filesystem (/usr, /bin, ect).
Kernels and hypervisors that support DAX/NVDIMM read the MBR #2, otherwise MBR #1 is read.
[1] - https://github.com/kata-containers/osbuilder/blob/master/image-builder/nsdax.gpl.c
[2] - https://github.com/torvalds/linux/blob/master/drivers/nvdimm/pfn.h
EOT
}
# build the image using container engine
build_with_container() {
local rootfs="$1"
local image="$2"
local fs_type="$3"
local block_size="$4"
local root_free_space="$5"
local agent_bin="$6"
local agent_init="$7"
local container_engine="$8"
local nsdax_bin="$9"
local container_image_name="image-builder-osbuilder"
local shared_files=""
image_dir=$(readlink -f "$(dirname "${image}")")
image_name=$(basename "${image}")
"${container_engine}" build \
--build-arg http_proxy="${http_proxy}" \
--build-arg https_proxy="${https_proxy}" \
-t "${container_image_name}" "${script_dir}"
readonly mke2fs_conf="/etc/mke2fs.conf"
if [ -f "${mke2fs_conf}" ]; then
shared_files+="-v ${mke2fs_conf}:${mke2fs_conf}:ro "
fi
#Make sure we use a compatible runtime to build rootfs
# In case Clear Containers Runtime is installed we dont want to hit issue:
#https://github.com/clearcontainers/runtime/issues/828
"${container_engine}" run \
--rm \
--runtime "${DOCKER_RUNTIME}" \
--privileged \
--env AGENT_BIN="${agent_bin}" \
--env AGENT_INIT="${agent_init}" \
--env FS_TYPE="${fs_type}" \
--env BLOCK_SIZE="${block_size}" \
--env ROOT_FREE_SPACE="${root_free_space}" \
--env NSDAX_BIN="${nsdax_bin}" \
--env DEBUG="${DEBUG}" \
-v /dev:/dev \
-v "${script_dir}":"/osbuilder" \
-v "${script_dir}/../scripts":"/scripts" \
-v "${rootfs}":"/rootfs" \
-v "${image_dir}":"/image" \
${shared_files} \
${container_image_name} \
bash "/osbuilder/${script_name}" -o "/image/${image_name}" /rootfs
}
check_rootfs() {
local rootfs="${1}"
[ -d "${rootfs}" ] || die "${rootfs} is not a directory"
# The kata rootfs image expect init and kata-agent to be installed
init_path="/sbin/init"
init="${rootfs}${init_path}"
if [ ! -x "${init}" ] && [ ! -L "${init}" ]; then
error "${init_path} is not installed in ${rootfs}"
return 1
fi
OK "init is installed"
systemd_path="/lib/systemd/systemd"
systemd="${rootfs}${systemd_path}"
# check agent or systemd
case "${AGENT_INIT}" in
"no")
if [ ! -x "${systemd}" ] && [ ! -L "${systemd}" ]; then
error "${systemd_path} is not installed in ${rootfs}"
return 1
fi
OK "init is systemd"
;;
"yes")
agent_path="/sbin/init"
agent="${rootfs}${agent_path}"
if [ ! -x "${agent}" ]; then
error "${agent_path} is not installed in ${rootfs}. Use AGENT_BIN env variable to change the expected agent binary name"
return 1
fi
# checksum must be different to system
if [ -f "${systemd}" ] && cmp -s "${systemd}" "${agent}"; then
error "The agent is not the init process. ${agent_path} is systemd"
return 1
fi
OK "Agent installed"
;;
*)
error "Invalid value for AGENT_INIT: '${AGENT_INIT}'. Use to 'yes' or 'no'"
return 1
;;
esac
return 0
}
calculate_required_disk_size() {
local rootfs="$1"
local fs_type="$2"
local block_size="$3"
readonly rootfs_size_mb=$(du -B 1MB -s "${rootfs}" | awk '{print $1}')
readonly image="$(mktemp)"
readonly mount_dir="$(mktemp -d)"
readonly max_tries=20
readonly increment=10
for i in $(seq 1 $max_tries); do
local img_size="$((rootfs_size_mb + (i * increment)))"
create_disk "${image}" "${img_size}" "${fs_type}" "${rootfs_start}" > /dev/null 2>&1
if ! device="$(setup_loop_device "${image}")"; then
continue
fi
if ! format_loop "${device}" "${block_size}" "${fs_type}" > /dev/null 2>&1 ; then
die "Could not format loop device: ${device}"
fi
mount "${device}p1" "${mount_dir}"
avail="$(df -BM --output=avail "${mount_dir}" | tail -n1 | sed 's/[M ]//g')"
umount "${mount_dir}"
losetup -d "${device}"
if [ "${avail}" -gt "${rootfs_size_mb}" ]; then
rmdir "${mount_dir}"
rm -f "${image}"
echo "${img_size}"
return
fi
done
rmdir "${mount_dir}"
rm -f "${image}"
error "Could not calculate the required disk size"
}
# Calculate image size based on the rootfs and free space
calculate_img_size() {
local rootfs="$1"
local root_free_space_mb="$2"
local fs_type="$3"
local block_size="$4"
# rootfs start + DAX header size + rootfs end
local reserved_size_mb=$((rootfs_start + dax_header_sz + rootfs_end))
disk_size="$(calculate_required_disk_size "${rootfs}" "${fs_type}" "${block_size}")"
img_size="$((disk_size + reserved_size_mb))"
if [ -n "${root_free_space_mb}" ]; then
img_size="$((img_size + root_free_space_mb))"
fi
remaining="$((img_size % mem_boundary_mb))"
if [ "${remaining}" != "0" ]; then
img_size=$((img_size + mem_boundary_mb - remaining))
fi
echo "${img_size}"
}
setup_loop_device() {
local image="$1"
# Get the loop device bound to the image file (requires /dev mounted in the
# image build system and root privileges)
device=$(losetup -P -f --show "${image}")
#Refresh partition table
partprobe -s "${device}" > /dev/null
# Poll for the block device p1
for _ in $(seq 1 5); do
if [ -b "${device}p1" ]; then
echo "${device}"
return 0
fi
sleep 1
done
error "File ${device}p1 is not a block device"
return 1
}
format_loop() {
local device="$1"
local block_size="$2"
local fs_type="$3"
case "${fs_type}" in
"${ext4_format}")
mkfs.ext4 -q -F -b "${block_size}" "${device}p1"
info "Set filesystem reserved blocks percentage to ${reserved_blocks_percentage}%"
tune2fs -m "${reserved_blocks_percentage}" "${device}p1"
;;
"${xfs_format}")
mkfs.xfs -q -f -b size="${block_size}" "${device}p1"
;;
*)
error "Unsupported fs type: ${fs_type}"
return 1
;;
esac
}
create_disk() {
local image="$1"
local img_size="$2"
local fs_type="$3"
local part_start="$4"
info "Creating raw disk with size ${img_size}M"
qemu-img create -q -f raw "${image}" "${img_size}M"
OK "Image file created"
# Kata runtime expect an image with just one partition
# The partition is the rootfs content
info "Creating partitions"
parted -s -a optimal "${image}" -- \
mklabel msdos \
mkpart primary "${fs_type}" "${part_start}"M "${rootfs_end}"M
OK "Partitions created"
}
create_rootfs_image() {
local rootfs="$1"
local image="$2"
local img_size="$3"
local fs_type="$4"
local block_size="$5"
create_disk "${image}" "${img_size}" "${fs_type}" "${rootfs_start}"
if ! device="$(setup_loop_device "${image}")"; then
die "Could not setup loop device"
fi
if ! format_loop "${device}" "${block_size}" "${fs_type}"; then
die "Could not format loop device: ${device}"
fi
info "Mounting root partition"
readonly mount_dir=$(mktemp -p ${TMPDIR:-/tmp} -d osbuilder-mount-dir.XXXX)
mount "${device}p1" "${mount_dir}"
OK "root partition mounted"
info "Copying content from rootfs to root partition"
cp -a "${rootfs}"/* "${mount_dir}"
sync
OK "rootfs copied"
info "Removing unneeded systemd services and sockets"
for u in "${systemd_units[@]}"; do
find "${mount_dir}" -type f \( \
-name "${u}.service" -o \
-name "${u}.socket" \) \
-exec rm -f {} \;
done
info "Removing unneeded systemd files"
for u in "${systemd_files[@]}"; do
find "${mount_dir}" -type f -name "${u}" -exec rm -f {} \;
done
info "Creating empty machine-id to allow systemd to bind-mount it"
touch "${mount_dir}/etc/machine-id"
info "Unmounting root partition"
umount "${mount_dir}"
OK "Root partition unmounted"
if [ "${fs_type}" = "${ext4_format}" ]; then
fsck.ext4 -D -y "${device}p1"
fi
losetup -d "${device}"
rmdir "${mount_dir}"
}
set_dax_header() {
local image="$1"
local img_size="$2"
local fs_type="$3"
local nsdax_bin="$4"
# rootfs start + DAX header size
local rootfs_offset=$((rootfs_start + dax_header_sz))
local header_image="${image}.header"
local dax_image="${image}.dax"
rm -f "${dax_image}" "${header_image}"
create_disk "${header_image}" "${img_size}" "${fs_type}" "${rootfs_offset}"
dax_header_bytes=$((dax_header_sz * 1024 * 1024))
dax_alignment_bytes=$((dax_alignment * 1024 * 1024))
info "Set DAX metadata"
# Set metadata header
# Issue: https://github.com/kata-containers/osbuilder/issues/240
if [ -z "${nsdax_bin}" ] ; then
nsdax_bin="${script_dir}/nsdax"
gcc -O2 "${script_dir}/nsdax.gpl.c" -o "${nsdax_bin}"
trap "rm ${nsdax_bin}" EXIT
fi
"${nsdax_bin}" "${header_image}" "${dax_header_bytes}" "${dax_alignment_bytes}"
sync
touch "${dax_image}"
# Copy MBR #1 + DAX metadata
dd if="${header_image}" of="${dax_image}" bs="${dax_header_sz}M" count=1
# Copy MBR #2 + Rootfs
dd if="${image}" of="${dax_image}" oflag=append conv=notrunc
# final image
mv "${dax_image}" "${image}"
sync
rm -f "${dax_image}" "${header_image}"
}
main() {
[ "$(id -u)" -eq 0 ] || die "$0: must be run as root"
# variables that can be overwritten by environment variables
local agent_bin="${AGENT_BIN:-kata-agent}"
local agent_init="${AGENT_INIT:-no}"
local fs_type="${FS_TYPE:-${ext4_format}}"
local image="${IMAGE:-kata-containers.img}"
local block_size="${BLOCK_SIZE:-4096}"
local root_free_space="${ROOT_FREE_SPACE:-}"
local nsdax_bin="${NSDAX_BIN:-}"
while getopts "ho:r:f:" opt
do
case "$opt" in
h) usage; return 0;;
o) image="${OPTARG}" ;;
r) root_free_space="${OPTARG}" ;;
f) fs_type="${OPTARG}" ;;
*) break ;;
esac
done
shift $(( OPTIND - 1 ))
rootfs="$(readlink -f "$1")"
if [ -z "${rootfs}" ]; then
usage
exit 0
fi
local container_engine
if [ -n "${USE_DOCKER}" ]; then
container_engine="docker"
elif [ -n "${USE_PODMAN}" ]; then
container_engine="podman"
fi
if [ -n "$container_engine" ]; then
build_with_container "${rootfs}" \
"${image}" "${fs_type}" "${block_size}" \
"${root_free_space}" "${agent_bin}" \
"${agent_init}" "${container_engine}" \
"${nsdax_bin}"
exit $?
fi
if ! check_rootfs "${rootfs}" ; then
die "Invalid rootfs"
fi
img_size=$(calculate_img_size "${rootfs}" "${root_free_space}" "${fs_type}" "${block_size}")
# the first 2M are for the first MBR + NVDIMM metadata and were already
# consider in calculate_img_size
rootfs_img_size=$((img_size - dax_header_sz))
create_rootfs_image "${rootfs}" "${image}" "${rootfs_img_size}" \
"${fs_type}" "${block_size}"
# insert at the beginning of the image the MBR + DAX header
set_dax_header "${image}" "${img_size}" "${fs_type}" "${nsdax_bin}"
}
main "$@"

View File

@ -0,0 +1,171 @@
/*
* Copyright(c) 2013-2019 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdint.h>
#include <fcntl.h>
#include <stdbool.h>
#define __KERNEL__
#include <linux/types.h>
#include <linux/byteorder/little_endian.h>
/*
Next types, definitions and functions were copied from kernel 4.19.24 source
code, specifically from nvdimm driver
*/
#define PFN_SIG_LEN 16
#define PFN_SIG "NVDIMM_PFN_INFO"
#define SZ_4K 0x00001000
typedef __u16 u16;
typedef __u8 u8;
typedef __u64 u64;
typedef __u32 u32;
enum nd_pfn_mode {
PFN_MODE_NONE,
PFN_MODE_RAM,
PFN_MODE_PMEM,
};
struct nd_pfn_sb {
u8 signature[PFN_SIG_LEN];
u8 uuid[16];
u8 parent_uuid[16];
__le32 flags;
__le16 version_major;
__le16 version_minor;
__le64 dataoff; /* relative to namespace_base + start_pad */
__le64 npfns;
__le32 mode;
/* minor-version-1 additions for section alignment */
__le32 start_pad;
__le32 end_trunc;
/* minor-version-2 record the base alignment of the mapping */
__le32 align;
u8 padding[4000];
__le64 checksum;
};
struct nd_gen_sb {
char reserved[SZ_4K - 8];
__le64 checksum;
};
u64 nd_fletcher64(void *addr, size_t len, bool le)
{
u32 *buf = addr;
u32 lo32 = 0;
u64 hi32 = 0;
int i;
for (i = 0; i < len / sizeof(u32); i++) {
lo32 += le ? __le32_to_cpu((__le32) buf[i]) : buf[i];
hi32 += lo32;
}
return hi32 << 32 | lo32;
}
/*
* nd_sb_checksum: compute checksum for a generic info block
*
* Returns a fletcher64 checksum of everything in the given info block
* except the last field (since that's where the checksum lives).
*/
u64 nd_sb_checksum(struct nd_gen_sb *nd_gen_sb)
{
u64 sum;
__le64 sum_save;
sum_save = nd_gen_sb->checksum;
nd_gen_sb->checksum = 0;
sum = nd_fletcher64(nd_gen_sb, sizeof(*nd_gen_sb), 1);
nd_gen_sb->checksum = sum_save;
return sum;
}
void show_usage(const char* name) {
printf("Usage: %s IMAGE_FILE DATA_OFFSET ALIGNMENT\n", name);
printf("DATA_OFFSET and ALIGNMENT must be in bytes\n");
}
int main(int argc, char *argv[]) {
if (argc != 4) {
show_usage(argv[0]);
return -1;
}
const char* img_path = argv[1];
char *ptr = NULL;
const long int data_offset = strtol(argv[2], &ptr, 10);
if (ptr == argv[2]) {
fprintf(stderr, "Couldn't convert string '%s' to int\n", argv[2]);
show_usage(argv[0]);
return -1;
}
ptr = NULL;
const long int alignment = strtol(argv[3], &ptr, 10);
if (ptr == argv[3]) {
fprintf(stderr, "Couldn't convert string '%s' to int\n", argv[3]);
show_usage(argv[0]);
return -1;
}
printf("Opening file '%s'\n", img_path);
int fd = open(img_path, O_WRONLY);
if (fd == -1) {
perror("open:");
return -1;
}
struct nd_pfn_sb sb = { 0 };
snprintf((char*)sb.signature, PFN_SIG_LEN, PFN_SIG);
sb.mode = PFN_MODE_RAM;
sb.align = alignment;
sb.dataoff = data_offset;
sb.version_minor = 2;
// checksum must be calculated at the end
sb.checksum = nd_sb_checksum((struct nd_gen_sb*) &sb);
// NVDIMM driver: SZ_4K is the namespace-relative starting offset
int ret = lseek(fd, SZ_4K, SEEK_SET);
if (ret == -1) {
perror("lseek: ");
close(fd);
return -1;
}
printf("Writing metadata\n");
ret = write(fd, &sb, sizeof(sb));
if (ret == -1) {
perror("write: ");
}
close(fd);
printf("OK!\n");
return 0;
}

View File

@ -0,0 +1,25 @@
* [Creating a guest OS initrd image](#creating-a-guest-os-initrd-image)
* [Further information](#further-information)
# Kata Containers initrd image generation
A Kata Containers initrd image is generated using the `initrd_builder.sh` script.
This script uses a rootfs directory created by the `rootfs-builder/rootfs.sh` script.
## Creating a guest OS initrd image
To create a guest OS initrd image run:
```
$ sudo ./initrd_builder.sh path/to/rootfs
```
The `rootfs.sh` script populates the `path/to/rootfs` directory.
## Further information
For more information on how to use the `initrd_builder.sh` script, run:
```
$ ./initrd_builder.sh -h
```

View File

@ -0,0 +1,77 @@
#!/bin/bash
#
# Copyright (c) 2018 HyperHQ Inc.
#
# SPDX-License-Identifier: Apache-2.0
set -e
[ -n "$DEBUG" ] && set -x
script_name="${0##*/}"
script_dir="$(dirname $(readlink -f $0))"
lib_file="${script_dir}/../scripts/lib.sh"
source "$lib_file"
INITRD_IMAGE="${INITRD_IMAGE:-kata-containers-initrd.img}"
AGENT_BIN=${AGENT_BIN:-kata-agent}
AGENT_INIT=${AGENT_INIT:-no}
usage()
{
error="${1:-0}"
cat <<EOT
Usage: ${script_name} [options] <rootfs-dir>
This script creates a Kata Containers initrd image file based on the
<rootfs-dir> directory.
Options:
-h Show help
-o Set the path where the generated image file is stored.
DEFAULT: the path stored in the environment variable INITRD_IMAGE
Extra environment variables:
AGENT_BIN: use it to change the expected agent binary name
DEFAULT: kata-agent
AGENT_INIT: use kata agent as init process
DEFAULT: no
EOT
exit "${error}"
}
while getopts "ho:" opt
do
case "$opt" in
h) usage ;;
o) INITRD_IMAGE="${OPTARG}" ;;
esac
done
shift $(( $OPTIND - 1 ))
ROOTFS="$1"
[ -n "${ROOTFS}" ] || usage
[ -d "${ROOTFS}" ] || die "${ROOTFS} is not a directory"
ROOTFS=$(readlink -f ${ROOTFS})
IMAGE_DIR=$(dirname ${INITRD_IMAGE})
IMAGE_DIR=$(readlink -f ${IMAGE_DIR})
IMAGE_NAME=$(basename ${INITRD_IMAGE})
# The kata rootfs image expects init to be installed
init="${ROOTFS}/sbin/init"
[ -x "${init}" ] || [ -L ${init} ] || die "/sbin/init is not installed in ${ROOTFS}"
OK "init is installed"
[ "${AGENT_INIT}" == "yes" ] || [ -x "${ROOTFS}/usr/bin/${AGENT_BIN}" ] || \
die "/usr/bin/${AGENT_BIN} is not installed in ${ROOTFS}
use AGENT_BIN env variable to change the expected agent binary name"
OK "Agent is installed"
# initramfs expects /init
ln -sf /sbin/init "${ROOTFS}/init"
info "Creating ${IMAGE_DIR}/${IMAGE_NAME} based on rootfs at ${ROOTFS}"
( cd "${ROOTFS}" && find . | cpio -H newc -o | gzip -9 ) > "${IMAGE_DIR}"/"${IMAGE_NAME}"

View File

@ -0,0 +1 @@
Dockerfile

View File

@ -0,0 +1,212 @@
* [Building a Guest OS rootfs for Kata Containers](#building-a-guest-os-rootfs-for-kata-containers)
* [Supported base OSs](#supported-base-oss)
* [Extra features](#extra-features)
* [Supported distributions list](#supported-distributions-list)
* [Generate Kata specific files](#generate-kata-specific-files)
* [Rootfs requirements](#rootfs-requirements)
* [Creating a rootfs](#creating-a-rootfs)
* [Creating a rootfs with kernel modules](#creating-a-rootfs-with-kernel-modules)
* [Build a rootfs using Docker](#build-a-rootfs-using-docker)
* [Adding support for a new guest OS](#adding-support-for-a-new-guest-os)
* [Create template files](#create-template-files)
* [Modify template files](#modify-template-files)
* [Expected rootfs directory content](#expected-rootfs-directory-content)
* [Optional - Customize the rootfs](#optional---customize-the-rootfs)
* [Adding extra packages](#adding-extra-packages)
* [Arbitrary rootfs changes](#arbitrary-rootfs-changes)
# Building a Guest OS rootfs for Kata Containers
The Kata Containers rootfs is created using the `rootfs.sh` script.
## Supported base OSs
The `rootfs.sh` script builds a rootfs based on a particular Linux\*
distribution. The script supports multiple distributions and can be extended
to add further ones.
### Extra features
#### Supported distributions list
List the supported distributions by running the following:
```
$ ./rootfs.sh -l
```
#### Generate Kata specific files
The `rootfs.sh` script can be used to populate a directory with only Kata specific files and
components, without creating a full usable rootfs.
This feature is used to create a rootfs based on a distribution not officially
supported by osbuilder, and when building an image using the dracut build method.
To achieve this, simply invoke `rootfs.sh` without specifying a target rootfs, e.g.:
```
$ mkdir kata-overlay
$ ./rootfs.sh -r "$PWD/kata-overlay"
```
## Rootfs requirements
The rootfs must provide at least the following components:
- [Kata agent](https://github.com/kata-containers/agent)
Path: `/bin/kata-agent` - Kata Containers guest.
- An `init` system (e.g. `systemd`) to start the Kata agent
when the guest OS boots.
Path: `/sbin/init` - init binary called by the kernel.
When the `AGENT_INIT` environment variable is set to `yes`, use Kata agent as `/sbin/init`.
> **Note**: `AGENT_INIT=yes` **must** be used for the Alpine distribution
> since it does not use `systemd` as its init daemon.
## Creating a rootfs
To build a rootfs for your chosen distribution, run:
```
$ sudo ./rootfs.sh <distro>
```
## Creating a rootfs with kernel modules
To build a rootfs with additional kernel modules, run:
```
$ sudo KERNEL_MODULES_DIR=${kernel_mod_dir} ./rootfs.sh <distro>
```
Where `kernel_mod_dir` points to the kernel modules directory to be put under the
`/lib/modules/` directory of the created rootfs.
## Build a rootfs using Docker
Depending on the base OS to build the rootfs guest OS, it is required some
specific programs that probably are not available or installed in the system
that will build the guest image. For this case `rootfs.sh` can use
a Docker\* container to build the rootfs. The following requirements
must be met:
1. Docker 1.12+ installed.
2. `runc` is configured as the default runtime.
To check if `runc` is the default runtime:
```
$ docker info | grep 'Default Runtime: runc'
```
Note:
This requirement is specific to the Clear Containers runtime.
See [issue](https://github.com/clearcontainers/runtime/issues/828) for
more information.
3. Export `USE_DOCKER` variable.
```
$ export USE_DOCKER=true
```
4. Use `rootfs.sh`:
Example:
```
$ export USE_DOCKER=true
$ # build guest O/S rootfs based on fedora
$ ./rootfs-builder/rootfs.sh -r "${PWD}/fedora_rootfs" fedora
$ # build image based rootfs created above
$ ./image-builder/image_builder.sh "${PWD}/fedora_rootfs"
```
## Adding support for a new guest OS
The `rootfs.sh` script will check for immediate sub-directories
containing the following expected files:
- A `bash(1)` script called `config.sh`
This represents the specific configuration for `<distro>`. It must
provide configuration specific variables for the user to modify as needed.
The `config.sh` file will be loaded before executing `build_rootfs()` to
provide all the needed configuration to the function.
Path: `rootfs-builder/<distro>/config.sh`.
- (OPTIONAL) A `bash(1)` script called `rootfs_lib.sh`
This file must contain a function called `build_rootfs()`, which must
receive the path to where the rootfs is created, as its first argument.
Normally, this file is needed if a new distro with a special requirement
is needed. This function will override the `build_rootfs()` function in
`scripts/lib.sh`.
Path: `rootfs-builder/<distro>/rootfs_lib.sh`.
### Create template files
To create a directory with the expected file structure run:
```
$ make -f template/Makefile ROOTFS_BASE_NAME=my_new_awesome_rootfs
```
After running the previous command, a new directory is created in
`rootfs-builder/my_new_awesome_rootfs/`.
To verify the directory can be used to build a rootfs, run `./rootfs.sh -h`.
Running this script shows `my_new_awesome_rootfs` as one of the options for
use. To use the new guest OS, follow the instructions in [Creating a rootfs](#creating-a-rootfs).
### Modify template files
After the new directory structure is created:
- If needed, add configuration variables to
`rootfs-builder/my_new_awesome_rootfs/config.sh`.
- Implement the stub `build_rootfs()` function from
`rootfs-builder/my_new_awesome_rootfs/rootfs_lib.sh`.
### Expected rootfs directory content
After the function `build_rootfs` is called, the script expects the
rootfs directory to contain `/sbin/init` and `/sbin/kata-agent` binaries.
### Optional - Customize the rootfs
For particular use cases developers might want to modify the guest OS.
#### Adding extra packages
To add additional packages, use one of the following methods:
- Use the environment variable `EXTRA_PKGS` to provide a list of space-separated
packages to install.
Note:
The package names might vary among Linux distributions, the extra
package names must exist in the base OS flavor you use to build the
rootfs from.
Example:
```
$ EXTRA_PKGS="vim emacs" ./rootfs-builder/rootfs.sh -r ${PWD}/myrootfs fedora
```
- Modify the variable `PACKAGES` in `rootfs-builder/<distro>/config.sh`.
This variable specifies the minimal set of packages needed. The
configuration file must use the package names from the distro for which they
were created.
#### Arbitrary rootfs changes
Once the rootfs directory is created, you can add and remove files as
needed. Changes affect the files included in the final guest image.

View File

@ -0,0 +1,41 @@
#
# Copyright (c) 2018 HyperHQ Inc.
#
# SPDX-License-Identifier: Apache-2.0
From docker.io/golang:@GO_VERSION@-alpine
RUN apk update && apk add \
apk-tools-static \
autoconf \
automake \
bash \
binutils \
cmake \
coreutils \
curl \
g++ \
gcc \
git \
libc-dev \
libseccomp \
libseccomp-dev \
linux-headers \
m4 \
make \
musl \
musl-dev \
tar \
vim
# alpine doesn't support x86_64-unknown-linux-gnu
# It only support x86_64-unknown-linux-musl. Even worse,
# it doesn't support proc-macro, which is needed for serde_derive
#
# See issue: https://github.com/kata-containers/osbuilder/issues/386
# -- FIXME
#
# Thus, we cannot build rust agent on alpine
# The way to use alpine is to generate rootfs or build
# go agent to get rootfs and then cp rust agent to rootfs.
# pity..
# RUN ln -svf /usr/bin/gcc /bin/musl-gcc; ln -svf /usr/bin/g++ /bin/musl-g++

View File

@ -0,0 +1,26 @@
#
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
OS_NAME="Alpine"
OS_VERSION=${OS_VERSION:-latest-stable}
BASE_PACKAGES="alpine-base"
# Alpine mirror to use
# See a list of mirrors at http://nl.alpinelinux.org/alpine/MIRRORS.txt
MIRROR=http://dl-5.alpinelinux.org/alpine
# Mandatory Packages that must be installed
# - iptables: Need by Kata agent
PACKAGES="iptables"
# Init process must be one of {systemd,kata-agent}
INIT_PROCESS=kata-agent
# List of zero or more architectures to exclude from build,
# as reported by `uname -m`
ARCH_EXCLUDE_LIST=()
[ "$SECCOMP" = "yes" ] && PACKAGES+=" libseccomp" || true

View File

@ -0,0 +1,44 @@
#!/bin/bash
#
# Copyright (c) 2018 HyperHQ Inc.
#
# SPDX-License-Identifier: Apache-2.0
# - Arguments
# rootfs_dir=$1
#
# - Optional environment variables
#
# EXTRA_PKGS: Variable to add extra PKGS provided by the user
#
# BIN_AGENT: Name of the Kata-Agent binary
#
# Any other configuration variable for a specific distro must be added
# and documented on its own config.sh
#
# - Expected result
#
# rootfs_dir populated with rootfs pkgs
# It must provide a binary in /sbin/init
build_rootfs() {
# Mandatory
local ROOTFS_DIR=$1
# In case of support EXTRA packages, use it to allow
# users add more packages to the base rootfs
local EXTRA_PKGS=${EXTRA_PKGS:-}
# Populate ROOTFS_DIR
check_root
mkdir -p "${ROOTFS_DIR}"
/sbin/apk.static \
-X ${MIRROR}/${OS_VERSION}/main \
-U \
--allow-untrusted \
--root ${ROOTFS_DIR}\
--initdb add ${BASE_PACKAGES} ${EXTRA_PKGS} ${PACKAGES}
mkdir -p ${ROOTFS_DIR}{/root,/etc/apk,/proc}
echo "${MIRROR}/${OS_VERSION}/main" > ${ROOTFS_DIR}/etc/apk/repositories
}

View File

@ -0,0 +1,41 @@
#
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
From docker.io/centos:@OS_VERSION@
@SET_PROXY@
RUN yum -y update && yum install -y \
autoconf \
automake \
binutils \
chrony \
coreutils \
curl \
gcc \
gcc-c++ \
git \
glibc-common \
glibc-devel \
glibc-headers \
glibc-static \
glibc-utils \
libseccomp \
libseccomp-devel \
libstdc++-devel \
libstdc++-static \
m4 \
make \
sed \
tar \
vim \
which
# install cmake because centos7's cmake is too old
@INSTALL_CMAKE@
@INSTALL_MUSL@
# This will install the proper golang to build Kata components
@INSTALL_GO@
@INSTALL_RUST@

View File

@ -0,0 +1,38 @@
#
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
OS_NAME="Centos"
OS_VERSION=${OS_VERSION:-7}
LOG_FILE="/var/log/yum-centos.log"
MIRROR_LIST="http://mirrorlist.centos.org/?release=${OS_VERSION}&arch=${ARCH}&repo=os&container=container"
# Aditional Repos
CENTOS_UPDATES_MIRROR_LIST="http://mirrorlist.centos.org/?release=${OS_VERSION}&arch=${ARCH}&repo=updates&container=container"
CENTOS_EXTRAS_MIRROR_LIST="http://mirrorlist.centos.org/?release=${OS_VERSION}&arch=${ARCH}&repo=extras&container=container"
CENTOS_PLUS_MIRROR_LIST="http://mirrorlist.centos.org/?release=${OS_VERSION}&arch=${ARCH}&repo=centosplus&container=container"
GPG_KEY_URL="https://www.centos.org/keys/RPM-GPG-KEY-CentOS-7"
GPG_KEY_FILE="RPM-GPG-KEY-CentOS-7"
PACKAGES="iptables chrony"
#Optional packages:
# systemd: An init system that will start kata-agent if kata-agent
# itself is not configured as init process.
[ "$AGENT_INIT" == "no" ] && PACKAGES+=" systemd" || true
# Init process must be one of {systemd,kata-agent}
INIT_PROCESS=systemd
# List of zero or more architectures to exclude from build,
# as reported by `uname -m`
ARCH_EXCLUDE_LIST=()
[ "$SECCOMP" = "yes" ] && PACKAGES+=" libseccomp" || true

View File

@ -0,0 +1,18 @@
#
# Copyright (c) 2018 ARM Limited
#
# SPDX-License-Identifier: Apache-2.0
# Base Repos
BASE_URL="http://mirror.centos.org/altarch/${OS_VERSION}/os/${ARCH}/"
# Additional Repos
CENTOS_UPDATES_URL="http://mirror.centos.org/altarch/${OS_VERSION}/updates/${ARCH}/"
CENTOS_EXTRAS_URL="http://mirror.centos.org/altarch/${OS_VERSION}/extras/${ARCH}/"
CENTOS_PLUS_URL="http://mirror.centos.org/altarch/${OS_VERSION}/centosplus/${ARCH}/"
GPG_KEY_ARCH_URL="http://mirror.centos.org/altarch/7/os/aarch64/RPM-GPG-KEY-CentOS-7-aarch64"
GPG_KEY_ARCH_FILE="RPM-GPG-KEY-CentOS-7-aarch64"

View File

@ -0,0 +1,18 @@
#
# Copyright (c) 2018 IBM
#
# SPDX-License-Identifier: Apache-2.0
# Base Repos
BASE_URL="http://mirror.centos.org/altarch/${OS_VERSION}/os/${ARCH}/"
# Additional Repos
CENTOS_UPDATES_URL="http://mirror.centos.org/altarch/${OS_VERSION}/updates/${ARCH}/"
CENTOS_EXTRAS_URL="http://mirror.centos.org/altarch/${OS_VERSION}/extras/${ARCH}/"
CENTOS_PLUS_URL="http://mirror.centos.org/altarch/${OS_VERSION}/centosplus/${ARCH}/"
GPG_KEY_ARCH_URL="http://mirror.centos.org/altarch/7/os/ppc64le/RPM-GPG-KEY-CentOS-SIG-AltArch-7-ppc64le"
GPG_KEY_ARCH_FILE="RPM-GPG-KEY-CentOS-SIG-AltArch-7-ppc64le"

View File

@ -0,0 +1,43 @@
#
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
From docker.io/fedora:30
@SET_PROXY@
RUN dnf -y update && dnf install -y \
autoconf \
automake \
binutils \
chrony \
cmake \
coreutils \
curl \
curl \
gcc \
gcc-c++ \
git \
glibc-common \
glibc-devel \
glibc-headers \
glibc-static \
glibc-utils \
libseccomp \
libseccomp-devel \
libstdc++-devel \
libstdc++-static \
m4 \
make \
pkgconfig \
sed \
systemd \
tar \
vim \
which
# This will install the proper golang to build Kata components
@INSTALL_MUSL@
@INSTALL_GO@
@INSTALL_RUST@

View File

@ -0,0 +1,31 @@
#
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
OS_NAME="Clear"
REPO_NAME="clear"
OS_VERSION=${OS_VERSION:-latest}
clr_url="https://download.clearlinux.org"
# resolve version
[ "${OS_VERSION}" = "latest" ] && OS_VERSION=$(curl -sL "${clr_url}/latest")
BASE_URL="${clr_url}/releases/${OS_VERSION}/${REPO_NAME}/${ARCH}/os/"
PACKAGES="libudev0-shim kmod-bin"
#Optional packages:
# systemd: An init system that will start kata-agent if kata-agent
# itself is not configured as init process.
[ "$AGENT_INIT" == "no" ] && PACKAGES+=" systemd chrony iptables-bin util-linux-bin" || true
# Init process must be one of {systemd,kata-agent}
INIT_PROCESS=systemd
# List of zero or more architectures to exclude from build,
# as reported by `uname -m`
ARCH_EXCLUDE_LIST=( aarch64 ppc64le s390x )
[ "$SECCOMP" = "yes" ] && PACKAGES+=" libseccomp" || true

View File

@ -0,0 +1,35 @@
#
# Copyright (c) 2020 ARM Limited
#
# SPDX-License-Identifier: Apache-2.0
# NOTE: OS_VERSION is set according to config.sh
from docker.io/debian:@OS_VERSION@
# RUN commands
RUN apt-get update && apt-get install -y \
autoconf \
automake \
binutils \
build-essential \
chrony \
cmake \
coreutils \
curl \
debianutils \
debootstrap \
g++ \
gcc \
git \
libc-dev \
libstdc++-6-dev \
m4 \
make \
sed \
systemd \
tar \
vim
# This will install the proper golang to build Kata components
@INSTALL_GO@
@INSTALL_MUSL@
@INSTALL_RUST@

View File

@ -0,0 +1,41 @@
#
# Copyright (c) 2018 SUSE
#
# SPDX-License-Identifier: Apache-2.0
# NOTE: OS_VERSION is set according to config.sh
from docker.io/debian:@OS_VERSION@
# RUN commands
RUN apt-get update && apt-get --no-install-recommends install -y \
apt-utils \
autoconf \
automake \
binutils \
build-essential \
ca-certificates \
chrony \
cmake \
coreutils \
curl \
debianutils \
debootstrap \
g++ \
gcc \
git \
libc-dev \
libstdc++-6-dev \
m4 \
make \
musl \
musl-dev \
musl-tools \
sed \
systemd \
tar \
vim \
wget
# This will install the proper golang to build Kata components
@INSTALL_GO@
@INSTALL_RUST@

View File

@ -0,0 +1,20 @@
#
# Copyright (c) 2018 SUSE
#
# SPDX-License-Identifier: Apache-2.0
OS_VERSION=${OS_VERSION:-9.5}
# Set OS_NAME to the desired debian "codename"
OS_NAME=${OS_NAME:-"stretch"}
PACKAGES="systemd iptables init chrony kmod"
# NOTE: Re-using ubuntu rootfs configuration, see 'ubuntu' folder for full content.
source $script_dir/ubuntu/$CONFIG_SH
# Init process must be one of {systemd,kata-agent}
INIT_PROCESS=systemd
# List of zero or more architectures to exclude from build,
# as reported by `uname -m`
ARCH_EXCLUDE_LIST=()

View File

@ -0,0 +1,7 @@
#
# Copyright (c) 2018 SUSE
#
# SPDX-License-Identifier: Apache-2.0
# NOTE: Re-using ubuntu rootfs lib, see 'ubuntu' folder for details.
source ${script_dir}/ubuntu/$LIB_SH

View File

@ -0,0 +1,46 @@
#
# Copyright (C) 2018 Huawei Technologies Co., Ltd
#
# SPDX-License-Identifier: Apache-2.0
FROM docker.io/euleros:@OS_VERSION@
@SET_PROXY@
RUN yum -y update && yum install -y \
autoconf \
automake \
binutils \
chrony \
coreutils \
curl \
gcc \
gcc-c++ \
git \
glibc-common \
glibc-devel \
glibc-headers \
glibc-static \
glibc-utils \
libstdc++-devel \
libstdc++-static \
m4 \
make \
sed \
tar \
vim \
which \
yum
# This will install the proper golang to build Kata components
@INSTALL_GO@
# several problems prevent us from building rust agent on euleros
# 1. There is no libstdc++.a. copy one from somewhere get through
# compilation
# 2. The kernel (3.10.x) is too old, kernel-headers pacakge
# has no vm_socket.h because kernel has no vsock support or
# vsock header files
# We will disable rust agent build in rootfs.sh for euleros
# and alpine(musl cannot support proc-macro)

View File

@ -0,0 +1,52 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2
mQINBFhFFc8BEADu77vsD7rA1zCTreI9Ex9dIbWWR0Ntu4e7OL+VSIxXserWron2
kTHagPIrDGtFqWTQgbt4tpjJ8vOAMzCADYq2eNRbEbUL/TOGfYk5Lgfo0P7F5Slr
dXNow2HrZhxehTwRSvseQg9Yrx2LVXDgr8wAMLldnkCSa0iyAE90ehDLOUaf2Lal
c99p+4tw8GhWP7C41pX4ywLrJ1FXodFTpg+I7p9EW5zt5mZhwX7NkhdoISnNAA6L
R5NA+6G8rCC1fdTGfqYPfNGrO9DBSZNfunWZsN+kYo4ac3GbZkdnh3LA2YCW4yiA
u5AoPv1UIkFMLh0KoJDxOORMkxI++3qFAIzShtMRAQencsM85bzdXNmk3VE+nY9V
J0BHCLMELtr/o6b+e5ak3qcG1sMFBEMn367/k6suIpTF5sEszQScWeqbhdeFmXt6
mur2z6zDwwa5Y4n0x9Lsz50PxgkDrHXxeoLO5ByE8iTJqxhYSl0hb/bhSmBaYXnW
JiqtoLbYW/isgZ8OW414P2ZUwgByA9O4Tso37oEU69ycrxFVI63M5xUGkchI+HBo
VB9XZ7QzjU8SGoelj5YtjV7og974dcXC4NwUTnhJW3pd3MfiA3C96voCN/ozjzpg
uJGg0vzuTUcHAIMhujWPWCb0YN6fr5z+7Et8yqPv4qt3fgaxdVO5qQds1wARAQAB
tC5FdWxlck9TIChFdWxlck9TIDIuMCBTUDIpIDxFdWxlck9TQGh1YXdlaS5jb20+
iQI/BBMBAgApBQJYRRXPAhsDBQkJZgGABwsJCAcDAgEGFQgCCQoLBBYCAwECHgEC
F4AACgkQYAMXvDgdesNPCxAAh7huw08/oFHpCSN9dYd/YlFfCs/+wb2KUqZZ2yIK
SSpmRmQiQdJRUiJly69WZL4H2NYCw3MQiV8Q433err3iQXMjumfl5hq2KplMgsAQ
sraOreJPeN1687rzEV9eDjuKV7btd2VaSyiMIaAFaWjoxl6E77x8ifNbXcnTHk+5
39BCRn3WsSXbQKWolFEvwNr/SYzGIIdtmrlZSog/vAKPqzTsJDj/Qsf/0Uec1iCX
6pnZwMrQTlc+nnnAp9bMVla39uWGwyhhicTsokElm/4wD+OaLF2xz3gWk3l3fHjh
V8PtzhQxpHlyqR7pOvG5eun9VsBeWwH6TcHU1B+cPi4SmQcflvJCV/XCTTcK6Z1i
/35cmZdwCoDnM+Aadfywfoaliy5rnsvvMSljI+hw8gX3NACIDd2RBPmER3wknZw6
bIpm0vtlaG1fcCio0kFo9CplLYbYHtx9Y7Icln3O3keODlR+rc8HI5X0YPkLa9Fs
mqP0fN2PGcEPm7CjoEdFdfOJn+1TvR7T1cnBiso5hLcMPtX8b6vzvIrFy5OKq90N
LYjgdn8LMmE6Gi/LA6yEBB958vGS5kAQI3HvCmw9vBeGdVZ+QXjmeVN6Vp9bEnBS
3oZbUXzo3CpeGxvj7+8s8j6MMvDLPLIdxXWi1ZTJkZFa+ElvZMG34SI/kSHHdSSO
gRG5Ag0EWEUVzwEQANBn+RzOAOl8OVPBtmDRIC/G7yssy7Q3ZGWUDIxs2NNk2oBH
9RsCm+vYeQkScloed+Cv6dkQvCPiFk+VtlLeSl0ugmvjNjZknuMhbgiC1ObR2SmV
uNpT3qMaNQQBJg0tJGU/1hLHDqjj2TGvj+WJDfzRoVie1dHq6bnogOErEXvKGmNZ
/cDuvmeURmFqx/+cwim2QFc95hcylBXRhnTnGblgxjzYXnXbIMvtCNz3Nnd1yT3P
9Z+h7Mwk746UEK2R1EgpVzZa9YF/mg2NRwBFuuJ4yP0MxmzP1AMgqQSp7XrMP6KG
6RbmDymrTHFTkP/lI3qZ1bgNB64bq/Eq1J3qgukEDN8JZKMiG+/vAg3lkPQwn3Uy
8IfBCqVrF0/dg+kJesgEMs6T+CsINWQ/SEPYHT/6LGytr+4MgDVqI1wxII8gBZzk
FHohleNRWvKKGLphECO7NwgrDFwWlIsT46d1Hga0uHNDSg1mczU2swYHD7/j1HQE
McByTVuzUjT/eAxmbN+DZ4cGBccqMP8RkZfBpalhB5lyjnIN2tMJ3y3yZrpmJkU1
LaetdFqwycMmV7Mmi2dEdqumnmKhSZqyJ1ShuSm9pEBxahwJGdhtC6Id7iwzZ3uJ
53nhO7hvGC0gt3w0frX0TcvT7aFa4ZsgaJxUJy0MKDPZmv/3hYKpH/QkLiu7ABEB
AAGJAiUEGAECAA8FAlhFFc8CGwwFCQlmAYAACgkQYAMXvDgdesO+fw//bQImNhW0
ZwG5FG7oP+KPgmma2+N/JnzemqEUzjRTIiEN4LCj8qvJ/aKYZJkfUcKvP4kpVW66
+tlJ11Ie9Bnkqm3GdT1nkWDghzTK7/x6ktRwyuowmEYh01fW3bybB0RcQOJzGnMK
umnNzd4VUdMGwdbg/sQnKc6lMU9+hz/tCOU9Ok6Ps384gRXjmRQ+J9EFHq14kXtP
Xy584MD1+OBsPwlMViAAjV9L3pxtS1JoFplNPYogbBOKHdImS3dNOMLwV3dHAf1d
l0MqgMEabLBQusx2q7CUw4xBi5EJJtnos9bJvGSCplDyjlshDiY7wxcFLLb90VWs
TnJVbDswCjsdVi5x8eyPplygGxgt9Qg2XNYN5EgN9MLbmbC7Mi7oRf1E7QMLuuQ+
+lkTb1rAe4YewwwAZHao4zGJelNXmSPN8u8s/zUrnFKG78qjLDZW9kGvkFpElOPj
KkgsSaTn8kbxWoyR9wKW56onTos6eMfhItLCFy5/oAD3sIp5aCsATuJZPSAtDKxw
1jzQRx4KOOYYrsS1qMd7gG151/QM15E56gdi+6gaeLcz8YQ2zcvxg5eabUDKp+bP
I47NsT6rLAhV5mTB0NneC//Yng7JJ0q0jkiJu49BQ1if6Pz8txDxBs4U3mvCw2rA
qSxRE/XMoebNx2CFQwFp7izDHwuG6uRRUQQ=
=3beT
-----END PGP PUBLIC KEY BLOCK-----

View File

@ -0,0 +1,29 @@
#
# Copyright (C) 2018 Huawei Technologies Co., Ltd
#
# SPDX-License-Identifier: Apache-2.0
OS_NAME="EulerOS"
OS_VERSION=${OS_VERSION:-2.2}
BASE_URL="http://developer.huawei.com/ict/site-euleros/euleros/repo/yum/${OS_VERSION}/os/${ARCH}/"
GPG_KEY_FILE="RPM-GPG-KEY-EulerOS"
PACKAGES="iptables chrony"
#Optional packages:
# systemd: An init system that will start kata-agent if kata-agent
# itself is not configured as init process.
[ "$AGENT_INIT" == "no" ] && PACKAGES+=" systemd" || true
# Init process must be one of {systemd,kata-agent}
INIT_PROCESS=systemd
# List of zero or more architectures to exclude from build,
# as reported by `uname -m`
ARCH_EXCLUDE_LIST=( aarch64 ppc64le s390x )
# Allow the build to fail without generating an error.
# For more info see: https://github.com/kata-containers/osbuilder/issues/190
BUILD_CAN_FAIL=1
[ "$SECCOMP" = "yes" ] && PACKAGES+=" libseccomp" || true

View File

@ -0,0 +1,43 @@
#
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
From docker.io/fedora:@OS_VERSION@
@SET_PROXY@
RUN dnf -y update && dnf install -y \
autoconf \
automake \
binutils \
chrony \
cmake \
coreutils \
curl \
gcc \
gcc-c++ \
git \
glibc-common \
glibc-devel \
glibc-headers \
glibc-static \
glibc-utils \
libseccomp \
libseccomp-devel \
libstdc++-devel \
libstdc++-static \
m4 \
make \
pkgconfig \
redhat-release \
sed \
systemd \
tar \
vim \
which
# This will install the proper golang to build Kata components
@INSTALL_MUSL@
@INSTALL_GO@
@INSTALL_RUST@

View File

@ -0,0 +1,23 @@
#
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
OS_NAME="Fedora"
OS_VERSION=${OS_VERSION:-30}
MIRROR_LIST="https://mirrors.fedoraproject.org/metalink?repo=fedora-${OS_VERSION}&arch=\$basearch"
PACKAGES="iptables chrony"
#Optional packages:
# systemd: An init system that will start kata-agent if kata-agent
# itself is not configured as init process.
[ "$AGENT_INIT" == "no" ] && PACKAGES+=" systemd" || true
# Init process must be one of {systemd,kata-agent}
INIT_PROCESS=systemd
ARCH_EXCLUDE_LIST=()
[ "$SECCOMP" = "yes" ] && PACKAGES+=" libseccomp" || true

View File

@ -0,0 +1,10 @@
#
# Copyright (c) 2019 ARM Limited
#
# SPDX-License-Identifier: Apache-2.0
# image busybox will fail on fedora 30 rootfs image
# see https://github.com/kata-containers/osbuilder/issues/334 for detailed info
OS_VERSION="29"
MIRROR_LIST="https://mirrors.fedoraproject.org/metalink?repo=fedora-${OS_VERSION}&arch=\$basearch"

View File

@ -0,0 +1,686 @@
#!/bin/bash
#
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
set -o errexit
set -o pipefail
set -o errtrace
[ -n "$DEBUG" ] && set -x
script_name="${0##*/}"
script_dir="$(dirname $(readlink -f $0))"
AGENT_VERSION=${AGENT_VERSION:-}
GO_AGENT_PKG=${GO_AGENT_PKG:-github.com/kata-containers/agent}
RUST_AGENT_PKG=${RUST_AGENT_PKG:-github.com/kata-containers/kata-containers}
RUST_AGENT=${RUST_AGENT:-no}
RUST_VERSION="null"
CMAKE_VERSION=${CMAKE_VERSION:-"null"}
MUSL_VERSION=${MUSL_VERSION:-"null"}
AGENT_BIN=${AGENT_BIN:-kata-agent}
AGENT_INIT=${AGENT_INIT:-no}
KERNEL_MODULES_DIR=${KERNEL_MODULES_DIR:-""}
OSBUILDER_VERSION="unknown"
DOCKER_RUNTIME=${DOCKER_RUNTIME:-runc}
GO_VERSION="null"
export GOPATH=${GOPATH:-${HOME}/go}
lib_file="${script_dir}/../scripts/lib.sh"
source "$lib_file"
handle_error() {
local exit_code="${?}"
local line_number="${1:-}"
echo "Failed at $line_number: ${BASH_COMMAND}"
exit "${exit_code}"
}
trap 'handle_error $LINENO' ERR
# Default architecture
ARCH=$(uname -m)
# distro-specific config file
typeset -r CONFIG_SH="config.sh"
# optional arch-specific config file
typeset -r CONFIG_ARCH_SH="config_${ARCH}.sh"
# Name of an optional distro-specific file which, if it exists, must implement the
# build_rootfs() function.
typeset -r LIB_SH="rootfs_lib.sh"
# rootfs distro name specified by the user
typeset distro=
# Absolute path to the rootfs root folder
typeset ROOTFS_DIR
# Absolute path in the rootfs to the "init" executable / symlink.
# Typically something like "${ROOTFS_DIR}/init
typeset init=
#$1: Error code if want to exit different to 0
usage()
{
error="${1:-0}"
cat <<EOT
Usage: ${script_name} [options] [DISTRO]
Build and setup a rootfs directory based on DISTRO OS, used to create
Kata Containers images or initramfs.
When no DISTRO is provided, an existing base rootfs at ROOTFS_DIR is provisioned
with the Kata specific components and configuration.
Supported DISTRO values:
$(get_distros | tr "\n" " ")
Options:
-a <version> Specify the agent version. Overrides the AGENT_VERSION
environment variable.
-h Show this help message.
-l List the supported Linux distributions and exit immediately.
-o <version> Specify the version of osbuilder to embed in the rootfs
yaml description.
-r <directory> Specify the rootfs base directory. Overrides the ROOTFS_DIR
environment variable.
-t DISTRO Print the test configuration for DISTRO and exit
immediately.
Environment Variables:
AGENT_BIN Name of the agent binary (used when running sanity checks on
the rootfs).
Default value: ${AGENT_BIN}
AGENT_INIT When set to "yes", use ${AGENT_BIN} as init process in place
of systemd.
Default value: no
RUST_AGENT When set to "yes", build kata-agent from kata-rust-agent instead of go agent
Default value: "no"
RUST_AGENT_PKG URL of the Git repository hosting the agent package.
Default value: ${RUST_AGENT_PKG}
AGENT_VERSION Version of the agent to include in the rootfs.
Default value: ${AGENT_VERSION:-<not set>}
AGENT_SOURCE_BIN Path to the directory of agent binary.
If set, use the binary as agent but not build agent package.
Default value: <not set>
DISTRO_REPO Use host repositories to install guest packages.
Default value: <not set>
GO_AGENT_PKG URL of the Git repository hosting the agent package.
Default value: ${GO_AGENT_PKG}
GRACEFUL_EXIT If set, and if the DISTRO configuration specifies a
non-empty BUILD_CAN_FAIL variable, do not return with an
error code in case any of the build step fails.
This is used when running CI jobs, to tolerate failures for
specific distributions.
Default value: <not set>
KERNEL_MODULES_DIR Path to a directory containing kernel modules to include in
the rootfs.
Default value: <empty>
ROOTFS_DIR Path to the directory that is populated with the rootfs.
Default value: <${script_name} path>/rootfs-<DISTRO-name>
USE_DOCKER If set, build the rootfs inside a container (requires
Docker).
Default value: <not set>
USE_PODMAN If set and USE_DOCKER not set, then build the rootfs inside
a podman container (requires podman).
Default value: <not set>
DOCKER_RUNTIME Docker runtime to use when USE_DOCKER is set.
Default value: runc
Refer to the Platform-OS Compatibility Matrix for more details on the supported
architectures:
https://github.com/kata-containers/osbuilder#platform-distro-compatibility-matrix
EOT
exit "${error}"
}
get_distros() {
cdirs=$(find "${script_dir}" -maxdepth 1 -type d)
find ${cdirs} -maxdepth 1 -name "${CONFIG_SH}" -printf '%H\n' | while read dir; do
basename "${dir}"
done
}
get_test_config() {
local -r distro="$1"
[ -z "$distro" ] && die "No distro name specified"
local config="${script_dir}/${distro}/config.sh"
source ${config}
echo -e "INIT_PROCESS:\t\t$INIT_PROCESS"
echo -e "ARCH_EXCLUDE_LIST:\t\t${ARCH_EXCLUDE_LIST[@]}"
}
check_function_exist()
{
function_name="$1"
[ "$(type -t ${function_name})" == "function" ] || die "${function_name} function was not defined"
}
docker_extra_args()
{
local args=""
case "$1" in
ubuntu | debian)
# Requred to chroot
args+=" --cap-add SYS_CHROOT"
# debootstrap needs to create device nodes to properly function
args+=" --cap-add MKNOD"
;&
suse)
# Required to mount inside a container
args+=" --cap-add SYS_ADMIN"
# When AppArmor is enabled, mounting inside a container is blocked with docker-default profile.
# See https://github.com/moby/moby/issues/16429
args+=" --security-opt apparmor=unconfined"
;;
*)
;;
esac
echo "$args"
}
setup_agent_init()
{
agent_bin="$1"
init_bin="$2"
[ -z "$agent_bin" ] && die "need agent binary path"
[ -z "$init_bin" ] && die "need init bin path"
info "Install $agent_bin as init process"
mv -f "${agent_bin}" ${init_bin}
OK "Agent is installed as init process"
}
copy_kernel_modules()
{
local module_dir="$1"
local rootfs_dir="$2"
[ -z "$module_dir" ] && die "need module directory"
[ -z "$rootfs_dir" ] && die "need rootfs directory"
local dest_dir="${rootfs_dir}/lib/modules"
info "Copy kernel modules from ${KERNEL_MODULES_DIR}"
mkdir -p "${dest_dir}"
cp -a "${KERNEL_MODULES_DIR}" "${dest_dir}/"
OK "Kernel modules copied"
}
error_handler()
{
[ "$?" -eq 0 ] && return
if [ -n "$GRACEFUL_EXIT" ] && [ -n "$BUILD_CAN_FAIL" ]; then
info "Detected a build error, but $distro is allowed to fail (BUILD_CAN_FAIL specified), so exiting sucessfully"
touch "$(dirname ${ROOTFS_DIR})/${distro}_fail"
exit 0
fi
}
# Compares two SEMVER-style versions passed as arguments, up to the MINOR version
# number.
# Returns a zero exit code if the version specified by the first argument is
# older OR equal than / to the version in the second argument, non-zero exit
# code otherwise.
compare_versions()
{
typeset -i -a v1=($(echo "$1" | awk 'BEGIN {FS = "."} {print $1" "$2}'))
typeset -i -a v2=($(echo "$2" | awk 'BEGIN {FS = "."} {print $1" "$2}'))
# Sanity check: first version can't be all zero
[ "${v1[0]}" -eq "0" ] && \
[ "${v1[1]}" -eq "0" ] && \
die "Failed to parse version number"
# Major
[ "${v1[0]}" -gt "${v2[0]}" ] && { false; return; }
# Minor
[ "${v1[0]}" -eq "${v2[0]}" ] && \
[ "${v1[1]}" -gt "${v2[1]}" ] && { false; return; }
true
}
check_env_variables()
{
# Fetch the first element from GOPATH as working directory
# as go get only works against the first item in the GOPATH
[ -z "$GOPATH" ] && die "GOPATH not set"
GOPATH_LOCAL="${GOPATH%%:*}"
[ "$AGENT_INIT" == "yes" -o "$AGENT_INIT" == "no" ] || die "AGENT_INIT($AGENT_INIT) is invalid (must be yes or no)"
if [ -z "${AGENT_SOURCE_BIN}" ]; then
[ "$RUST_AGENT" == "yes" -o "$RUST_AGENT" == "no" ] || die "RUST_AGENT($RUST_AGENT) is invalid (must be yes or no)"
fi
[ -n "${KERNEL_MODULES_DIR}" ] && [ ! -d "${KERNEL_MODULES_DIR}" ] && die "KERNEL_MODULES_DIR defined but is not an existing directory"
[ -n "${OSBUILDER_VERSION}" ] || die "need osbuilder version"
}
# Builds a rootfs based on the distro name provided as argument
build_rootfs_distro()
{
[ -n "${distro}" ] || usage 1
distro_config_dir="${script_dir}/${distro}"
# Source config.sh from distro
rootfs_config="${distro_config_dir}/${CONFIG_SH}"
source "${rootfs_config}"
# Source arch-specific config file
rootfs_arch_config="${distro_config_dir}/${CONFIG_ARCH_SH}"
if [ -f "${rootfs_arch_config}" ]; then
source "${rootfs_arch_config}"
fi
[ -d "${distro_config_dir}" ] || die "Not found configuration directory ${distro_config_dir}"
if [ -z "$ROOTFS_DIR" ]; then
ROOTFS_DIR="${script_dir}/rootfs-${OS_NAME}"
fi
if [ -e "${distro_config_dir}/${LIB_SH}" ];then
rootfs_lib="${distro_config_dir}/${LIB_SH}"
info "rootfs_lib.sh file found. Loading content"
source "${rootfs_lib}"
fi
CONFIG_DIR=${distro_config_dir}
check_function_exist "build_rootfs"
if [ -z "$INSIDE_CONTAINER" ] ; then
# Capture errors, but only outside of the docker container
trap error_handler ERR
fi
mkdir -p ${ROOTFS_DIR}
detect_go_version ||
die "Could not detect the required Go version for AGENT_VERSION='${AGENT_VERSION:-master}'."
echo "Required Go version: $GO_VERSION"
# need to detect rustc's version too?
detect_rust_version ||
die "Could not detect the required rust version for AGENT_VERSION='${AGENT_VERSION:-master}'."
echo "Required rust version: $RUST_VERSION"
detect_cmake_version ||
die "Could not detect the required cmake version for AGENT_VERSION='${AGENT_VERSION:-master}'."
echo "Required cmake version: $CMAKE_VERSION"
detect_musl_version ||
die "Could not detect the required musl version for AGENT_VERSION='${AGENT_VERSION:-master}'."
echo "Required musl version: $MUSL_VERSION"
if [ -z "${USE_DOCKER}" ] && [ -z "${USE_PODMAN}" ]; then
#Generate an error if the local Go version is too old
foundVersion=$(go version | sed -E "s/^.+([0-9]+\.[0-9]+\.[0-9]+).*$/\1/g")
compare_versions "${GO_VERSION}" "${foundVersion}" || \
die "Your Go version ${foundVersion} is older than the minimum expected Go version ${GO_VERSION}"
if [ "${RUST_AGENT}" == "yes" ]; then
source "${HOME}/.cargo/env"
foundVersion=$(rustc --version | sed -E "s/^.+([0-9]+\.[0-9]+\.[0-9]+).*$/\1/g")
compare_versions "${RUST_VERSION}" "${foundVersion}" || \
die "Your rust version ${foundVersion} is older than the minimum expected rust version ${RUST_VERSION}"
foundVersion=$(cmake --version | grep "[0-9]\+.[0-9]\+.[0-9]\+" | sed -E "s/^.+([0-9]+\.[0-9]+\.[0-9]+).*$/\1/g")
fi
else
if [ -n "${USE_DOCKER}" ]; then
container_engine="docker"
elif [ -n "${USE_PODMAN}" ]; then
container_engine="podman"
fi
image_name="${distro}-rootfs-osbuilder"
# setup to install go or rust here
generate_dockerfile "${distro_config_dir}"
"$container_engine" build \
--build-arg http_proxy="${http_proxy}" \
--build-arg https_proxy="${https_proxy}" \
-t "${image_name}" "${distro_config_dir}"
# fake mapping if KERNEL_MODULES_DIR is unset
kernel_mod_dir=${KERNEL_MODULES_DIR:-${ROOTFS_DIR}}
docker_run_args=""
docker_run_args+=" --rm"
docker_run_args+=" --runtime ${DOCKER_RUNTIME}"
if [ -z "${AGENT_SOURCE_BIN}" ] ; then
if [ "$RUST_AGENT" == "no" ]; then
docker_run_args+=" --env GO_AGENT_PKG=${GO_AGENT_PKG}"
else
docker_run_args+=" --env RUST_AGENT_PKG=${RUST_AGENT_PKG}"
fi
docker_run_args+=" --env RUST_AGENT=${RUST_AGENT} -v ${GOPATH_LOCAL}:${GOPATH_LOCAL} --env GOPATH=${GOPATH_LOCAL}"
else
docker_run_args+=" --env AGENT_SOURCE_BIN=${AGENT_SOURCE_BIN}"
docker_run_args+=" -v ${AGENT_SOURCE_BIN}:${AGENT_SOURCE_BIN}"
fi
docker_run_args+=" $(docker_extra_args $distro)"
# Relabel volumes so SELinux allows access (see docker-run(1))
if command -v selinuxenabled > /dev/null && selinuxenabled ; then
SRC_VOL=("${GOPATH_LOCAL}")
for volume_dir in "${script_dir}" \
"${ROOTFS_DIR}" \
"${script_dir}/../scripts" \
"${kernel_mod_dir}" \
"${SRC_VOL[@]}"; do
chcon -Rt svirt_sandbox_file_t "$volume_dir"
done
fi
#Make sure we use a compatible runtime to build rootfs
# In case Clear Containers Runtime is installed we dont want to hit issue:
#https://github.com/clearcontainers/runtime/issues/828
"$container_engine" run \
--env https_proxy="${https_proxy}" \
--env http_proxy="${http_proxy}" \
--env AGENT_VERSION="${AGENT_VERSION}" \
--env ROOTFS_DIR="/rootfs" \
--env AGENT_BIN="${AGENT_BIN}" \
--env AGENT_INIT="${AGENT_INIT}" \
--env KERNEL_MODULES_DIR="${KERNEL_MODULES_DIR}" \
--env EXTRA_PKGS="${EXTRA_PKGS}" \
--env OSBUILDER_VERSION="${OSBUILDER_VERSION}" \
--env INSIDE_CONTAINER=1 \
--env SECCOMP="${SECCOMP}" \
--env DEBUG="${DEBUG}" \
--env HOME="/root" \
-v "${script_dir}":"/osbuilder" \
-v "${ROOTFS_DIR}":"/rootfs" \
-v "${script_dir}/../scripts":"/scripts" \
-v "${kernel_mod_dir}":"${kernel_mod_dir}" \
$docker_run_args \
${image_name} \
bash /osbuilder/rootfs.sh "${distro}"
exit $?
fi
build_rootfs ${ROOTFS_DIR}
}
# Used to create a minimal directory tree where the agent can be instaleld.
# This is used when a distro is not specified.
prepare_overlay()
{
pushd "${ROOTFS_DIR}" > /dev/null
mkdir -p ./etc ./lib/systemd ./sbin ./var
# This symlink hacking is mostly to make later rootfs
# validation work correctly for the dracut case.
# We skip this if /sbin/init exists in the rootfs, meaning
# we were passed a pre-populated rootfs directory
if [ ! -e ./sbin/init ]; then
ln -sf ./usr/lib/systemd/systemd ./init
ln -sf ../../init ./lib/systemd/systemd
ln -sf ../init ./sbin/init
fi
# Kata systemd unit file
mkdir -p ./etc/systemd/system/basic.target.wants/
ln -sf /usr/lib/systemd/system/kata-containers.target ./etc/systemd/system/basic.target.wants/kata-containers.target
popd > /dev/null
}
# Setup an existing rootfs directory, based on the OPTIONAL distro name
# provided as argument
setup_rootfs()
{
info "Create symlink to /tmp in /var to create private temporal directories with systemd"
pushd "${ROOTFS_DIR}" >> /dev/null
if [ "$PWD" != "/" ] ; then
rm -rf ./var/cache/ ./var/lib ./var/log ./var/tmp
fi
ln -s ../tmp ./var/
# For some distros tmp.mount may not be installed by default in systemd paths
if ! [ -f "./etc/systemd/system/tmp.mount" ] && \
! [ -f "./usr/lib/systemd/system/tmp.mount" ] &&
[ "$AGENT_INIT" != "yes" ]; then
local unitFile="./etc/systemd/system/tmp.mount"
info "Install tmp.mount in ./etc/systemd/system"
mkdir -p `dirname "$unitFile"`
cp ./usr/share/systemd/tmp.mount "$unitFile" || cat > "$unitFile" << EOT
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Temporary Directory (/tmp)
Documentation=man:hier(7)
Documentation=https://www.freedesktop.org/wiki/Software/systemd/APIFileSystems
ConditionPathIsSymbolicLink=!/tmp
DefaultDependencies=no
Conflicts=umount.target
Before=local-fs.target umount.target
After=swap.target
[Mount]
What=tmpfs
Where=/tmp
Type=tmpfs
Options=mode=1777,strictatime,nosuid,nodev
EOT
fi
popd >> /dev/null
[ -n "${KERNEL_MODULES_DIR}" ] && copy_kernel_modules ${KERNEL_MODULES_DIR} ${ROOTFS_DIR}
info "Create ${ROOTFS_DIR}/etc"
mkdir -p "${ROOTFS_DIR}/etc"
case "${distro}" in
"ubuntu" | "debian")
echo "I am ubuntu or debian"
chrony_conf_file="${ROOTFS_DIR}/etc/chrony/chrony.conf"
chrony_systemd_service="${ROOTFS_DIR}/lib/systemd/system/chrony.service"
;;
*)
chrony_conf_file="${ROOTFS_DIR}/etc/chrony.conf"
chrony_systemd_service="${ROOTFS_DIR}/usr/lib/systemd/system/chronyd.service"
;;
esac
info "Configure chrony file ${chrony_conf_file}"
cat >> "${chrony_conf_file}" <<EOT
refclock PHC /dev/ptp0 poll 3 dpoll -2 offset 0
# Step the system clock instead of slewing it if the adjustment is larger than
# one second, at any time
makestep 1 -1
EOT
# Comment out ntp sources for chrony to be extra careful
# Reference: https://chrony.tuxfamily.org/doc/3.4/chrony.conf.html
sed -i 's/^\(server \|pool \|peer \)/# &/g' ${chrony_conf_file}
if [ -f "$chrony_systemd_service" ]; then
sed -i '/^\[Unit\]/a ConditionPathExists=\/dev\/ptp0' ${chrony_systemd_service}
fi
# The CC on s390x for fedora needs to be manually set to gcc when the golang is downloaded from the main page.
# See issue: https://github.com/kata-containers/osbuilder/issues/217
[ "$distro" == "fedora" ] && [ "$ARCH" == "s390x" ] && export CC=gcc
AGENT_DIR="${ROOTFS_DIR}/usr/bin"
AGENT_DEST="${AGENT_DIR}/${AGENT_BIN}"
if [ -z "${AGENT_SOURCE_BIN}" ] ; then
if [ "$RUST_AGENT" != "yes" ]; then
agent_pkg="${GO_AGENT_PKG}"
agent_dir="${GOPATH_LOCAL}/src/${GO_AGENT_PKG}"
else
# The PATH /.cargo/bin is apparently wrong
# looks like $HOME is resolved to empty when
# container is started
source "${HOME}/.cargo/env"
agent_pkg="${RUST_AGENT_PKG}"
agent_dir="${GOPATH_LOCAL}/src/${RUST_AGENT_PKG}/src/agent"
# For now, rust-agent doesn't support seccomp yet.
SECCOMP="no"
fi
info "Pull Agent source code"
go get -d "${agent_pkg}" || true
OK "Pull Agent source code"
info "Build agent"
pushd "${agent_dir}"
[ -n "${AGENT_VERSION}" ] && git checkout "${AGENT_VERSION}" && OK "git checkout successful" || info "checkout failed!"
make clean
make INIT=${AGENT_INIT}
make install DESTDIR="${ROOTFS_DIR}" INIT=${AGENT_INIT} SECCOMP=${SECCOMP}
popd
else
cp ${AGENT_SOURCE_BIN} ${AGENT_DEST}
OK "cp ${AGENT_SOURCE_BIN} ${AGENT_DEST}"
fi
[ -x "${AGENT_DEST}" ] || die "${AGENT_DEST} is not installed in ${ROOTFS_DIR}"
OK "Agent installed"
[ "${AGENT_INIT}" == "yes" ] && setup_agent_init "${AGENT_DEST}" "${init}"
info "Check init is installed"
[ -x "${init}" ] || [ -L "${init}" ] || die "/sbin/init is not installed in ${ROOTFS_DIR}"
OK "init is installed"
# Create an empty /etc/resolv.conf, to allow agent to bind mount container resolv.conf to Kata VM
dns_file="${ROOTFS_DIR}/etc/resolv.conf"
if [ -L "$dns_file" ]; then
# if /etc/resolv.conf is a link, it cannot be used for bind mount
rm -f "$dns_file"
fi
info "Create /etc/resolv.conf file in rootfs if not exist"
touch "$dns_file"
info "Creating summary file"
create_summary_file "${ROOTFS_DIR}"
}
parse_arguments()
{
while getopts a:hlo:r:t: opt
do
case $opt in
a) AGENT_VERSION="${OPTARG}" ;;
h) usage ;;
l) get_distros | sort && exit 0;;
o) OSBUILDER_VERSION="${OPTARG}" ;;
r) ROOTFS_DIR="${OPTARG}" ;;
t) get_test_config "${OPTARG}" && exit 0;;
*) die "Found an invalid option";;
esac
done
shift $(($OPTIND - 1))
distro="$1"
arch=$(uname -m)
if [ "${distro}" == "alpine" -o "${distro}" == "euleros" ]; then
if [ "${RUST_AGENT}" == "yes" ]; then
die "rust agent cannot be built on ${distro}.
alpine: only has stable/nightly-x86_64-unknown-linux-musl toolchain. It does not support proc-macro compilation.
See issue: https://github.com/kata-containers/osbuilder/issues/386
euleros: 1. Missing libstdc++.a
2. kernel is 3.10.x, there is no vsock support
You can build rust agent on your host and then copy it into
image's rootfs(eg. rootfs-builder/rootfs/usr/bin), and then
use image_builder.sh to build image with the rootfs. Please
refer to documentation for how to use customer agent.
See issue: https://github.com/kata-containers/osbuilder/issues/387"
fi
fi
if [ "${RUST_AGENT}" == "yes" ] && [ "${arch}" == "s390x" -o "${arch}" == "ppc64le" ]; then
die "Cannot build rust agent on ppc64le.
musl cannot be built on ppc64le because of long double
reprentation is broken. And rust has no musl target on ppc64le.
See issue: https://github.com/kata-containers/osbuilder/issues/388"
fi
}
detect_host_distro()
{
source /etc/os-release
case "$ID" in
"*suse*")
distro="suse"
;;
"clear-linux-os")
distro="clearlinux"
;;
*)
distro="$ID"
;;
esac
}
main()
{
parse_arguments $*
check_env_variables
init="${ROOTFS_DIR}/sbin/init"
if [ -n "$distro" ]; then
build_rootfs_distro
else
#Make sure ROOTFS_DIR is set correctly
[ -d "${ROOTFS_DIR}" ] || die "Invalid rootfs directory: '$ROOTFS_DIR'"
# Set the distro for dracut build method
detect_host_distro
prepare_overlay
fi
setup_rootfs
}
main $*

View File

@ -0,0 +1,20 @@
#
# Copyright (c) 2018 SUSE LLC
#
# SPDX-License-Identifier: Apache-2.0
#suse: docker image to be used to create a rootfs
#@OS_VERSION@: Docker image version to build this dockerfile
from docker.io/opensuse/leap
# This dockerfile needs to provide all the componets need to build a rootfs
# Install any package need to create a rootfs (package manager, extra tools)
COPY install-packages.sh config.sh /
# RUN commands
RUN chmod +x /install-packages.sh; /install-packages.sh
# This will install the proper golang to build Kata components
@INSTALL_MUSL@
@INSTALL_GO@
@INSTALL_RUST@

View File

@ -0,0 +1,58 @@
#
# Copyright (c) 2018 SUSE LLC
#
# SPDX-License-Identifier: Apache-2.0
# May also be "Tumbleweed"
OS_DISTRO="Leap"
# Leave this empty for distro "Tumbleweed"
OS_VERSION=${OS_VERSION:-15.0}
OS_IDENTIFIER="$OS_DISTRO${OS_VERSION:+:$OS_VERSION}"
# Extra packages to install in the rootfs
PACKAGES="systemd iptables libudev1"
# http or https
REPO_TRANSPORT="https"
# Can specify an alternative domain
REPO_DOMAIN="download.opensuse.org"
# Init process must be one of {systemd,kata-agent}
INIT_PROCESS=systemd
# List of zero or more architectures to exclude from build,
# as reported by `uname -m`
ARCH_EXCLUDE_LIST=()
###############################################################################
#
# NOTE: you probably dont need to edit things below this
#
SUSE_URL_BASE="${REPO_TRANSPORT}://${REPO_DOMAIN}"
SUSE_PATH_OSS="/distribution/${OS_DISTRO,,}/$OS_VERSION/repo/oss"
SUSE_PATH_UPDATE="/update/${OS_DISTRO,,}/$OS_VERSION/oss"
arch="$(uname -m)"
case "$arch" in
x86_64)
REPO_URL_PORT=""
;;
ppc|ppc64le)
REPO_URL_PORT="/ports/ppc"
;;
aarch64)
REPO_URL_PORT="/ports/aarch64"
;;
*)
die "Unsupported architecture: $arch"
;;
esac
SUSE_FULLURL_OSS="${SUSE_URL_BASE}${REPO_URL_PORT}${SUSE_PATH_OSS}"
SUSE_FULLURL_UPDATE="${SUSE_URL_BASE}${SUSE_PATH_UPDATE}"
if [ -z "${REPO_URL:-}" ]; then
REPO_URL="$SUSE_FULLURL_OSS"
fi

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2018 SUSE LLC
SPDX-License-Identifier: Apache-2.0
-->
<image schemaversion="6.8" name="openSUSE-rootfs-for-Kata-vm">
<description type="system">
<author>SUSE</author>
<contact>mvedovati@suse.com</contact>
<specification>openSUSE rootfs for Kata Containers guest vm</specification>
</description>
<preferences>
<version>1.0.0</version>
<packagemanager>zypper</packagemanager>
<locale>en_US</locale>
<keytable>us</keytable>
<rpm-excludedocs>true</rpm-excludedocs>
<type image="vmx" filesystem="ext4" />
</preferences>
<repository type="rpm-md">
<!-- NOTE: the following is a placeholder, specify the actual path with kiwi option set-repo=... -->
<source path="obs://"/>
</repository>
<packages type="image">
<package name=""/>
</packages>
<packages type="delete">
<package name="zypper"/>
<package name="rpm"/>
</packages>
<packages type="bootstrap" patternType="onlyRequired">
<package name="udev"/>
<package name="filesystem"/>
<package name="ca-certificates"/>
<package name="ca-certificates-mozilla"/>
<package name="openSUSE-release"/>
</packages>
</image>

View File

@ -0,0 +1,50 @@
#!/usr/bin/env bash
#
# Copyright (c) 2018 SUSE LLC
#
# SPDX-License-Identifier: Apache-2.0
set -euo pipefail
source config.sh
removeRepos=(repo-non-oss repo-update-non-oss repo-oss repo-update)
for r in ${removeRepos[@]}; do
zypper --non-interactive removerepo $r
done
zypper --non-interactive addrepo ${SUSE_FULLURL_OSS} osbuilder-oss
zypper --non-interactive addrepo ${SUSE_FULLURL_UPDATE} osbuilder-update
# Workaround for zypper slowdowns observed when running inside
# a container: see https://github.com/openSUSE/zypper/pull/209
# The fix is upstream but it will take a while before landing
# in Leap
ulimit -n 1024
zypper --non-interactive refresh
zypper --non-interactive install --no-recommends --force-resolution \
autoconf \
automake \
binutils \
cmake \
coreutils \
curl \
gcc \
gcc-c++ \
git \
glibc-devel \
glibc-devel-static \
glibc-utils \
libstdc++-devel \
linux-glibc-devel \
m4 \
make \
python3-kiwi \
sed \
tar \
vim \
which
zypper --non-interactive clean --all

View File

@ -0,0 +1,76 @@
#
# Copyright (c) 2018 SUSE LLC
#
# SPDX-License-Identifier: Apache-2.0
# - Arguments
# rootfs_dir=$1
#
# - Optional environment variables
#
# EXTRA_PKGS: Variable to add extra PKGS provided by the user
#
# BIN_AGENT: Name of the Kata-Agent binary
#
# REPO_URL: URL to distribution repository ( should be configured in
# config.sh file)
#
# Any other configuration variable for a specific distro must be added
# and documented on its own config.sh
#
# - Expected result
#
# rootfs_dir populated with rootfs pkgs
# It must provide a binary in /sbin/init
#
# Note: For some distros, the build_rootfs() function provided in scripts/lib.sh
# will suffice. If a new distro is introduced with a special requirement,
# then, a rootfs_builder/<distro>/rootfs_lib.sh file should be created
# using this template.
build_rootfs() {
# Mandatory
local ROOTFS_DIR=$1
#Name of the Kata-Agent binary
local BIN_AGENT=${BIN_AGENT}
# In case of support EXTRA packages, use it to allow
# users add more packages to the base rootfs
local EXTRA_PKGS=${EXTRA_PKGS:-}
#PATH where files this script is placed
#Use it to refer to files in the same directory
#Exmaple: ${CONFIG_DIR}/foo
local CONFIG_DIR=${CONFIG_DIR}
# Populate ROOTFS_DIR
# Must provide /sbin/init and /bin/${BIN_AGENT}
if [ -e "$ROOTFS_DIR" ] && ! [ -z "$(ls -A $ROOTFS_DIR)" ]; then
echo "ERROR: $ROOTFS_DIR is not empty"
exit 1
fi
local addPackages=""
for p in $PACKAGES $EXTRA_PKGS; do
addPackages+=" --add-package=$p"
done
# set-repo format: <source,type,alias,priority,imageinclude,package_gpgcheck>
# man kiwi::system::build for details
local setRepo=" --set-repo $REPO_URL,rpm-md,$OS_IDENTIFIER,99,false,false"
# Workaround for zypper slowdowns observed when running inside
# a container: see https://github.com/openSUSE/zypper/pull/209
# The fix is upstream but it will take a while before landing
# in Leap
ulimit -n 1024
kiwi system prepare \
--description $CONFIG_DIR \
--allow-existing-root \
--root $ROOTFS_DIR \
$addPackages \
$setRepo
install -d $ROOTFS_DIR/lib/systemd
ln -s /usr/lib/systemd/systemd $ROOTFS_DIR/lib/systemd/systemd
}

View File

@ -0,0 +1,16 @@
#
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#@distro@: docker image to be used to create a rootfs
#@OS_VERSION@: Docker image version to build this dockerfile
from @distro@:@OS_VERSION@
# This dockerfile needs to provide all the componets need to build a rootfs
# Install any package need to create a rootfs (package manager, extra tools)
# RUN commands
# This will install the proper golang to build Kata components
@INSTALL_GO@

View File

@ -0,0 +1,20 @@
# Copyright (c) 2017 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
#
MK_DIR :=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
## Default destdir is one level up where is rootfs.sh script
DESTDIR ?= "$(realpath $(MK_DIR)/../)/$(ROOTFS_BASE_NAME)"
all:
ifndef ROOTFS_BASE_NAME
$(error ROOTFS_BASE_NAME is not set, use $ make ROOTFS_BASE_NAME=new_supported_os)
endif
mkdir -p $(DESTDIR)
cp "$(MK_DIR)/rootfs_lib_template.sh" "$(DESTDIR)/rootfs_lib.sh"
cp "$(MK_DIR)/config_template.sh" "$(DESTDIR)/config.sh"
sed \
-e "s|@distro@|$(ROOTFS_BASE_NAME)|g" \
"$(MK_DIR)/Dockerfile.template" > $(DESTDIR)/Dockerfile.in

View File

@ -0,0 +1,22 @@
#
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
# This is a configuration file add extra variables to
# be used by build_rootfs() from rootfs_lib.sh the variables will be
# loaded just before call the function. For more information see the
# rootfs-builder/README.md file.
OS_VERSION=${OS_VERSION:-DEFAULT_VERSION}
PACKAGES="systemd iptables udevlib.so"
# Init process must be one of {systemd,kata-agent}
INIT_PROCESS=systemd
# List of zero or more architectures to exclude from build,
# as reported by `uname -m`
ARCH_EXCLUDE_LIST=()
# [When uncommented,] Allow the build to fail without generating an error
# For more info see: https://github.com/kata-containers/osbuilder/issues/190
#BUILD_CAN_FAIL=1

View File

@ -0,0 +1,49 @@
# - Arguments
# rootfs_dir=$1
#
# - Optional environment variables
#
# EXTRA_PKGS: Variable to add extra PKGS provided by the user
#
# BIN_AGENT: Name of the Kata-Agent binary
#
# REPO_URL: URL to distribution repository ( should be configured in
# config.sh file)
#
# Any other configuration variable for a specific distro must be added
# and documented on its own config.sh
#
# - Expected result
#
# rootfs_dir populated with rootfs pkgs
# It must provide a binary in /sbin/init
#
# Note: For some distros, the build_rootfs() function provided in scripts/lib.sh
# will suffice. If a new distro is introduced with a special requirement,
# then, a rootfs_builder/<distro>/rootfs_lib.sh file should be created
# using this template.
build_rootfs() {
# Mandatory
local ROOTFS_DIR=$1
#Name of the Kata-Agent binary
local BIN_AGENT=${BIN_AGENT}
# In case of support EXTRA packages, use it to allow
# users add more packages to the base rootfs
local EXTRA_PKGS=${EXTRA_PKGS:-}
#In case rootfs is created usign repositories allow user to modify
# the default URL
local REPO_URL=${REPO_URL:-YOUR_REPO}
#PATH where files this script is placed
#Use it to refer to files in the same directory
#Exmaple: ${CONFIG_DIR}/foo
local CONFIG_DIR=${CONFIG_DIR}
# Populate ROOTFS_DIR
# Must provide /sbin/init and /bin/${BIN_AGENT}
}

View File

@ -0,0 +1,39 @@
#
# Copyright (c) 2020 ARM Limited
#
# SPDX-License-Identifier: Apache-2.0
#ubuntu: docker image to be used to create a rootfs
#@OS_VERSION@: Docker image version to build this dockerfile
from docker.io/ubuntu:@OS_VERSION@
# This dockerfile needs to provide all the componets need to build a rootfs
# Install any package need to create a rootfs (package manager, extra tools)
# RUN commands
RUN apt-get update && apt-get install -y \
autoconf \
automake \
binutils \
build-essential \
chrony \
cmake \
coreutils \
curl \
debianutils \
debootstrap \
g++ \
gcc \
git \
libc6-dev \
libstdc++-8-dev \
m4 \
make \
sed \
systemd \
tar \
vim
# This will install the proper golang to build Kata components
@INSTALL_GO@
@INSTALL_MUSL@
@INSTALL_RUST@

View File

@ -0,0 +1,44 @@
#
# Copyright (c) 2018 Yash Jain
#
# SPDX-License-Identifier: Apache-2.0
#ubuntu: docker image to be used to create a rootfs
#@OS_VERSION@: Docker image version to build this dockerfile
from docker.io/ubuntu:@OS_VERSION@
# This dockerfile needs to provide all the componets need to build a rootfs
# Install any package need to create a rootfs (package manager, extra tools)
# RUN commands
RUN apt-get update && apt-get --no-install-recommends install -y \
apt-utils \
autoconf \
automake \
binutils \
build-essential \
ca-certificates \
chrony \
cmake \
coreutils \
curl \
debianutils \
debootstrap \
g++ \
gcc \
git \
libc6-dev \
libstdc++-8-dev \
m4 \
make \
musl \
musl-dev \
musl-tools \
sed \
systemd \
tar \
vim \
wget
# This will install the proper golang to build Kata components
@INSTALL_GO@
@INSTALL_RUST@

View File

@ -0,0 +1,33 @@
# This is a configuration file add extra variables to
#
# Copyright (c) 2018 Yash Jain
#
# SPDX-License-Identifier: Apache-2.0
# be used by build_rootfs() from rootfs_lib.sh the variables will be
# loaded just before call the function. For more information see the
# rootfs-builder/README.md file.
OS_VERSION=${OS_VERSION:-18.04}
# this should be ubuntu's codename eg bionic for 18.04
OS_NAME=${OS_NAME:-"bionic"}
# packages to be installed by default
PACKAGES="systemd iptables init chrony kmod"
DEBOOTSTRAP=${PACKAGE_MANAGER:-"debootstrap"}
case $(uname -m) in
x86_64) ARCHITECTURE="amd64";;
ppc64le) ARCHITECTURE="ppc64el";;
aarch64) ARCHITECTURE="arm64";;
s390x) ARCHITECTURE="s390x";;
(*) die "$(uname -m) not supported "
esac
# Init process must be one of {systemd,kata-agent}
INIT_PROCESS=systemd
# List of zero or more architectures to exclude from build,
# as reported by `uname -m`
ARCH_EXCLUDE_LIST=()
[ "$SECCOMP" = "yes" ] && PACKAGES+=" libseccomp2" || true

View File

@ -0,0 +1,84 @@
# - Arguments
#
# Copyright (c) 2018 Yash Jain
#
# SPDX-License-Identifier: Apache-2.0
#
#
# rootfs_dir=$1
#
# - Optional environment variables
#
# EXTRA_PKGS: Variable to add extra PKGS provided by the user
#
# BIN_AGENT: Name of the Kata-Agent binary
#
# REPO_URL: URL to distribution repository ( should be configured in
# config.sh file)
#
# Any other configuration variable for a specific distro must be added
# and documented on its own config.sh
#
# - Expected result
#
# rootfs_dir populated with rootfs pkgs
# It must provide a binary in /sbin/init
#
build_rootfs() {
# Mandatory
local ROOTFS_DIR=$1
# Name of the Kata-Agent binary
local BIN_AGENT=${BIN_AGENT}
# In case of support EXTRA packages, use it to allow
# users to add more packages to the base rootfs
local EXTRA_PKGS=${EXTRA_PKGS:-}
# In case rootfs is created using repositories allow user to modify
# the default URL
local REPO_URL=${REPO_URL:-YOUR_REPO}
# PATH where files this script is placed
# Use it to refer to files in the same directory
# Example: ${CONFIG_DIR}/foo
local CONFIG_DIR=${CONFIG_DIR}
# Populate ROOTFS_DIR
# Must provide /sbin/init and /bin/${BIN_AGENT}
DEBOOTSTRAP="debootstrap"
check_root
mkdir -p "${ROOTFS_DIR}"
if [ -n "${PKG_MANAGER}" ]; then
info "debootstrap path provided by user: ${PKG_MANAGER}"
elif check_program $DEBOOTSTRAP ; then
PKG_MANAGER=$DEBOOTSTRAP
else
die "$DEBOOTSTRAP is not installed"
fi
# trim whitespace
PACKAGES=$(echo $PACKAGES |xargs )
EXTRA_PKGS=$(echo $EXTRA_PKGS |xargs)
# add comma as debootstrap needs , separated package names.
# Don't change $PACKAGES in config.sh to include ','
# This is done to maintain consistency
PACKAGES=$(echo $PACKAGES | sed -e 's/ /,/g' )
EXTRA_PKGS=$(echo $EXTRA_PKGS | sed -e 's/ /,/g' )
# extra packages are added to packages and finally passed to debootstrap
if [ "${EXTRA_PKGS}" = "" ]; then
echo "no extra packages"
else
PACKAGES="${PACKAGES},${EXTRA_PKGS}"
fi
${PKG_MANAGER} --variant=minbase \
--arch=${ARCHITECTURE}\
--include="$PACKAGES" \
${OS_NAME} \
${ROOTFS_DIR}
chroot $ROOTFS_DIR ln -s /lib/systemd/systemd /usr/lib/systemd/systemd
}

View File

@ -0,0 +1,72 @@
#!/usr/bin/env bash
#
# Copyright (c) 2019 IBM
#
# SPDX-License-Identifier: Apache-2.0
#
# If we fail for any reason a message will be displayed
die() {
msg="$*"
echo "ERROR: $msg" >&2
exit 1
}
# Install the yq yaml query package from the mikefarah github repo
# Install via binary download, as we may not have golang installed at this point
function install_yq() {
GOPATH=${GOPATH:-${HOME}/go}
local yq_path="${GOPATH}/bin/yq"
local yq_pkg="github.com/mikefarah/yq"
[ -x "${GOPATH}/bin/yq" ] && return
read -r -a sysInfo <<< "$(uname -sm)"
case "${sysInfo[0]}" in
"Linux" | "Darwin")
goos="${sysInfo[0],}"
;;
"*")
die "OS ${sysInfo[0]} not supported"
;;
esac
case "${sysInfo[1]}" in
"aarch64")
goarch=arm64
;;
"ppc64le")
goarch=ppc64le
;;
"x86_64")
goarch=amd64
;;
"s390x")
goarch=s390x
;;
"*")
die "Arch ${sysInfo[1]} not supported"
;;
esac
mkdir -p "${GOPATH}/bin"
# Check curl
if ! command -v "curl" >/dev/null; then
die "Please install curl"
fi
local yq_version=2.3.0
local yq_url="https://${yq_pkg}/releases/download/${yq_version}/yq_${goos}_${goarch}"
curl -o "${yq_path}" -LSsf ${yq_url}
[ $? -ne 0 ] && die "Download ${yq_url} failed"
chmod +x ${yq_path}
if ! command -v "${yq_path}" >/dev/null; then
die "Cannot not get ${yq_path} executable"
fi
}
install_yq

View File

@ -0,0 +1,566 @@
#!/bin/bash
#
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
set -e
GO_AGENT_PKG=${GO_AGENT_PKG:-github.com/kata-containers/agent}
GO_RUNTIME_PKG=${GO_RUNTIME_PKG:-github.com/kata-containers/runtime}
RUST_AGENT_PKG=${RUST_AGENT_PKG:-github.com/kata-containers/kata-rust-agent}
CMAKE_VERSION=${CMAKE_VERSION:-"null"}
MUSL_VERSION=${MUSL_VERSION:-"null"}
#https://github.com/kata-containers/tests/blob/master/.ci/jenkins_job_build.sh
# Give preference to variable set by CI
KATA_BRANCH=${branch:-}
KATA_BRANCH=${KATA_BRANCH:-master}
yq_file="${script_dir}/../scripts/install-yq.sh"
error()
{
local msg="$*"
echo "ERROR: ${msg}" >&2
}
die()
{
error "$*"
exit 1
}
OK()
{
local msg="$*"
echo "[OK] ${msg}" >&2
}
info()
{
local msg="$*"
echo "INFO: ${msg}"
}
warning()
{
local msg="$*"
echo "WARNING: ${msg}"
}
check_program()
{
type "$1" >/dev/null 2>&1
}
check_root()
{
if [ "$(id -u)" != "0" ]; then
echo "Root is needed"
exit 1
fi
}
generate_dnf_config()
{
REPO_NAME=${REPO_NAME:-"base"}
CACHE_DIR=${CACHE_DIR:-"/var/cache/dnf"}
cat > "${DNF_CONF}" << EOF
[main]
cachedir=${CACHE_DIR}
logfile=${LOG_FILE}
keepcache=0
debuglevel=2
exactarch=1
obsoletes=1
plugins=0
installonly_limit=3
reposdir=/root/mash
retries=5
EOF
if [ "$BASE_URL" != "" ]; then
cat >> "${DNF_CONF}" << EOF
[base]
name=${OS_NAME}-${OS_VERSION} ${REPO_NAME}
failovermethod=priority
baseurl=${BASE_URL}
enabled=1
EOF
elif [ "$MIRROR_LIST" != "" ]; then
cat >> "${DNF_CONF}" << EOF
[base]
name=${OS_NAME}-${OS_VERSION} ${REPO_NAME}
mirrorlist=${MIRROR_LIST}
enabled=1
EOF
fi
if [ -n "$GPG_KEY_URL" ]; then
if [ ! -f "${CONFIG_DIR}/${GPG_KEY_FILE}" ]; then
curl -L ${GPG_KEY_URL} -o ${CONFIG_DIR}/${GPG_KEY_FILE}
fi
cat >> "${DNF_CONF}" << EOF
gpgcheck=1
gpgkey=file://${CONFIG_DIR}/${GPG_KEY_FILE}
EOF
fi
if [ -n "$GPG_KEY_ARCH_URL" ]; then
if [ ! -f "${CONFIG_DIR}/${GPG_KEY_ARCH_FILE}" ]; then
curl -L ${GPG_KEY_ARCH_URL} -o ${CONFIG_DIR}/${GPG_KEY_ARCH_FILE}
fi
cat >> "${DNF_CONF}" << EOF
file://${CONFIG_DIR}/${GPG_KEY_ARCH_FILE}
EOF
fi
}
build_rootfs()
{
# Mandatory
local ROOTFS_DIR="$1"
[ -z "$ROOTFS_DIR" ] && die "need rootfs"
# In case of support EXTRA packages, use it to allow
# users add more packages to the base rootfs
local EXTRA_PKGS=${EXTRA_PKGS:-""}
#PATH where files this script is placed
#Use it to refer to files in the same directory
#Exmaple: ${CONFIG_DIR}/foo
#local CONFIG_DIR=${CONFIG_DIR}
check_root
if [ ! -f "${DNF_CONF}" ] && [ -z "${DISTRO_REPO}" ] ; then
DNF_CONF="./kata-${OS_NAME}-dnf.conf"
generate_dnf_config
fi
mkdir -p "${ROOTFS_DIR}"
if [ -n "${PKG_MANAGER}" ]; then
info "DNF path provided by user: ${PKG_MANAGER}"
elif check_program "dnf"; then
PKG_MANAGER="dnf"
elif check_program "yum" ; then
PKG_MANAGER="yum"
else
die "neither yum nor dnf is installed"
fi
DNF="${PKG_MANAGER} -y --installroot=${ROOTFS_DIR} --noplugins"
if [ -n "${DNF_CONF}" ] ; then
DNF="${DNF} --config=${DNF_CONF}"
else
DNF="${DNF} --releasever=${OS_VERSION}"
fi
$DNF install ${EXTRA_PKGS} ${PACKAGES}
}
# Create a YAML metadata file inside the rootfs.
#
# This provides useful information about the rootfs than can be interrogated
# once the rootfs has been converted into a image/initrd.
create_summary_file()
{
local -r rootfs_dir="$1"
[ -z "$rootfs_dir" ] && die "need rootfs"
local -r file_dir="/var/lib/osbuilder"
local -r dir="${rootfs_dir}${file_dir}"
local -r filename="osbuilder.yaml"
local file="${dir}/${filename}"
local -r now=$(date -u -d@${SOURCE_DATE_EPOCH:-$(date +%s.%N)} '+%Y-%m-%dT%T.%N%zZ')
# sanitise package lists
PACKAGES=$(echo "$PACKAGES"|tr ' ' '\n'|sort -u|tr '\n' ' ')
EXTRA_PKGS=$(echo "$EXTRA_PKGS"|tr ' ' '\n'|sort -u|tr '\n' ' ')
local -r packages=$(for pkg in ${PACKAGES}; do echo " - \"${pkg}\""; done)
local -r extra=$(for pkg in ${EXTRA_PKGS}; do echo " - \"${pkg}\""; done)
mkdir -p "$dir"
# Semantic version of the summary file format.
#
# XXX: Increment every time the format of the summary file changes!
local -r format_version="0.0.2"
local -r osbuilder_url="https://github.com/kata-containers/osbuilder"
local agent="${AGENT_DEST}"
[ "$AGENT_INIT" = yes ] && agent="${init}"
local agent_version
if [ "${RUST_AGENT}" == "no" ]; then
agent_version=$("$agent" --version|awk '{print $NF}')
else
local -r agentdir="${GOPATH}/src/${RUST_AGENT_PKG}/src/agent"
agent_version=$(cat ${agentdir}/VERSION)
fi
local REAL_AGENT_PKG
if [ "$RUST_AGENT" == "no" ]; then
REAL_AGENT_PKG=${GO_AGENT_PKG}
else
REAL_AGENT_PKG=${RUST_AGENT_PKG}
fi
cat >"$file"<<-EOT
---
osbuilder:
url: "${osbuilder_url}"
version: "${OSBUILDER_VERSION}"
rootfs-creation-time: "${now}"
description: "osbuilder rootfs"
file-format-version: "${format_version}"
architecture: "${ARCH}"
base-distro:
name: "${OS_NAME}"
version: "${OS_VERSION}"
packages:
default:
${packages}
extra:
${extra}
agent:
url: "https://${REAL_AGENT_PKG}"
name: "${AGENT_BIN}"
version: "${agent_version}"
agent-is-init-daemon: "${AGENT_INIT}"
EOT
local rootfs_file="${file_dir}/$(basename "${file}")"
info "Created summary file '${rootfs_file}' inside rootfs"
}
# generate_dockerfile takes as only argument a path. It expects a Dockerfile.in
# Dockerfile template to be present in that path, and will generate a usable
# Dockerfile replacing the '@PLACEHOLDER@' in that Dockerfile
generate_dockerfile()
{
dir="$1"
[ -d "${dir}" ] || die "${dir}: not a directory"
local architecture=$(uname -m)
local rustarch=${architecture}
local muslarch=${architecture}
case "$(uname -m)" in
"ppc64le")
goarch=ppc64le
rustarch=powerpc64le
muslarch=powerpc64
;;
"aarch64")
goarch=arm64
;;
"s390x")
goarch=s390x
;;
*)
goarch=amd64
;;
esac
[ -n "${http_proxy:-}" ] && readonly set_proxy="RUN sed -i '$ a proxy="${http_proxy:-}"' /etc/dnf/dnf.conf /etc/yum.conf; true"
curlOptions=("-OL")
[ -n "${http_proxy:-}" ] && curlOptions+=("-x ${http_proxy:-}")
readonly install_go="
RUN cd /tmp ; curl ${curlOptions[@]} https://storage.googleapis.com/golang/go${GO_VERSION}.linux-${goarch}.tar.gz
RUN tar -C /usr/ -xzf /tmp/go${GO_VERSION}.linux-${goarch}.tar.gz
ENV GOROOT=/usr/go
ENV PATH=\$PATH:\$GOROOT/bin:\$GOPATH/bin
"
# Rust agent
# rust installer should set path apropiately, just in case
local cmake_file="cmake-${CMAKE_VERSION}.tar.gz"
local cmake_dir="cmake-${CMAKE_VERSION}"
readonly install_cmake="
RUN pushd /root; \
curl -sLO https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${cmake_file}; \
tar -zxf ${cmake_file}; \
cd ${cmake_dir}; \
./bootstrap > /dev/null 2>\&1; \
make > /dev/null 2>\&1; \
make install > /dev/null 2>\&1; \
popd
"
# install musl for compiling rust-agent
install_musl=
if [ "${muslarch}" == "aarch64" ]; then
local musl_tar="${muslarch}-linux-musl-native.tgz"
local musl_dir="${muslarch}-linux-musl-native"
install_musl="
RUN cd /tmp; \
curl -sLO https://musl.cc/${musl_tar}; tar -zxf ${musl_tar}; \
mkdir -p /usr/local/musl/; \
cp -r ${musl_dir}/* /usr/local/musl/
ENV PATH=\$PATH:/usr/local/musl/bin
RUN ln -sf /usr/local/musl/bin/g++ /usr/bin/g++
"
else
local musl_tar="musl-${MUSL_VERSION}.tar.gz"
local musl_dir="musl-${MUSL_VERSION}"
install_musl="
RUN pushd /root; \
curl -sLO https://www.musl-libc.org/releases/${musl_tar}; tar -zxf ${musl_tar}; \
cd ${musl_dir}; \
sed -i \"s/^ARCH = .*/ARCH = ${muslarch}/g\" dist/config.mak; \
./configure > /dev/null 2>\&1; \
make > /dev/null 2>\&1; \
make install > /dev/null 2>\&1; \
echo \"/usr/local/musl/lib\" > /etc/ld-musl-${muslarch}.path; \
popd
ENV PATH=\$PATH:/usr/local/musl/bin
"
fi
readonly install_rust="
RUN curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSLf --output /tmp/rust-init; \
chmod a+x /tmp/rust-init; \
export http_proxy=${http_proxy:-}; \
export https_proxy=${http_proxy:-}; \
/tmp/rust-init -y
RUN . /root/.cargo/env; \
export http_proxy=${http_proxy:-}; \
export https_proxy=${http_proxy:-}; \
cargo install cargo-when; \
rustup toolchain install ${RUST_VERSION}; \
rustup default ${RUST_VERSION}; \
rustup target install ${rustarch}-unknown-linux-musl
RUN ln -sf /usr/bin/g++ /bin/musl-g++
"
# rust agent still need go to build
# because grpc-sys need go to build
pushd ${dir}
dockerfile_template="Dockerfile.in"
dockerfile_arch_template="Dockerfile-${architecture}.in"
# if arch-specific docker file exists, swap the univesal one with it.
if [ -f "${dockerfile_arch_template}" ]; then
dockerfile_template="${dockerfile_arch_template}"
else
[ -f "${dockerfile_template}" ] || die "${dockerfile_template}: file not found"
fi
# powerpc have no musl target, don't setup rust enviroment
# since we cannot static link agent. Besides, there is
# also long double representation problem when building musl-libc
if [ "${architecture}" == "ppc64le" ] || [ "${architecture}" == "s390x" ]; then
sed \
-e "s|@GO_VERSION@|${GO_VERSION}|g" \
-e "s|@OS_VERSION@|${OS_VERSION:-}|g" \
-e "s|@INSTALL_CMAKE@||g" \
-e "s|@INSTALL_MUSL@||g" \
-e "s|@INSTALL_GO@|${install_go//$'\n'/\\n}|g" \
-e "s|@INSTALL_RUST@||g" \
-e "s|@SET_PROXY@|${set_proxy:-}|g" \
${dockerfile_template} > Dockerfile
else
sed \
-e "s|@GO_VERSION@|${GO_VERSION}|g" \
-e "s|@OS_VERSION@|${OS_VERSION:-}|g" \
-e "s|@INSTALL_CMAKE@|${install_cmake//$'\n'/\\n}|g" \
-e "s|@INSTALL_MUSL@|${install_musl//$'\n'/\\n}|g" \
-e "s|@INSTALL_GO@|${install_go//$'\n'/\\n}|g" \
-e "s|@INSTALL_RUST@|${install_rust//$'\n'/\\n}|g" \
-e "s|@SET_PROXY@|${set_proxy:-}|g" \
${dockerfile_template} > Dockerfile
fi
popd
}
detect_go_version()
{
info "Detecting agent go version"
typeset yq=$(command -v yq || command -v ${GOPATH}/bin/yq || echo "${GOPATH}/bin/yq")
if [ ! -f "$yq" ]; then
source "$yq_file"
fi
local runtimeRevision=""
# Detect runtime revision by fetching the agent's VERSION file
local runtime_version_url="https://raw.githubusercontent.com/kata-containers/agent/${AGENT_VERSION:-master}/VERSION"
info "Detecting runtime version using ${runtime_version_url}"
if runtimeRevision="$(curl -fsSL ${runtime_version_url})"; then
[ -n "${runtimeRevision}" ] || die "failed to get agent version"
typeset -r runtimeVersionsURL="https://raw.githubusercontent.com/kata-containers/runtime/${runtimeRevision}/versions.yaml"
info "Getting golang version from ${runtimeVersionsURL}"
# This may fail if we are a kata bump.
if GO_VERSION="$(curl -fsSL "$runtimeVersionsURL" | $yq r - "languages.golang.version")"; then
[ "$GO_VERSION" != "null" ]
return 0
fi
fi
info "Agent version has not match with a runtime version, assumming it is a PR"
local kata_runtime_pkg_dir="${GOPATH}/src/${GO_RUNTIME_PKG}"
if [ ! -d "${kata_runtime_pkg_dir}" ];then
info "There is not runtime repository in filesystem (${kata_runtime_pkg_dir})"
local runtime_versions_url="https://raw.githubusercontent.com/kata-containers/runtime/${KATA_BRANCH}/versions.yaml"
info "Get versions file from ${runtime_versions_url}"
GO_VERSION="$(curl -fsSL "${runtime_versions_url}" | $yq r - "languages.golang.version")"
if [ "$?" == "0" ] && [ "$GO_VERSION" != "null" ]; then
return 0
fi
return 1
fi
local kata_versions_file="${kata_runtime_pkg_dir}/versions.yaml"
info "Get Go version from ${kata_versions_file}"
GO_VERSION="$(cat "${kata_versions_file}" | $yq r - "languages.golang.version")"
[ "$?" == "0" ] && [ "$GO_VERSION" != "null" ]
}
detect_rust_version()
{
info "Detecting agent rust version"
typeset -r yq=$(command -v yq || command -v ${GOPATH}/bin/yq || echo "${GOPATH}/bin/yq")
if [ ! -f "$yq" ]; then
source "$yq_file"
fi
local runtimeRevision=""
# Detect runtime revision by fetching the agent's VERSION file
local runtime_version_url="https://raw.githubusercontent.com/kata-containers/agent/${AGENT_VERSION:-master}/VERSION"
info "Detecting runtime version using ${runtime_version_url}"
if runtimeRevision="$(curl -fsSL ${runtime_version_url})"; then
[ -n "${runtimeRevision}" ] || die "failed to get agent version"
typeset -r runtimeVersionsURL="https://raw.githubusercontent.com/kata-containers/runtime/${runtimeRevision}/versions.yaml"
info "Getting rust version from ${runtimeVersionsURL}"
# This may fail if we are a kata bump.
if RUST_VERSION="$(curl -fsSL "$runtimeVersionsURL" | $yq r - "languages.rust.version")"; then
[ "$RUST_VERSION" != "null" ]
return 0
fi
fi
info "Agent version has not match with a runtime version, assumming it is a PR"
local kata_runtime_pkg_dir="${GOPATH}/src/${GO_RUNTIME_PKG}"
if [ ! -d "${kata_runtime_pkg_dir}" ];then
info "There is not runtime repository in filesystem (${kata_runtime_pkg_dir})"
local runtime_versions_url="https://raw.githubusercontent.com/kata-containers/runtime/${KATA_BRANCH}/versions.yaml"
info "Get versions file from ${runtime_versions_url}"
RUST_VERSION="$(curl -fsSL "${runtime_versions_url}" | $yq r - "languages.rust.version")"
if [ "$?" == "0" ] && [ "$RUST_VERSION" != "null" ]; then
return 0
fi
return 1
fi
local kata_versions_file="${kata_runtime_pkg_dir}/versions.yaml"
info "Get rust version from ${kata_versions_file}"
RUST_VERSION="$(cat "${kata_versions_file}" | $yq r - "languages.rust.version")"
[ "$?" == "0" ] && [ "$RUST_VERSION" != "null" ]
}
detect_cmake_version()
{
info "Detecting cmake version"
typeset -r yq=$(command -v yq || command -v ${GOPATH}/bin/yq || echo "${GOPATH}/bin/yq")
if [ ! -f "$yq" ]; then
source "$yq_file"
fi
local runtimeRevision=""
# Detect runtime revision by fetching the agent's VERSION file
local runtime_version_url="https://raw.githubusercontent.com/kata-containers/agent/${AGENT_VERSION:-master}/VERSION"
info "Detecting runtime version using ${runtime_version_url}"
if runtimeRevision="$(curl -fsSL ${runtime_version_url})"; then
[ -n "${runtimeRevision}" ] || die "failed to get agent version"
typeset -r runtimeVersionsURL="https://raw.githubusercontent.com/kata-containers/runtime/${runtimeRevision}/versions.yaml"
info "Getting cmake version from ${runtimeVersionsURL}"
# This may fail if we are a kata bump.
if CMAKE_VERSION="$(curl -fsSL "$runtimeVersionsURL" | $yq r - "externals.cmake.version")"; then
[ "$CMAKE_VERSION" != "null" ]
return 0
fi
fi
info "Agent version has not match with a runtime version, assumming it is a PR"
local kata_runtime_pkg_dir="${GOPATH}/src/${GO_RUNTIME_PKG}"
if [ ! -d "${kata_runtime_pkg_dir}" ];then
info "There is not runtime repository in filesystem (${kata_runtime_pkg_dir})"
local runtime_versions_url="https://raw.githubusercontent.com/kata-containers/runtime/${KATA_BRANCH}/versions.yaml"
info "Get versions file from ${runtime_versions_url}"
CMAKE_VERSION="$(curl -fsSL "${runtime_versions_url}" | $yq r - "externals.cmake.version")"
if [ "$?" == "0" ] && [ "$CMAKE_VERSION" != "null" ]; then
return 0
fi
return 1
fi
local kata_versions_file="${kata_runtime_pkg_dir}/versions.yaml"
info "Get cmake version from ${kata_versions_file}"
CMAKE_VERSION="$(cat "${kata_versions_file}" | $yq r - "externals.cmake.version")"
[ "$?" == "0" ] && [ "$CMAKE_VERSION" != "null" ]
}
detect_musl_version()
{
info "Detecting musl version"
typeset -r yq=$(command -v yq || command -v ${GOPATH}/bin/yq || echo "${GOPATH}/bin/yq")
if [ ! -f "$yq" ]; then
source "$yq_file"
fi
local runtimeRevision=""
# Detect runtime revision by fetching the agent's VERSION file
local runtime_version_url="https://raw.githubusercontent.com/kata-containers/agent/${AGENT_VERSION:-master}/VERSION"
info "Detecting runtime version using ${runtime_version_url}"
if runtimeRevision="$(curl -fsSL ${runtime_version_url})"; then
[ -n "${runtimeRevision}" ] || die "failed to get agent version"
typeset -r runtimeVersionsURL="https://raw.githubusercontent.com/kata-containers/runtime/${runtimeRevision}/versions.yaml"
info "Getting musl version from ${runtimeVersionsURL}"
# This may fail if we are a kata bump.
if MUSL_VERSION="$(curl -fsSL "$runtimeVersionsURL" | $yq r - "externals.musl.version")"; then
[ "$MUSL_VERSION" != "null" ]
return 0
fi
fi
info "Agent version has not match with a runtime version, assumming it is a PR"
local kata_runtime_pkg_dir="${GOPATH}/src/${GO_RUNTIME_PKG}"
if [ ! -d "${kata_runtime_pkg_dir}" ];then
info "There is not runtime repository in filesystem (${kata_runtime_pkg_dir})"
local runtime_versions_url="https://raw.githubusercontent.com/kata-containers/runtime/${KATA_BRANCH}/versions.yaml"
info "Get versions file from ${runtime_versions_url}"
MUSL_VERSION="$(curl -fsSL "${runtime_versions_url}" | $yq r - "externals.musl.version")"
if [ "$?" == "0" ] && [ "$MUSL_VERSION" != "null" ]; then
return 0
fi
return 1
fi
local kata_versions_file="${kata_runtime_pkg_dir}/versions.yaml"
info "Get musl version from ${kata_versions_file}"
MUSL_VERSION="$(cat "${kata_versions_file}" | $yq r - "externals.musl.version")"
[ "$?" == "0" ] && [ "$MUSL_VERSION" != "null" ]
}

View File

@ -0,0 +1,32 @@
* [Run the osbuilder tests](#run-the-osbuilder-tests)
* [Further information](#further-information)
## Run the osbuilder tests
osbuilder provides a test script that creates all rootfs disk images and
initrd images for all supported distributions and then tests them to ensure a
Kata Container can be created with each.
Before the build phase, the test script installs the Docker container manager
and all the Kata components required to run test containers. Individual tests
will also alter host `kata-runtime` and `docker` service configuration as needed.
All host config editing can be skipped by setting the environment variable
`KATA_DEV_MODE` to a non-empty value. In this mode, image/initrd targets
will be built but not runtime tested; If your host is configured to have
`kata-runtime` set as the default docker runtime, you will need to switch
to a runtime like `runc`/`crun` so the `docker build` test commands work
correctly.
```
$ ./test_images.sh
```
## Further information
The test script provides various options to modify the way it runs. For full
details:
```
$ ./test_images.sh -h
```

View File

@ -0,0 +1,68 @@
#
# Copyright (c) 2018 SUSE LLC
#
# SPDX-License-Identifier: Apache-2.0
# List of distros not to test, when running all tests with test_images.sh
typeset -a skipWhenTestingAll
typeset -a distros
arch="$(uname -m)"
sdir="${BASH_SOURCE[0]%/*}"
for distro in $(${sdir}/../rootfs-builder/rootfs.sh -l); do
distros+=("${distro}")
done
test_distros=()
test_distros+=("clearlinux")
test_distros+=("ubuntu")
skipForRustDistros=()
skipForRustDistros+=("alpine")
skipForRustDistros+=("euleros")
skipForRustArch=()
skipForRustArch+=("ppc64le")
skipForRustArch+=("s390x")
distro_in_set() {
local d=$1
shift
local dt
for dt in "$@"; do
if [ "${dt}" == "${d}" ]; then
return 0
fi
done
return 1
}
if [ -n "${CI:-}" ]; then
# CI tests may timeout with euleros, see:
# https://github.com/kata-containers/osbuilder/issues/46"
# Since too many distros timeout for now, we only test clearlinux and ubuntu. We can enable other distros when we fix timeout problem.
for distro in "${distros[@]}"; do
if distro_in_set "${distro}" "${test_distros[@]}"; then
continue
fi
skipWhenTestingAll+=("${distro}")
done
if [ "${RUST_AGENT:-}" == "yes" ]; then
# add skipForRustDistros to skipWhenTestingAll if it is not
for td in "${skipForRustDistros[@]}"; do
if distro_in_set "${td}" "${skipWhenTestingAll[@]}"; then
continue
fi
# not found in skipWhenTestingAll, add to it
skipWhenTestingAll+=("${td}")
done
if distro_in_set "${arch}" "${skipForRustArch[@]}"; then
for distro in "${test_distros[@]}"; do
if distro_in_set "${distro}" "${skipWhenTestingAll[@]}"; then
continue
fi
skipWhenTestingAll+=("${distro}")
done
fi
fi
fi

View File

@ -0,0 +1,750 @@
#!/usr/bin/env bash
#
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
set -o errexit
set -o nounset
set -o pipefail
[ -n "${DEBUG:-}" ] && set -o xtrace
readonly script_dir="$(dirname $(readlink -f $0))"
readonly script_name=${0##*/}
readonly project_dir="$(dirname ${script_dir})"
readonly tmp_dir=$(mktemp -t -d osbuilder-test.XXXXXXX)
readonly tmp_rootfs="${tmp_dir}/rootfs-osbuilder"
readonly images_dir="${tmp_dir}/images"
readonly osbuilder_file="/var/lib/osbuilder/osbuilder.yaml"
readonly docker_image="busybox"
readonly systemd_docker_config_file="/etc/systemd/system/docker.service.d/kata-containers.conf"
readonly sysconfig_docker_config_file="/etc/sysconfig/docker"
readonly tests_repo="github.com/kata-containers/tests"
readonly tests_repo_dir="${project_dir}/../tests"
readonly mgr="${tests_repo_dir}/cmd/kata-manager/kata-manager.sh"
readonly test_config=${script_dir}/test_config.sh
readonly rootfs_builder=${project_dir}/rootfs-builder/rootfs.sh
readonly DOCKER_RUNTIME=${DOCKER_RUNTIME:-runc}
readonly RUNTIME=${RUNTIME:-kata-runtime}
readonly MACHINE_TYPE=`uname -m`
readonly CI=${CI:-}
readonly KATA_HYPERVISOR="${KATA_HYPERVISOR:-}"
readonly KATA_DEV_MODE="${KATA_DEV_MODE:-}"
readonly ci_results_dir="/var/osbuilder/tests"
readonly dracut_dir=${project_dir}/dracut
build_images=1
build_initrds=1
typeset -a distrosSystemd distrosAgent
distrosSystemd=()
distrosAgent=()
# Hashes used to keep track of image sizes.
# - Key: name of distro.
# - Value: colon-separated roots and image sizes ("${rootfs_size}:${image_size}").
typeset -A built_images
typeset -A built_initrds
# If set, show the reason why a container using the built images/initrds could
# not be started. Needed only after all images/initrd built successfully
typeset -A showKataRunFailure=
source ${test_config}
source "${project_dir}/scripts/lib.sh"
usage()
{
cat <<EOT
Usage: $script_name [options] [command | <distro>]
Options:
-h | --help # Show usage.
--list # List all distros that can be tested.
--test-images-only # Only run images tests for the list of distros under test.
--test-initrds-only # Only run initrds tests for the list of distros under test.
Commands:
help : Show usage.
When <distro> is specified, tests are run only for the specified <distro>.
Otherwise, tests are run on all distros.
$(basename ${test_config}) includes a list of distros to exclude from testing,
depending on the detected test environment. However, when a <distro> is specified,
distro exclusion based on $(basename ${test_config}) is not enforced.
EOT
}
# Add an entry to the specified stats file
add_to_stats_file()
{
local statsfile="$1"
local name="$2"
local entry="$3"
local entry_type="$4"
local rootfs_size_bytes
local rootfs_size_mb
local image_size_bytes
local image_size_mb
rootfs_size_bytes=$(echo "$entry"|cut -d: -f1)
image_size_bytes=$(echo "$entry"|cut -d: -f2)
rootfs_size_mb=$(bc <<< "scale=2; ${rootfs_size_bytes} / 2^20")
image_size_mb=$(bc <<< "scale=2; ${image_size_bytes} / 2^20")
printf '%12.12s\t%10.10s\t%12.12s\t%10.10s\t%-8.8s\t%-20.20s\n' \
"${image_size_bytes}" \
"${image_size_mb}" \
"${rootfs_size_bytes}" \
"${rootfs_size_mb}" \
"${entry_type}" \
"${name}" >> "$statsfile"
}
# Show the sizes of all the generated initrds and images
show_stats()
{
local name
local sizes
local tmpfile=$(mktemp)
# images
for name in "${!built_images[@]}"
do
sizes=${built_images[$name]}
add_to_stats_file "$tmpfile" "$name" "$sizes" 'image'
done
# initrds
if [ "$KATA_HYPERVISOR" != "firecracker" ]; then
for name in "${!built_initrds[@]}"
do
sizes=${built_initrds[$name]}
add_to_stats_file "$tmpfile" "$name" "$sizes" 'initrd'
done
fi
info "Image and rootfs sizes (in bytes and MB), smallest image first:"
echo
printf '%12.12s\t%10.10s\t%12.12s\t%10.10s\t%-8.8s\t%-20.20s\n' \
"image-bytes" \
"image-MB" \
"rootfs-bytes" \
"rootfs-MB" \
"Type" \
"Name"
sort -k1,1n -k3,3n "$tmpfile"
rm -f "${tmpfile}"
}
# Run a kata-manager.sh command
run_mgr()
{
[ -n "${KATA_DEV_MODE:-}" ] && return
silent_run $mgr $*
}
exit_handler()
{
if [ "$?" -eq 0 ]
then
info "tests passed successfully - cleaning up"
# Rootfs and images are owned by root
sudo -E rm -rf "${tmp_rootfs}"
sudo -E rm -rf "${images_dir}"
rm -rf "${tmp_dir}"
# Restore the default image in config file
[ -n "${TRAVIS:-}" ] || run_mgr configure-image
return
fi
info "ERROR: test failed"
# The test failed so dump what we can
if [ -d "${tmp_rootfs}" ]; then
info "rootfs:"
sudo -E ls -l "${tmp_rootfs}" >&2
sudo -E rm -rf "${tmp_rootfs}"
else
info "no rootfs created"
# If no rootfs are created, no need to dump other info
return
fi
if [ -d "${images_dir}" ]; then
info "images:"
sudo -E ls -l "${images_dir}" >&2
sudo -E rm -rf "${images_dir}"
else
info "no images created"
# If no images are created, no need to dump other info
return
fi
if [ -z "${showKataRunFailure}" ]; then
# Restore the default image in config file
run_mgr configure-image
return
fi
info "local runtime config:"
cat /etc/kata-containers/configuration.toml >&2
info "main runtime config:"
cat /usr/share/defaults/kata-containers/configuration.toml >&2
info "collect script output:"
sudo -E kata-collect-data.sh >&2
info "processes:"
sudo -E ps -efwww | egrep "docker|kata" >&2
# Restore the default image in config file
run_mgr configure-image
}
die()
{
msg="$*"
echo "ERROR: $msg" >&2
exit 1
}
info()
{
s="$*"
echo -en "INFO: $s\n" >&2
}
debug()
{
[ -z "${TEST_DEBUG:-}" ] && return
s="$*"
echo -e "DBG: $s" >&2
}
# Run a command in silent mode using chronic.
# The command output is printed only if the command fails
silent_run()
{
typeset -a commandLine=("$@")
info "running: ${commandLine[@]}"
if [ -z "${DEBUG:-}" ]; then
chronic "${commandLine[@]}"
else
"${commandLine[@]}"
fi
}
set_runtime()
{
local name="$1"
[ -z "$name" ] && die "need name"
[ -n "${KATA_DEV_MODE}" ] && return
# Travis doesn't support VT-x
[ -n "${TRAVIS:-}" ] && return
if [ "$KATA_HYPERVISOR" != "firecracker" ]; then
if [ -f "$sysconfig_docker_config_file" ]; then
docker_config_file="$sysconfig_docker_config_file"
sed_script="s|^( *DOCKER_OPTS=.+--default-runtime[= ] *)[^ \"]+(.*\"$)|\1${name}\2|g"
else
docker_config_file="$systemd_docker_config_file"
sed_script="s/--default-runtime[= ][^ ]*/--default-runtime=${name}/g"
fi
sudo -E sed -i -E "$sed_script" "$docker_config_file"
sudo -E systemctl daemon-reload
sudo -E systemctl restart docker
fi
}
setup()
{
mkdir -p "${images_dir}"
if [ -n "$CI" ]; then
sudo -E rm -rf ${ci_results_dir}
sudo -E mkdir -p ${ci_results_dir}
fi
# Travis doesn't support VT-x
[ -n "${TRAVIS:-}" ] && return
[ ! -d "${tests_repo_dir}" ] && git clone "https://${tests_repo}" "${tests_repo_dir}"
if [ -z "${KATA_DEV_MODE}" ]; then
mkdir -p /etc/kata-containers/
sudo cp -a /usr/share/defaults/kata-containers/configuration.toml /etc/kata-containers/configuration.toml
else
info "Running with KATA_DEV_MODE set, skipping installation of docker and kata packages"
fi
run_mgr enable-debug
# "docker build" does not work with a VM-based runtime, and
# also does not accept a --runtime option, so our only
# option is to overwrite the system docker default runtime
set_runtime "${DOCKER_RUNTIME}"
}
# Fetches the distros test configuration from the distro-specific config.sh file.
# $1 : only fetch configuration for the distro with name $1. When not specified,
# fetch configuration for all distros.
get_distros_config()
{
local distro="$1"
local distrosList
local -A distroCfg=(\
[INIT_PROCESS]=\
[ARCH_EXCLUDE_LIST]=\
)
if [ -n "$distro" ]; then
distrosList=("$distro")
# When specifying a single distro name, skip does not apply
skipWhenTestingAll=()
else
distrosList=($(make list-distros))
fi
for d in ${distrosList[@]:-}; do
debug "Getting config for distro $d"
distroPattern="\<${d}\>"
if [[ "${skipWhenTestingAll[@]:-}" =~ $distroPattern ]]; then
info "Skipping distro $d as specified by $(basename ${test_config})"
continue
fi
tmpfile=$(mktemp /tmp/osbuilder-$d-config.XXX)
${rootfs_builder} -t $d > $tmpfile
# Get value of all keys in distroCfg
for k in ${!distroCfg[@]}; do
distroCfg[$k]="$(awk -v cfgKey=$k 'BEGIN{FS=":\t+"}{if ($1 == cfgKey) print $2}' $tmpfile)"
debug "distroCfg[$k]=${distroCfg[$k]}"
done
rm -f $tmpfile
machinePattern="\<${MACHINE_TYPE}\>"
if [[ "${distroCfg[ARCH_EXCLUDE_LIST]}" =~ $machinePattern ]]; then
info "Skipping distro $d on architecture $MACHINE_TYPE"
continue
fi
case "${distroCfg[INIT_PROCESS]}" in
systemd) distrosSystemd+=($d) ;;
kata-agent) distrosAgent+=($d) ;;
*) die "Invalid init process specified for distro $d: \"${distroCfg[INIT_PROCESS]}\"" ;;
esac
done
}
create_container()
{
# If KATA_DEV_MODE is set, we don't have any way to point kata-runtime
# at the image/initrd to boot, so there's nothing to do
[ -n "${KATA_DEV_MODE}" ] && return
out=$(mktemp)
local file="/proc/version"
# Create a container using the runtime under test which displays a
# file that is expected to exist.
docker run --rm -i --runtime "${RUNTIME}" "$docker_image" cat "${file}" > "$out"
info "contents of docker image ${docker_image} container file '${file}':"
cat "${out}" >&2
[ -s "$out" ]
rm -f "$out"
}
install_image_create_container()
{
local file="$1"
[ -z "$file" ] && die "need file"
[ ! -e "$file" ] && die "file does not exist: $file"
# Travis doesn't support VT-x
[ -n "${TRAVIS:-}" ] && return
showKataRunFailure=1
run_mgr reset-config
if [ "${RUST_AGENT:-}" = "yes" ]; then
run_mgr enable-vsock
fi
run_mgr configure-image "$file"
create_container
showKataRunFailure=
}
install_initrd_create_container()
{
local file="$1"
[ -z "$file" ] && die "need file"
[ ! -e "$file" ] && die "file does not exist: $file"
# Travis doesn't support VT-x
[ -n "${TRAVIS:-}" ] && return
showKataRunFailure=1
run_mgr reset-config
if [ "${RUST_AGENT:-}" = "yes" ]; then
run_mgr enable-vsock
fi
run_mgr configure-initrd "$file"
create_container
showKataRunFailure=
}
# Displays a list of distros which can be tested
list_distros()
{
tr " " "\n" <<< "${distrosSystemd[@]:-} ${distrosAgent[@]:-}" | sort
}
#
# Calls the `GNU make` utility with the set of passed arguments.
# Arguments can either be make targets or make variables assignments (in the form of VARIABLE=<value>)
#
call_make() {
targetType=$1
shift
makeVars=()
makeTargets=()
# Split args between make variable and targets
for t in $@; do
# RE to match a make variable assignment
pattern="^\w+\="
if [[ "$t" =~ $pattern ]]; then
makeVars+=("$t")
else
makeTargets+=($targetType-$t)
fi
done
# Set a default make target
[ "${#makeTargets[@]}" = "0" ] && makeTargets+=($targetType)
makeJobs=
if [ -z "$CI" ]; then
((makeJobs=$(nproc) / 2))
fi
# When calling make, do not use the silent_run wrapper, pass the
# OSBUILDER_USE_CHRONIC instead.
# In this way running make in parallel mode will, in case of failure, just
# show the print out of the single target failing.
makeVars+=(OSBUILDER_USE_CHRONIC=1)
info "Starting make with \n\
# of // jobs: ${makeJobs:-[unlimited]} \n\
targets: ${makeTargets[@]} \n\
variables: ${makeVars[@]}"
sudo -E make -j $makeJobs ${makeTargets[@]} ${makeVars[@]}
}
make_rootfs() {
call_make rootfs $@
}
make_image() {
call_make image $@
}
make_initrd() {
call_make initrd $@
}
get_rootfs_size() {
[ $# -ne 1 ] && die "get_rootfs_size: wrong number of arguments"
local rootfs_dir=$1
! [ -d "$rootfs_dir" ] && die "$rootfs_dir is not a valid rootfs path"
sudo -E du -sb "${rootfs_dir}" | awk '{print $1}'
}
show_rootfs_metadata() {
[ $# -ne 1 ] && die "show_rootfs_metadata: wrong number of arguments"
local rootfs_path=$1
local osbuilder_file_fullpath="${rootfs_path}/${osbuilder_file}"
yamllint "${osbuilder_file_fullpath}"
info "osbuilder metadata file for $d:"
cat "${osbuilder_file_fullpath}" >&2
}
# Create an image and/or initrd for the available distributions,
# then test each by configuring the runtime and creating a container.
#
# When passing the name of a distribution, tests are run against that
# distribution only.
#
# Parameters:
#
# 1: distro name.
#
test_distros()
{
local distro="$1"
get_distros_config "$distro"
local commonMakeVars=( \
USE_DOCKER=true \
DOCKER_RUNTIME="${DOCKER_RUNTIME}" \
ROOTFS_BUILD_DEST="$tmp_rootfs" \
IMAGES_BUILD_DEST="$images_dir" \
DEBUG=1 )
# If a distro was specified, filter out the distro list to only include that distro
if [ -n "$distro" ]; then
pattern="\<$distro\>"
if [[ "${distrosAgent[@]:-}" =~ $pattern ]]; then
distrosAgent=($distro)
distrosSystemd=()
elif [[ "${distrosSystemd[@]:-}" =~ $pattern ]]; then
distrosSystemd=($distro)
distrosAgent=()
build_initrds=
else
die "Not a valid distro: $distro"
fi
info "Running tests for distro: $distro"
else
info "Running tests for all distros"
# Graceful exit allowed for selected distros, but only when testing all distros
commonMakeVars+=(GRACEFUL_EXIT=1)
fi
# distro with systemd as init -> normal rootfs image
# distro with kata-agent as init -> normal rootfs image AND initrd image
# If user does not need rootfs images, then do not build systemd rootfses
[ -z "$build_images" ] && distrosSystemd=()
# Build systemd and agent rootfs with 2 separate jobs
bgJobs=()
if [ ${#distrosSystemd[@]} -gt 0 ]; then
info "building rootfses with systemd as init: ${distrosSystemd[@]}"
make_rootfs ${commonMakeVars[@]} "${distrosSystemd[@]}" &
bgJobs+=($!)
fi
if [ "$KATA_HYPERVISOR" != "firecracker" ]; then
if [ ${#distrosAgent[@]} -gt 0 ]; then
info "building all rootfses with kata-agent as init"
make_rootfs ${commonMakeVars[@]} AGENT_INIT=yes "${distrosAgent[@]}" &
bgJobs+=($!)
fi
fi
# Check for build failures (`wait` remembers up to CHILD_MAX bg processes exit status)
for j in ${bgJobs[@]}; do
if ! wait $j; then
info "Background rootfs build job failed:"
#find completed an uncompleted jobs checking for the rootfs marker
local marker=$(make print-ROOTFS_MARKER_SUFFIX)
[ -z "$marker" ] && die "Invalid rootfs marker"
typeset -a completed=($(find ${tmp_rootfs} -name ".*${marker}" -exec basename {} \; | sed -E "s/\.(.+)${marker}/\1/"))
for d in "${distrosSystemd[@]:-}" "${distrosAgent[@]:-}"; do
if [[ "${completed[@]}" =~ $d ]]; then
info "- $d : completed"
else
info "- $d : failed"
fi
done
die "rootfs build failed"
fi
done
# TODO: once support for rootfs images with kata-agent as init is in place,
# uncomment the following line
# for d in ${distrosSystemd[@]} ${distrosAgent[@]}; do
for d in ${distrosSystemd[@]:-}; do
local rootfs_path="${tmp_rootfs}/${d}_rootfs"
local image_path="${images_dir}/kata-containers-image-$d.img"
local rootfs_size=$(get_rootfs_size "$rootfs_path")
# Skip failed distros
if [ -e "${tmp_rootfs}/${d}_fail" ]; then
info "Building rootfs for ${d} failed, not creating an image"
[ -n "$CI" ] && sudo -E touch "${ci_results_dir}/${d}_fail"
continue
fi
show_rootfs_metadata "$rootfs_path"
info "Making rootfs image for ${d}"
make_image ${commonMakeVars[@]} $d
local image_size=$(stat -c "%s" "${image_path}")
built_images["${d}"]="${rootfs_size}:${image_size}"
info "Creating container for ${d}"
install_image_create_container $image_path
done
for d in ${distrosAgent[@]:-}; do
local rootfs_path="${tmp_rootfs}/${d}_rootfs"
local initrd_path="${images_dir}/kata-containers-initrd-$d.img"
local rootfs_size=$(get_rootfs_size "$rootfs_path")
# Skip failed distros
if [ -e "${tmp_rootfs}/${d}_fail" ]; then
info "Building rootfs for ${d} failed, not creating an initrd"
[ -n "$CI" ] && touch "${ci_results_dir}/${d}_fail"
continue
fi
if [ "$KATA_HYPERVISOR" != "firecracker" ]; then
info "Making initrd image for ${d}"
make_initrd ${commonMakeVars[@]} AGENT_INIT=yes $d
local initrd_size=$(stat -c "%s" "${initrd_path}")
built_initrds["${d}"]="${rootfs_size}:${initrd_size}"
info "Creating container for ${d}"
install_initrd_create_container $initrd_path
fi
done
show_stats
}
test_dracut()
{
local initrd_path="${images_dir}/kata-containers-initrd-dracut.img"
local image_path="${images_dir}/kata-containers-image-dracut.img"
local rootfs_path="${tmp_rootfs}/dracut_rootfs"
local overlay_path="${tmp_rootfs}/dracut_overlay"
detect_go_version ||
die "Could not detect the required Go version for AGENT_VERSION='${AGENT_VERSION:-master}'."
detect_rust_version ||
die "Could not detect the required rust version for AGENT_VERSION='${AGENT_VERSION:-master}'."
detect_cmake_version ||
die "Could not detect the required cmake version for AGENT_VERSION='${AGENT_VERSION:-master}'."
detect_musl_version ||
die "Could not detect the required musl version for AGENT_VERSION='${AGENT_VERSION:-master}'."
generate_dockerfile ${dracut_dir}
info "Creating container for dracut"
silent_run docker build -t dracut-test-osbuilder ${dracut_dir}
typeset -a dockerRunArgs=(\
--rm \
--runtime="${DOCKER_RUNTIME}" \
-v "${project_dir}":"${project_dir}" \
-v "${tmp_dir}":"${tmp_dir}" \
-v /etc/localtime:/etc/localtime:ro \
dracut-test-osbuilder \
)
typeset -a makeVars=(\
BUILD_METHOD=dracut \
TARGET_INITRD="${initrd_path}" \
TARGET_IMAGE=${image_path} \
TARGET_ROOTFS=${rootfs_path} \
DRACUT_OVERLAY_DIR="${overlay_path}" \
USE_DOCKER=1 \
DOCKER_RUNTIME="${DOCKER_RUNTIME}" \
)
info "Making image for dracut inside a container"
silent_run docker run ${dockerRunArgs[@]} make -C ${project_dir} ${makeVars[@]} rootfs
make_image ${makeVars[@]}
local image_size=$(stat -c "%s" "${image_path}")
local rootfs_size=$(get_rootfs_size "$rootfs_path")
built_images["dracut"]="${rootfs_size}:${image_size}"
info "Creating container for dracut"
install_image_create_container $image_path
if [ "$KATA_HYPERVISOR" != "firecracker" ]; then
info "Making initrd for dracut inside a container"
silent_run docker run ${dockerRunArgs[@]} make -C ${project_dir} ${makeVars[@]} AGENT_INIT=yes clean initrd
local initrd_size=$(stat -c "%s" "${initrd_path}")
built_initrds["dracut"]="${rootfs_size}:${initrd_size}"
install_initrd_create_container $initrd_path
fi
}
main()
{
local args=$(getopt \
-n "$script_name" \
-a \
--options="h" \
--longoptions="help distro: list test-images-only test-initrds-only" \
-- "$@")
eval set -- "$args"
[ $? -ne 0 ] && { usage >&2; exit 1; }
local distro=
while [ $# -gt 1 ]
do
case "$1" in
-h|--help) usage; exit 0 ;;
--list) list_distros; exit 0;;
--test-images-only)
build_initrds=
;;
--test-initrds-only)
build_images=
;;
--) shift; break ;;
esac
shift
done
# Consume getopt cruft
[ "$1" = "--" ] && shift
case "${1:-}" in
help) usage; exit 0;;
*) distro="${1:-}";;
esac
trap exit_handler EXIT ERR
setup
# Run only if distro is not dracut
[ "${distro:-}" != "dracut" ] && test_distros "$distro"
# Run if distro is empty or it is dracut
[ -z "$distro" ] || [ "$distro" = "dracut" ] && test_dracut
# We shouldn't really need a message like this but the CI can fail in
# mysterious ways so make it clear!
info "all tests finished successfully"
}
main "$@"