From 96301988596494438a22cd6c2ef9e1746783f924 Mon Sep 17 00:00:00 2001 From: Akshay Karle Date: Mon, 9 Nov 2015 13:18:56 -0300 Subject: [PATCH 1/8] basic lxc-start-ephemeral support on overlayfs TODO: * support aufs * support tmpfs * documentation * tests --- lib/lxc.rb | 1 + lib/lxc/start_ephemeral.rb | 98 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 lib/lxc/start_ephemeral.rb diff --git a/lib/lxc.rb b/lib/lxc.rb index e118110..7a61e60 100644 --- a/lib/lxc.rb +++ b/lib/lxc.rb @@ -1,2 +1,3 @@ require 'lxc/version' require 'lxc/lxc' +require 'lxc/start_ephemeral' diff --git a/lib/lxc/start_ephemeral.rb b/lib/lxc/start_ephemeral.rb new file mode 100644 index 0000000..1e6db1a --- /dev/null +++ b/lib/lxc/start_ephemeral.rb @@ -0,0 +1,98 @@ +module LXC + class << self + def start_ephemeral(original_container_name, target_container_name, opts={}) + orig = LXC::Container.new(original_container_name) + dest = LXC::Container.new(target_container_name) + raise "#{original_container_name} is not present. Exiting.." unless orig.defined? + raise "#{target_container_name} is already present. Exiting.." if dest.defined? + + dest_path = File.join(LXC.global_config_item('lxc.lxcpath'), target_container_name) + Dir.mkdir(dest_path, 0770) + overlay_dirs = [[orig.config_item("lxc.rootfs"), File.join(dest_path, "rootfs")]] + + dest.load_config(orig.config_file_name) + + dest.set_config_item("lxc.utsname", dest.name) + dest.set_config_item("lxc.rootfs", File.join(dest_path, "rootfs")) + + dest.config_item("lxc.network").each_with_index do |network_type, index| + dest.set_config_item("lxc.network.#{index}.hwaddr", random_mac) if dest.config_item("lxc.network.#{index}.hwaddr") + end + + File.open(File.join(dest_path, 'pre-mount'), 'w+', 0755) do |pre_mount| + pre_mount.puts "#!/bin/sh" + pre_mount.puts %Q{LXC_DIR="#{dest_path}"} + pre_mount.puts %Q{LXC_BASE="#{orig.name}"} + pre_mount.puts %Q{LXC_NAME="#{dest.name}"} + overlay_dirs.each_with_index do |entry, count| + tmpdir = File.join(dest_path, 'tmpfs') + pre_mount.puts "mkdir -p #{tmpdir}" + deltdir = File.join(tmpdir, "/delta#{count}") + workdir = File.join(tmpdir, "/work#{count}") + pre_mount.puts "mkdir -p #{deltdir} #{entry[1]} #{workdir if new_overlay?}" + pre_mount.puts "getfacl -a #{entry[0]} | setfacl --set-file=- #{deltdir} || true" + pre_mount.puts "getfacl -a #{entry[0]} | setfacl --set-file=- #{entry[1]} || true" + + if new_overlay? + pre_mount.puts "mount -n -t overlay -oupperdir=#{deltdir},lowerdir=#{entry[0]},workdir=#{workdir} none #{entry[1]}" + else + pre_mount.puts "mount -n -t overlayfs -oupperdir=#{deltdir},lowerdir=#{entry[0]} none #{entry[1]}" + end + end + + opts[:bdir].each do |host_entry, container_entry| + if Dir.exists?(host_entry) + src_path = File.absolute_path(host_entry) + dst_path = File.join(dest_path, 'rootfs', container_entry) + pre_mount.puts "mkdir -p #{dst_path}" + pre_mount.puts "mount -n --bind #{src_path} #{dst_path}" + else + raise "Couldn't locate #{host_entry} on the host" + end + end + + pre_mount.puts %Q{[ -e $LXC_DIR/configured ] && exit 0} + pre_mount.puts %Q{for file in $LXC_DIR/rootfs/etc/hostname \\} + pre_mount.puts %Q{ $LXC_DIR/rootfs/etc/hosts \\} + pre_mount.puts %Q{ $LXC_DIR/rootfs/etc/sysconfig/network \\} + pre_mount.puts %Q{ $LXC_DIR/rootfs/etc/sysconfig/network-scripts/ifcfg-eth0; do} + pre_mount.puts %Q{ [ -f "$file" ] && sed -i -e "s/$LXC_BASE/$LXC_NAME/" $file} + pre_mount.puts %Q{done} + pre_mount.puts %Q{touch $LXC_DIR/configured} + end + + dest.set_config_item("lxc.hook.pre-mount", File.join(dest_path, "pre-mount")) + + File.open(File.join(dest_path, 'post-stop'), 'w+', 0755) do |post_stop| + post_stop.puts %Q{[ -d #{dest_path} ] && rm -Rf "#{dest_path}"} + end + + dest.set_config_item("lxc.hook.post-stop", File.join(dest_path, "post-stop")) + + dest.save_config + + dest.start(daemonize: opts[:daemonize]) + + if !dest.wait(:running, 5) + dest.stop + dest.destroy if dest.defined? + raise "The container '#{dest.name}' failed to start." + end + end + + private + + def new_overlay? + @new_overlay ||= File.readlines('/proc/filesystems').include?("nodev\toverlay\n") + end + + def random_mac + mac = [0x00, 0x16, 0x3e, + SecureRandom.random_number(0x7f), + SecureRandom.random_number(0xff), + SecureRandom.random_number(0xff) + ] + mac.map {|number| number.to_s(16) }.join(':') + end + end +end From 577cb4592cbfe8223966340db0fc68eb9c3925a9 Mon Sep 17 00:00:00 2001 From: Akshay Karle Date: Wed, 18 Nov 2015 18:49:16 -0300 Subject: [PATCH 2/8] Extract pre-mount and post-stop into separate methods --- lib/lxc/start_ephemeral.rb | 62 ++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/lib/lxc/start_ephemeral.rb b/lib/lxc/start_ephemeral.rb index 1e6db1a..cb58ac8 100644 --- a/lib/lxc/start_ephemeral.rb +++ b/lib/lxc/start_ephemeral.rb @@ -8,7 +8,6 @@ def start_ephemeral(original_container_name, target_container_name, opts={}) dest_path = File.join(LXC.global_config_item('lxc.lxcpath'), target_container_name) Dir.mkdir(dest_path, 0770) - overlay_dirs = [[orig.config_item("lxc.rootfs"), File.join(dest_path, "rootfs")]] dest.load_config(orig.config_file_name) @@ -19,6 +18,39 @@ def start_ephemeral(original_container_name, target_container_name, opts={}) dest.set_config_item("lxc.network.#{index}.hwaddr", random_mac) if dest.config_item("lxc.network.#{index}.hwaddr") end + create_pre_mount(orig, dest, opts) + + create_post_stop(orig, dest, opts) + + dest.save_config + + dest.start(daemonize: opts[:daemonize]) + + if !dest.wait(:running, 5) + dest.stop + dest.destroy if dest.defined? + raise "The container '#{dest.name}' failed to start." + end + end + + private + + def new_overlay? + @new_overlay ||= File.readlines('/proc/filesystems').include?("nodev\toverlay\n") + end + + def random_mac + mac = [0x00, 0x16, 0x3e, + SecureRandom.random_number(0x7f), + SecureRandom.random_number(0xff), + SecureRandom.random_number(0xff) + ] + mac.map {|number| number.to_s(16) }.join(':') + end + + def create_pre_mount(orig, dest, opts) + dest_path = File.join(LXC.global_config_item('lxc.lxcpath'), dest.name) + overlay_dirs = [[orig.config_item("lxc.rootfs"), File.join(dest_path, "rootfs")]] File.open(File.join(dest_path, 'pre-mount'), 'w+', 0755) do |pre_mount| pre_mount.puts "#!/bin/sh" pre_mount.puts %Q{LXC_DIR="#{dest_path}"} @@ -62,37 +94,15 @@ def start_ephemeral(original_container_name, target_container_name, opts={}) end dest.set_config_item("lxc.hook.pre-mount", File.join(dest_path, "pre-mount")) + end + def create_post_stop(orig, dest, opts) + dest_path = File.join(LXC.global_config_item('lxc.lxcpath'), dest.name) File.open(File.join(dest_path, 'post-stop'), 'w+', 0755) do |post_stop| post_stop.puts %Q{[ -d #{dest_path} ] && rm -Rf "#{dest_path}"} end dest.set_config_item("lxc.hook.post-stop", File.join(dest_path, "post-stop")) - - dest.save_config - - dest.start(daemonize: opts[:daemonize]) - - if !dest.wait(:running, 5) - dest.stop - dest.destroy if dest.defined? - raise "The container '#{dest.name}' failed to start." - end - end - - private - - def new_overlay? - @new_overlay ||= File.readlines('/proc/filesystems').include?("nodev\toverlay\n") - end - - def random_mac - mac = [0x00, 0x16, 0x3e, - SecureRandom.random_number(0x7f), - SecureRandom.random_number(0xff), - SecureRandom.random_number(0xff) - ] - mac.map {|number| number.to_s(16) }.join(':') end end end From d6e0be1f4e3c39ab1912fd2d9bfa6a7cf4eafc24 Mon Sep 17 00:00:00 2001 From: Akshay Karle Date: Wed, 18 Nov 2015 18:59:10 -0300 Subject: [PATCH 3/8] don't fail when bdir option not provided --- lib/lxc/start_ephemeral.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/lxc/start_ephemeral.rb b/lib/lxc/start_ephemeral.rb index cb58ac8..435b077 100644 --- a/lib/lxc/start_ephemeral.rb +++ b/lib/lxc/start_ephemeral.rb @@ -72,7 +72,8 @@ def create_pre_mount(orig, dest, opts) end end - opts[:bdir].each do |host_entry, container_entry| + bind_directories = opts[:bdir].nil? ? [] : opts[:bdir] + bind_directories.each do |host_entry, container_entry| if Dir.exists?(host_entry) src_path = File.absolute_path(host_entry) dst_path = File.join(dest_path, 'rootfs', container_entry) From 42ffa9575806d2ee1333f810383280c2cd2c7494 Mon Sep 17 00:00:00 2001 From: Akshay Karle Date: Wed, 18 Nov 2015 19:04:38 -0300 Subject: [PATCH 4/8] raise a custom exceptions for LXC.start_ephemeral --- lib/lxc/start_ephemeral.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/lxc/start_ephemeral.rb b/lib/lxc/start_ephemeral.rb index 435b077..3823705 100644 --- a/lib/lxc/start_ephemeral.rb +++ b/lib/lxc/start_ephemeral.rb @@ -1,10 +1,12 @@ module LXC + class ContainerError < StandardError; end + class << self def start_ephemeral(original_container_name, target_container_name, opts={}) orig = LXC::Container.new(original_container_name) dest = LXC::Container.new(target_container_name) - raise "#{original_container_name} is not present. Exiting.." unless orig.defined? - raise "#{target_container_name} is already present. Exiting.." if dest.defined? + raise ContainerError.new("#{original_container_name} is not present. Exiting..") unless orig.defined? + raise ContainerError.new("#{target_container_name} is already present. Exiting..") if dest.defined? dest_path = File.join(LXC.global_config_item('lxc.lxcpath'), target_container_name) Dir.mkdir(dest_path, 0770) @@ -29,8 +31,11 @@ def start_ephemeral(original_container_name, target_container_name, opts={}) if !dest.wait(:running, 5) dest.stop dest.destroy if dest.defined? - raise "The container '#{dest.name}' failed to start." + raise ContainerError.new("The container '#{dest.name}' failed to start.") end + + rescue => e + ContainerError.new("Unexpected error when starting container. The error was: #{e}") end private From 8dd0434386af52df1c87d6846347e50641b16b2c Mon Sep 17 00:00:00 2001 From: Akshay Karle Date: Wed, 18 Nov 2015 22:32:58 -0300 Subject: [PATCH 5/8] replace 'if !' with 'unless' --- lib/lxc/start_ephemeral.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lxc/start_ephemeral.rb b/lib/lxc/start_ephemeral.rb index 3823705..e7d66ce 100644 --- a/lib/lxc/start_ephemeral.rb +++ b/lib/lxc/start_ephemeral.rb @@ -28,7 +28,7 @@ def start_ephemeral(original_container_name, target_container_name, opts={}) dest.start(daemonize: opts[:daemonize]) - if !dest.wait(:running, 5) + unless dest.wait(:running, 5) dest.stop dest.destroy if dest.defined? raise ContainerError.new("The container '#{dest.name}' failed to start.") From 10ef1cae8e24e672a8c03085fb592f424886517f Mon Sep 17 00:00:00 2001 From: Akshay Karle Date: Wed, 18 Nov 2015 22:49:54 -0300 Subject: [PATCH 6/8] use enumerator instead of slurping it --- lib/lxc/start_ephemeral.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lxc/start_ephemeral.rb b/lib/lxc/start_ephemeral.rb index e7d66ce..e531236 100644 --- a/lib/lxc/start_ephemeral.rb +++ b/lib/lxc/start_ephemeral.rb @@ -41,7 +41,7 @@ def start_ephemeral(original_container_name, target_container_name, opts={}) private def new_overlay? - @new_overlay ||= File.readlines('/proc/filesystems').include?("nodev\toverlay\n") + @new_overlay ||= File.open('/proc/filesystems').grep(/nodev\s+overlay/).any? end def random_mac From 00d27d6ec481b1b32f417219092c989bb2700cdc Mon Sep 17 00:00:00 2001 From: Akshay Karle Date: Thu, 19 Nov 2015 14:24:26 -0300 Subject: [PATCH 7/8] we need to perform an exact match as we are comparing overlay with overlayfs --- lib/lxc/start_ephemeral.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lxc/start_ephemeral.rb b/lib/lxc/start_ephemeral.rb index e531236..d867d48 100644 --- a/lib/lxc/start_ephemeral.rb +++ b/lib/lxc/start_ephemeral.rb @@ -41,7 +41,7 @@ def start_ephemeral(original_container_name, target_container_name, opts={}) private def new_overlay? - @new_overlay ||= File.open('/proc/filesystems').grep(/nodev\s+overlay/).any? + @new_overlay ||= File.open('/proc/filesystems').grep(/^nodev\s+overlay$/).any? end def random_mac From 30c84c78997446fa82671da48059d1612bfc10de Mon Sep 17 00:00:00 2001 From: Akshay Karle Date: Tue, 19 Jan 2016 15:59:16 -0800 Subject: [PATCH 8/8] make sure to raise the exception --- lib/lxc/start_ephemeral.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lxc/start_ephemeral.rb b/lib/lxc/start_ephemeral.rb index d867d48..f500122 100644 --- a/lib/lxc/start_ephemeral.rb +++ b/lib/lxc/start_ephemeral.rb @@ -35,7 +35,7 @@ def start_ephemeral(original_container_name, target_container_name, opts={}) end rescue => e - ContainerError.new("Unexpected error when starting container. The error was: #{e}") + raise ContainerError.new("Unexpected error when starting container. The error was: #{e}") end private