mirror of
https://github.com/OpenNebula/one.git
synced 2025-02-14 01:57:24 +03:00
parent
9ea10dd172
commit
28bfcdb3c2
@ -22,7 +22,7 @@ class OneImageHelper < OpenNebulaHelper::OneHelper
|
||||
|
||||
# This list contains prefixes that should skip adding user home to the path
|
||||
# This must have the same content as the case $FROM in downloader.sh
|
||||
PREFIXES = %w[http https ssh s3 rbd vcenter lxd docker]
|
||||
PREFIXES = %w[http https ssh s3 rbd vcenter lxd docker dockerfile]
|
||||
|
||||
TEMPLATE_OPTIONS=[
|
||||
{
|
||||
|
@ -35,6 +35,7 @@ end
|
||||
$LOAD_PATH << RUBY_LIB_LOCATION
|
||||
$LOAD_PATH << RUBY_LIB_LOCATION + '/cli'
|
||||
|
||||
require 'tempfile'
|
||||
require 'command_parser'
|
||||
require 'one_helper/oneimage_helper'
|
||||
require 'one_helper/onedatastore_helper'
|
||||
@ -73,6 +74,12 @@ CommandParser::CmdParser.new(ARGV) do
|
||||
:description => 'lock all actions'
|
||||
}
|
||||
|
||||
NO_CONTEXT = {
|
||||
:name => 'no_context',
|
||||
:large => '--no-context',
|
||||
:description => 'Do not add context when building from Dockerfile'
|
||||
}
|
||||
|
||||
########################################################################
|
||||
# Global Options
|
||||
########################################################################
|
||||
@ -83,7 +90,9 @@ CommandParser::CmdParser.new(ARGV) do
|
||||
list_options << OpenNebulaHelper::NUMERIC
|
||||
list_options << OpenNebulaHelper::DESCRIBE
|
||||
|
||||
CREATE_OPTIONS = [OneDatastoreHelper::DATASTORE, OneImageHelper::IMAGE]
|
||||
CREATE_OPTIONS = [OneDatastoreHelper::DATASTORE,
|
||||
OneImageHelper::IMAGE,
|
||||
NO_CONTEXT]
|
||||
|
||||
########################################################################
|
||||
# Formatters for arguments
|
||||
@ -168,6 +177,15 @@ CommandParser::CmdParser.new(ARGV) do
|
||||
next -1
|
||||
end
|
||||
|
||||
# Add context information when building image (just working on Docker)
|
||||
if (options.key? :no_context) && options[:path]
|
||||
if options[:path].include?('?')
|
||||
options[:path] << '&context=no'
|
||||
else
|
||||
options[:path] << '?context=no'
|
||||
end
|
||||
end
|
||||
|
||||
helper.create_resource(options) do |image|
|
||||
begin
|
||||
if args[0]
|
||||
@ -450,4 +468,73 @@ CommandParser::CmdParser.new(ARGV) do
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
dockerfile_desc = <<-EOT.unindent
|
||||
Create an image based on a Dockerfile
|
||||
EOT
|
||||
|
||||
command :dockerfile,
|
||||
dockerfile_desc,
|
||||
:options => CREATE_OPTIONS +
|
||||
OneImageHelper::TEMPLATE_OPTIONS do
|
||||
# Check user options
|
||||
unless options[:datastore]
|
||||
STDERR.puts 'Datastore to save the image is mandatory: '
|
||||
STDERR.puts '\t -d datastore_id'
|
||||
exit(-1)
|
||||
end
|
||||
|
||||
unless options[:name]
|
||||
STDERR.puts 'No name provided'
|
||||
exit(-1)
|
||||
end
|
||||
|
||||
unless options[:size]
|
||||
STDERR.puts 'No size given'
|
||||
exit(-1)
|
||||
end
|
||||
|
||||
# Prepare editor
|
||||
tmp = Tempfile.new('dockerfile')
|
||||
|
||||
if ENV['EDITOR']
|
||||
editor_path = ENV['EDITOR']
|
||||
else
|
||||
editor_path = EDITOR_PATH
|
||||
end
|
||||
|
||||
system("#{editor_path} #{tmp.path}")
|
||||
|
||||
unless $CHILD_STATUS.exitstatus.zero?
|
||||
STDERR.puts('Editor not defined')
|
||||
exit(-1)
|
||||
end
|
||||
|
||||
tmp.close
|
||||
|
||||
# Create image
|
||||
helper.create_resource(options) do |image|
|
||||
begin
|
||||
b64 = Base64.strict_encode64(File.read(tmp.path))
|
||||
options[:path] = "dockerfile:///?fileb64=#{b64}&" \
|
||||
"size=#{options[:size]}"
|
||||
|
||||
options[:path] << '&context=no' if options.key?(:no_context)
|
||||
|
||||
res = OneImageHelper.create_image_template(options)
|
||||
|
||||
if res.first != 0
|
||||
STDERR.puts res.last
|
||||
next -1
|
||||
end
|
||||
|
||||
template = res.last
|
||||
|
||||
image.allocate(template, options[:datastore], false)
|
||||
rescue StandardError => e
|
||||
STDERR.puts e.message
|
||||
exit(-1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -16,7 +16,7 @@
|
||||
# limitations under the License. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
if [ -z "${ONE_LOCATION}" ]; then
|
||||
if [ -z "$ONE_LOCATION" ]; then
|
||||
LIB_LOCATION=/usr/lib/one
|
||||
VAR_LOCATION=/var/lib/one
|
||||
SHARE_LOCATION=/usr/share/one
|
||||
@ -31,16 +31,16 @@ fi
|
||||
#-------------------------------------------------------------------------------
|
||||
# Downloader configuration attributes
|
||||
#-------------------------------------------------------------------------------
|
||||
DRIVER_PATH=$(dirname $0)
|
||||
DRIVER_PATH=$(dirname "$0")
|
||||
MARKET_URL=$1
|
||||
|
||||
MK_DOCKER=$LIB_LOCATION/sh/create_docker_image.sh
|
||||
MK_DOCKER="$LIB_LOCATION/sh/create_docker_image.sh"
|
||||
|
||||
#Default DNS server to download the packages
|
||||
DNS_SERVER="1.1.1.1"
|
||||
|
||||
#Directory used to download packages
|
||||
TMP_DIR=/var/tmp
|
||||
TMP_DIR="/var/tmp"
|
||||
|
||||
#Context location
|
||||
CONTEXT_PATH="$SHARE_LOCATION/context"
|
||||
@ -54,75 +54,119 @@ DOCKERFILE="$SHARE_LOCATION/dockerhub"
|
||||
# something fails
|
||||
#-------------------------------------------------------------------------------
|
||||
function clean {
|
||||
docker rm -f "$container_id" > /dev/null 2>&1 || true
|
||||
docker image rm -f one"$sid" > /dev/null 2>&1
|
||||
|
||||
docker rm -f $container_id > /dev/null 2>&1 || true
|
||||
docker image rm -f one$sid > /dev/null 2>&1
|
||||
|
||||
rm -rf $dockerdir
|
||||
rm -rf "$dockerdir"
|
||||
}
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
id=$(echo "$RANDOM-$RANDOM-$RANDOM-$RANDOM-$RANDOM")
|
||||
sid=$(echo "$id" | cut -d '-' -f 1)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Parse downloader URL
|
||||
#-------------------------------------------------------------------------------
|
||||
# URL is in the form
|
||||
# docker://<conatiner_name>?size=&filesystem=&format=&distro=&tag=
|
||||
# docker://<container_name>?size=&filesystem=&format=&distro=&tag=
|
||||
# dockerfile://<path_to_file>?size=&filesystem=&format=&distro=
|
||||
# dockerfile://?fileb64=&size=&filesystem=&format=&distro=
|
||||
#
|
||||
# consinter_name: As listed in docker hub. e.g. alpine
|
||||
# container_name: As listed in docker hub. e.g. alpine
|
||||
# size: in MB for the resulting image
|
||||
# filesystem: filesystem type e.g. ext4
|
||||
# format: image format e.g. raw or qcow2
|
||||
# distro: base image distro to install contents
|
||||
# tag: one of the image supported tags
|
||||
# fileb64: dockerfile in base 64
|
||||
# context: yes to generate image with context packages, falso otherwise
|
||||
#-------------------------------------------------------------------------------
|
||||
id=`echo "$RANDOM-$RANDOM-$RANDOM-$RANDOM-$RANDOM"`
|
||||
sid=`echo $id | cut -d '-' -f 1`
|
||||
|
||||
url=`echo $MARKET_URL | grep -oP "^"docker://"\K.*"`
|
||||
arguments=`echo $url | cut -d '?' -f 2`
|
||||
if [[ $MARKET_URL == dockerfile* ]]; then
|
||||
url=$(echo "$MARKET_URL" | grep -oP "^"dockerfile://"\K.*")
|
||||
export_from="dockerfile"
|
||||
elif [[ $MARKET_URL == docker* ]]; then
|
||||
url=$(echo "$MARKET_URL" | grep -oP "^"docker://"\K.*")
|
||||
export_from="dockerhub"
|
||||
else
|
||||
echo "Unknown URL format" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
arguments=$(echo "$url" | cut -d '?' -f 2)
|
||||
|
||||
#Create a shell variable for every argument (size=5219, format=raw...)
|
||||
for p in ${arguments//&/ }; do
|
||||
kvp=( ${p/=/ } );
|
||||
k=${kvp[0]};v=${kvp[1]};
|
||||
[ -n "$k" -a -n "$v" ] && eval $k=$v;
|
||||
[ -n "$k" -a -n "$v" ] && eval "$k"="$v";
|
||||
done
|
||||
|
||||
if [ -z $tag ]; then
|
||||
if [ -z "$tag" ]; then
|
||||
tag="latest"
|
||||
fi
|
||||
|
||||
docker_hub="`echo $url | cut -d '?' -f 1`:${tag}"
|
||||
docker_image=`echo $docker_hub | cut -f1 -d':'``echo $id |cut -f1 -d'-'`
|
||||
|
||||
dockerdir=$TMP_DIR/$id
|
||||
dockerfile=$dockerdir/dockerfile
|
||||
tarball=$dockerdir/fs.tar
|
||||
img_raw=$dockerdir/img.raw
|
||||
img_qcow=$dockerdir/img.qcow
|
||||
dockerdir="$TMP_DIR/$id"
|
||||
dockerfile="$dockerdir/dockerfile"
|
||||
tarball="$dockerdir/fs.tar"
|
||||
img_raw="$dockerdir/img.raw"
|
||||
img_qcow="$dockerdir/img.qcow"
|
||||
|
||||
# Trap for cleaning temporary directories
|
||||
trap clean EXIT
|
||||
|
||||
mkdir -p $dockerdir
|
||||
mkdir -p $dockerdir/mnt
|
||||
mkdir -p "$dockerdir"
|
||||
mkdir -p "$dockerdir/mnt"
|
||||
|
||||
# Check distro
|
||||
if [ -z $distro ]; then
|
||||
distro=`docker run --rm --entrypoint cat \
|
||||
-e "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
|
||||
$docker_hub /etc/os-release | grep "^ID=.*\n" | cut -d= -f 2 | xargs`
|
||||
cp -rL "$CONTEXT_PATH" "$dockerdir"
|
||||
|
||||
if [ $export_from == "dockerhub" ]; then
|
||||
docker_hub=$(echo "$url" | cut -d '?' -f 1):"$tag"
|
||||
extra=''
|
||||
else
|
||||
if [ -z "$fileb64" ]; then
|
||||
# Dockerfile is a path in the server
|
||||
d_file=$(echo "$url" | cut -d '?' -f 1)
|
||||
|
||||
if [ -f "$d_file" ]; then
|
||||
extra=$(cat "$d_file")
|
||||
else
|
||||
echo "$d_file does not exist" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Dockerfile is encoded in base64
|
||||
extra=$(echo "$fileb64" | base64 -d)
|
||||
fi
|
||||
|
||||
# Read image that needs to be build
|
||||
docker_hub=$(echo "$extra" | grep "^FROM" | cut -d ' ' -f2)
|
||||
|
||||
if [ -z "$docker_hub" ]; then
|
||||
echo "Can\'t identify image to build" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove FROM instruction from dockerfile as it will be added later
|
||||
extra=$(echo "$extra" | sed '/^FROM.*/d')
|
||||
fi
|
||||
|
||||
if [ -z $distro ]; then
|
||||
# Check distro
|
||||
if [ -z "$distro" ]; then
|
||||
distro=$(docker run --rm --entrypoint cat \
|
||||
-e "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
|
||||
"$docker_hub" /etc/os-release | grep "^ID=.*\n" | cut -d= -f 2 | xargs)
|
||||
fi
|
||||
|
||||
if [ -z "$distro" ]; then
|
||||
echo "Cannot identified $docker_hub distribution" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#---------------------------------------------------------------------------
|
||||
# Create a DockerFile
|
||||
#-------------------------------------------------------------------------------
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
case "$distro" in
|
||||
debian|ubuntu)
|
||||
@ -141,53 +185,60 @@ esac
|
||||
#Replace variables _docker_hub and _commands
|
||||
dockerfile_template=$(cat "$DOCKERFILE/dockerfile")
|
||||
dockerfile_template=${dockerfile_template/\%IMAGE_ID\%/$docker_hub}
|
||||
dockerfile_template=${dockerfile_template/\%COMMANDS\%/$commands}
|
||||
|
||||
# Only add context if this variable is not set or is set to yes
|
||||
if [ -z "$context" -o "$context" == "yes" ]; then
|
||||
dockerfile_template=${dockerfile_template/\%COMMANDS\%/$commands}
|
||||
else
|
||||
dockerfile_template=${dockerfile_template/\%COMMANDS\%/''}
|
||||
fi
|
||||
|
||||
# Add extra commands, this is the dockerfile in case there is any
|
||||
dockerfile_template=${dockerfile_template/\%EXTRA\%/$extra}
|
||||
|
||||
echo "$dockerfile_template" > "$dockerfile"
|
||||
|
||||
cp -rL $CONTEXT_PATH $dockerdir
|
||||
docker build -t one"$sid" -f "$dockerfile" "$dockerdir" > /dev/null 2>&1
|
||||
|
||||
docker build -t one$sid -f $dockerfile $dockerdir > /dev/null 2>&1
|
||||
|
||||
image_id=`docker images -q one$sid`
|
||||
image_id=$(docker images -q one"$sid")
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Flatten container image
|
||||
#-------------------------------------------------------------------------------
|
||||
container_id=$(docker run -d $image_id /bin/true)
|
||||
container_id=$(docker run -d "$image_id" /bin/true)
|
||||
|
||||
docker export -o $tarball $container_id > /dev/null 2>&1
|
||||
docker export -o "$tarball" "$container_id" > /dev/null 2>&1
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Dump container FS and create image
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# Get tarbal size
|
||||
tar_size=$(stat -c%s $tarball | awk '{ byte =$1 /1024/1024; print byte}' | cut -d '.' -f 1)
|
||||
tar_size=$(stat -c%s "$tarball" | awk '{ byte =$1 /1024/1024; print byte}' | cut -d '.' -f 1)
|
||||
|
||||
# Ensure $size is enough to fit the fs
|
||||
if [ "$tar_size" -ge "$size" ]; then
|
||||
echo "Not enough space, image size must be at least ${tar_size}Mb" 1>&2
|
||||
echo "Not enough space, image size must be at least $tar_size MB" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
qemu-img create -f raw $img_raw ${size}M > /dev/null 2>&1
|
||||
qemu-img create -f raw "$img_raw" "$size"M > /dev/null 2>&1
|
||||
|
||||
case $filesystem in
|
||||
case "$filesystem" in
|
||||
"ext4")
|
||||
mkfs.ext4 -F $img_raw > /dev/null 2>&1
|
||||
mkfs.ext4 -F "$img_raw" > /dev/null 2>&1
|
||||
;;
|
||||
"ext3")
|
||||
mkfs.ext3 -F $img_raw > /dev/null 2>&1
|
||||
mkfs.ext3 -F "$img_raw" > /dev/null 2>&1
|
||||
;;
|
||||
"ext2")
|
||||
mkfs.ext2 -F $img_raw > /dev/null 2>&1
|
||||
mkfs.ext2 -F "$img_raw" > /dev/null 2>&1
|
||||
;;
|
||||
"xfs")
|
||||
mkfs.xfs -f $img_raw > /dev/null 2>&1
|
||||
mkfs.xfs -f "$img_raw" > /dev/null 2>&1
|
||||
;;
|
||||
*)
|
||||
mkfs.ext4 -F $img_raw > /dev/null 2>&1
|
||||
mkfs.ext4 -F "$img_raw" > /dev/null 2>&1
|
||||
;;
|
||||
esac
|
||||
|
||||
@ -195,13 +246,13 @@ esac
|
||||
# Mount container disk image and untar rootfs contents to it
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
sudo -n $MK_DOCKER -d $dockerdir -i $img_raw -t $tarball
|
||||
sudo -n "$MK_DOCKER" -d "$dockerdir" -i "$img_raw" -t "$tarball"
|
||||
|
||||
if [ "$format" == "qcow2" ]; then
|
||||
qemu-img convert -f raw -O qcow2 $img_raw $img_qcow > /dev/null 2>&1
|
||||
cat $img_qcow
|
||||
qemu-img convert -f raw -O qcow2 "$img_raw" "$img_qcow" > /dev/null 2>&1
|
||||
cat "$img_qcow"
|
||||
else
|
||||
cat $img_raw
|
||||
cat "$img_raw"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
@ -3,6 +3,8 @@
|
||||
# IMPORTANT: IMAGE_ID and COMMANDS can **NOT** be changed, they are used
|
||||
# in the download process and are replaced by some variables.
|
||||
#
|
||||
# EXTRA is replaced by the user dockerfile in case it is used
|
||||
#
|
||||
# The rest of the template can be modified depending on your needs.
|
||||
#
|
||||
# It is installed on /usr/share/one/dockerfiles
|
||||
@ -11,7 +13,8 @@ FROM %IMAGE_ID%
|
||||
USER root
|
||||
COPY context /root/context
|
||||
%COMMANDS%
|
||||
%EXTRA%
|
||||
RUN rm -rf /root/context
|
||||
RUN echo "#Generated by OpenNebula" > /etc/resolv.conf
|
||||
RUN rm -f /etc/ssh/ssh_host_* > /dev/null 2>&1
|
||||
RUN usermod -p '*' root > /dev/null 2>&1
|
||||
RUN if [ `command -v usermod` ]; then usermod -p '*' root > /dev/null 2>&1; fi
|
||||
|
@ -331,7 +331,7 @@ lxd://*)
|
||||
file_type="application/octet-stream"
|
||||
command="$VAR_LOCATION/remotes/datastore/lxd_downloader.sh \"$FROM\""
|
||||
;;
|
||||
docker://*)
|
||||
docker://*|dockerfile://*)
|
||||
file_type="application/octet-stream"
|
||||
command="$VAR_LOCATION/remotes/datastore/docker_downloader.sh \"$FROM\""
|
||||
;;
|
||||
|
@ -251,8 +251,13 @@ function fs_size {
|
||||
if [ -d "${SRC}" ]; then
|
||||
SIZE=`set -o pipefail; du -sb "${SRC}" | cut -f1`
|
||||
error=$?
|
||||
elif (echo "${SRC}" | grep -qe '^docker\?://'); then
|
||||
url=`echo ${SRC} | grep -oP "^"docker://"\K.*"`
|
||||
elif (echo "${SRC}" | grep -qe '^docker\?://\|^dockerfile\?://'); then
|
||||
if [[ $SRC == dockerfile* ]]; then
|
||||
url=`echo ${SRC} | grep -oP "^"dockerfile://"\K.*"`
|
||||
elif [[ $SRC == docker* ]]; then
|
||||
url=$(echo $MARKET_URL | grep -oP "^"docker://"\K.*")
|
||||
fi
|
||||
|
||||
arguments=`echo $url | cut -d '?' -f 2`
|
||||
|
||||
for p in ${arguments//&/ }; do
|
||||
|
Loading…
x
Reference in New Issue
Block a user