From: Serge Hallyn Date: Sat, 5 Aug 2017 16:24:25 +0000 (-0500) Subject: Add OCI container creation template X-Git-Tag: lxc-3.0.0.beta1~224^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0ef43a5c1d635835950335375f0dd71bf1abe384;p=thirdparty%2Flxc.git Add OCI container creation template Closes #1813 This adds preliminary (but working) support for creating application containers from OCI formats. Examples: create a container from a local OCI layout in ../oci: sudo lxc-create -t oci -n a1 -- -u oci:../oci:alpine Or, create a container pulling from the docker hub. sudo lxc-create -t oci -n u1 -- -u docker://ubuntu The url is specified in the same format as for 'skopeo copy'. Comments appreciated. Signed-off-by: Serge Hallyn --- diff --git a/configure.ac b/configure.ac index 2057df762..642b78e7e 100644 --- a/configure.ac +++ b/configure.ac @@ -898,6 +898,7 @@ AC_CONFIG_FILES([ templates/lxc-fedora templates/lxc-fedora-legacy templates/lxc-gentoo + templates/lxc-oci templates/lxc-openmandriva templates/lxc-opensuse templates/lxc-oracle diff --git a/templates/Makefile.am b/templates/Makefile.am index 61f062d42..c4a5b9555 100644 --- a/templates/Makefile.am +++ b/templates/Makefile.am @@ -12,6 +12,7 @@ templates_SCRIPTS = \ lxc-fedora \ lxc-fedora-legacy \ lxc-gentoo \ + lxc-oci \ lxc-openmandriva \ lxc-opensuse \ lxc-oracle \ diff --git a/templates/lxc-oci.in b/templates/lxc-oci.in new file mode 100755 index 000000000..f715125f7 --- /dev/null +++ b/templates/lxc-oci.in @@ -0,0 +1,212 @@ +#!/bin/bash + +# Create application containers from OCI images + +# Copyright © 2014 Stéphane Graber +# Copyright © 2017 Serge Hallyn +# +# This library 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. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# USA + +set -eu +# set -x # debug + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +# Check for required binaries +for bin in skopeo umoci jq; do + if ! type $bin >/dev/null 2>&1; then + echo "ERROR: Missing required tool: $bin" 1>&2 + exit 1 + fi +done + +# Some useful functions +cleanup() { + if [ -d "$DOWNLOAD_TEMP" ]; then + rm -Rf $DOWNLOAD_TEMP + fi +} + +in_userns() { + [ -e /proc/self/uid_map ] || { echo no; return; } + while read line; do + fields=$(echo $line | awk '{ print $1 " " $2 " " $3 }') + [ "$fields" = "0 0 4294967295" ] && { echo no; return; } || true + echo $fields | grep -q " 0 1$" && { echo userns-root; return; } || true + done < /proc/self/uid_map + + [ "$(cat /proc/self/uid_map)" = "$(cat /proc/1/uid_map)" ] && \ + { echo userns-root; return; } + echo yes +} + +# get entrypoint from oci image. Use sh if unspecified +# TODO - we can get other things like resource limits here +getep() { + basedir="$1" + q="$2" + + + digest=`cat "${basedir}/index.json" | jq --arg q "$q" '.manifests[] | if .annotations."org.opencontainers.image.ref.name" == $q then .digest else null end' | sed -e 's/"//g'` + if [ -z "${digest}" ]; then + echo "$q not found in index.json" >&2 + echo "/bin/sh" + return + fi + + # Ok we have the image config digest, now get the config from that, + d=${digest:7} + cdigest=`cat "${basedir}/blobs/sha256/${d}" | jq '.config.digest' | sed -e 's/"//g'` + if [ -z "${cdigest}" ]; then + echo "container config not found" >&2 + echo "/bin/sh" + return + fi + + d2=${cdigest:7} + ep=`cat "${basedir}/blobs/sha256/${d2}" | jq -c '.config.Entrypoint' | sed -e 's/^\[//; s/\]$//; s/","/" "/'` + cmd=`cat "${basedir}/blobs/sha256/${d2}" | jq -c '.config.Cmd' | sed -e 's/^\[//; s/\]$//; s/","/" "/'` + if [ "${ep}" = "null" ]; then + ep="${cmd}" + if [ "${ep}" = "null" ]; then + ep="/bin/sh" + fi + elif [ "${cmd}" != "null" ]; then + ep="${ep} ${cmd}" + fi + + if [ -z "${ep}" ]; then + echo "/bin/sh" + return + fi + echo "${ep}" + return +} + +usage() { + cat < ]: The OCI image URL + +LXC internal arguments (do not pass manually!): +[ --name ]: The container name +[ --path ]: The path to the container +[ --rootfs ]: The path to the container's rootfs +[ --mapped-uid ]: A uid map (user namespaces) +[ --mapped-gid ]: A gid map (user namespaces) + +EOF + return 0 +} + +options=$(getopt -o u:h -l help,name:,path:,\ +rootfs:,mapped-uid:,mapped-gid: -- "$@") + +if [ $? -ne 0 ]; then + usage + exit 1 +fi +eval set -- "$options" + +OCI_URL="" +LXC_MAPPED_GID= +LXC_MAPPED_UID= +LXC_NAME= +LXC_PATH= +LXC_ROOTFS= + +while :; do + case "$1" in + -h|--help) usage && exit 1;; + -u|--url) OCI_URL=$2; shift 2;; + --name) LXC_NAME=$2; shift 2;; + --path) LXC_PATH=$2; shift 2;; + --rootfs) LXC_ROOTFS=$2; shift 2;; + --mapped-uid) LXC_MAPPED_UID=$2; shift 2;; + --mapped-gid) LXC_MAPPED_GID=$2; shift 2;; + *) break;; + esac +done + +# Check that we have all variables we need +if [ -z "$LXC_NAME" ] || [ -z "$LXC_PATH" ] || [ -z "$LXC_ROOTFS" ]; then + echo "ERROR: Not running through LXC." 1>&2 + exit 1 +fi + +if [ -z "$OCI_URL" ]; then + echo "ERROR: no OCI URL given" + exit 1 +fi + +USERNS=$(in_userns) + +if [ "$USERNS" != "no" ]; then + if [ "$USERNS" = "yes" ]; then + if [ -z "$LXC_MAPPED_UID" ] || [ "$LXC_MAPPED_UID" = "-1" ]; then + echo "ERROR: In a user namespace without a map." 1>&2 + exit 1 + fi + DOWNLOAD_MODE="user" + DOWNLOAD_TARGET="user" + else + DOWNLOAD_MODE="user" + DOWNLOAD_TARGET="system" + fi +fi + +# Trap all exit signals +trap cleanup EXIT HUP INT TERM + +if ! type mktemp >/dev/null 2>&1; then + DOWNLOAD_TEMP=/tmp/lxc-oci.$$ + mkdir -p $DOWNLOAD_TEMP +else + DOWNLOAD_TEMP=$(mktemp -d) +fi + +# Download the image - TODO - cache +skopeo copy "${OCI_URL}" "oci:${DOWNLOAD_TEMP}:latest" + +# Unpack the rootfs +echo "Unpacking the rootfs" + +umoci unpack --image "${DOWNLOAD_TEMP}:latest" "${LXC_ROOTFS}.tmp" +rmdir "${LXC_ROOTFS}" +mv "${LXC_ROOTFS}.tmp/rootfs" "${LXC_ROOTFS}" +entrypoint=$(getep ${DOWNLOAD_TEMP} latest) +rm -rf "${LXC_ROOTFS}.tmp" + +LXC_CONF_FILE="${LXC_PATH}/config" +echo "lxc.execute.cmd = ${entrypoint}" >> "${LXC_CONF_FILE}" +echo "lxc.mount.auto = proc:mixed sys:mixed cgroup:mixed" >> "${LXC_CONF_FILE}" + +echo "lxc.uts.name = ${LXC_NAME}" >> ${LXC_PATH}/config + +if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then + chown $LXC_MAPPED_UID $LXC_PATH/config $LXC_PATH/fstab >/dev/null 2>&1 || true +fi +if [ -n "$LXC_MAPPED_GID" ] && [ "$LXC_MAPPED_GID" != "-1" ]; then + chgrp $LXC_MAPPED_GID $LXC_PATH/config $LXC_PATH/fstab >/dev/null 2>&1 || true +fi + +exit 0