2023-03-16 15:12:21 +08:00
|
|
|
local dkjson = require "dkjson"
|
|
|
|
|
local posix = require "posix"
|
|
|
|
|
local stdlib = require "posix.stdlib"
|
|
|
|
|
local unistd = require "posix.unistd"
|
|
|
|
|
local libgen = require "posix.libgen"
|
|
|
|
|
|
|
|
|
|
local function is_file_exists(path)
|
|
|
|
|
local f = io.open(path, 'rb')
|
|
|
|
|
if f then
|
|
|
|
|
f:close()
|
|
|
|
|
return true
|
|
|
|
|
else
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function makedirs(path)
|
|
|
|
|
if not is_file_exists(path) then
|
|
|
|
|
makedirs(libgen.dirname(path))
|
|
|
|
|
local path, errmsg = posix.mkdir(path)
|
|
|
|
|
assert(path, errmsg)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function cp(src, dest)
|
|
|
|
|
local stat, exitm, retc = os.execute(
|
|
|
|
|
string.format("cp \"%s\" \"%s\"", src, dest))
|
|
|
|
|
return retc
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function mktestenv()
|
|
|
|
|
local format = string.format
|
|
|
|
|
local init_path = os.getenv("INIT")
|
2023-09-03 17:41:03 +08:00
|
|
|
assert(type(init_path) == "string" and #init_path > 0, "no INIT env var for init program")
|
2023-03-16 15:12:21 +08:00
|
|
|
local path, errmsg = stdlib.mkdtemp("/tmp/luacrun-test-XXXXXX")
|
|
|
|
|
assert(path, errmsg)
|
|
|
|
|
local rootfs_path = string.format("%s/%s", path, "rootfs")
|
|
|
|
|
makedirs(rootfs_path)
|
|
|
|
|
for i, p in ipairs({
|
|
|
|
|
"usr/bin", "etc", "var", "lib", "lib64", "usr/share/zoneinfo/Europe",
|
|
|
|
|
"proc", "sys", "dev"
|
|
|
|
|
}) do makedirs(string.format("%s/%s", rootfs_path, p)) end
|
|
|
|
|
local sbin_path = string.format("%s/%s", rootfs_path, "sbin")
|
|
|
|
|
makedirs(sbin_path)
|
|
|
|
|
|
|
|
|
|
local retc = cp(init_path, format("%s/%s", rootfs_path, "init"))
|
|
|
|
|
assert(retc == 0, "cp init to rootfs return " .. retc)
|
|
|
|
|
local retc = cp(init_path, format("%s/%s", sbin_path, "init"))
|
|
|
|
|
assert(retc == 0, "cp init to sbin return " .. retc)
|
|
|
|
|
|
|
|
|
|
unistd.link("../usr/share/zoneinfo/Europe/Rome",
|
|
|
|
|
format("%s/%s", rootfs_path, "etc/localtime"))
|
|
|
|
|
|
|
|
|
|
local passwd, errmsg = io.open(format("%s/%s", rootfs_path,
|
|
|
|
|
"usr/share/passwd"), "w")
|
|
|
|
|
assert(passwd, errmsg)
|
|
|
|
|
passwd, errmsg = passwd:write("root:x:0:0:root:/root:/bin/bash\n")
|
|
|
|
|
assert(passwd, errmsg)
|
|
|
|
|
passwd:close()
|
|
|
|
|
unistd.link("../usr/share/passwd",
|
|
|
|
|
format("%s/%s", rootfs_path, "etc/passwd"))
|
|
|
|
|
return path
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
insulate("luacrun", function()
|
|
|
|
|
local luacrun = require "luacrun"
|
|
|
|
|
|
|
|
|
|
describe("container_spec", function()
|
|
|
|
|
it("returns a string", function()
|
|
|
|
|
local s = luacrun.container_spec()
|
|
|
|
|
assert.are.equals("string", type(s))
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
it("returns a json object", function()
|
|
|
|
|
local s = luacrun.container_spec()
|
|
|
|
|
local t = dkjson.decode(s)
|
|
|
|
|
assert.are.equals("table", type(t))
|
|
|
|
|
end)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
describe("new_ctx", function()
|
|
|
|
|
it("can create context with only id", function()
|
|
|
|
|
local ctx = luacrun.new_ctx {id = "luacrun-test"}
|
|
|
|
|
assert.are.equals("userdata", type(ctx))
|
|
|
|
|
end)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
describe("new_container_from_string", function()
|
|
|
|
|
it("can create container object from json string", function()
|
|
|
|
|
local spec = dkjson.decode(luacrun.container_spec())
|
|
|
|
|
spec.root.path = "/"
|
|
|
|
|
spec.process.args = {'/bin/echo', "Hello World!"}
|
|
|
|
|
local cont, err = luacrun.new_container_from_string(dkjson.encode(
|
|
|
|
|
spec))
|
|
|
|
|
assert(cont, err)
|
|
|
|
|
end)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
describe("create_container", function()
|
|
|
|
|
it("is same to ctx.create", function()
|
|
|
|
|
local ctx = luacrun.new_ctx {id = "luacrun-test"}
|
|
|
|
|
assert.are.equals(ctx.create, luacrun.create_container)
|
|
|
|
|
end)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
it("can create container and delete container", function()
|
|
|
|
|
local temproot = mktestenv()
|
|
|
|
|
local ctx = luacrun.new_ctx {state_root = temproot, id = "luacrun-test"}
|
|
|
|
|
assert(ctx)
|
|
|
|
|
local spec =
|
|
|
|
|
dkjson.decode(luacrun.container_spec(unistd.geteuid() == 0))
|
|
|
|
|
spec.root = {
|
|
|
|
|
path = string.format("%s/%s", temproot, "rootfs"),
|
|
|
|
|
readonly = true
|
|
|
|
|
}
|
|
|
|
|
spec.process.args = {'/init', "true"}
|
|
|
|
|
spec.process.terminal = false
|
|
|
|
|
spec.process.user = {uid = unistd.geteuid(), gid = unistd.getegid()}
|
|
|
|
|
spec.linux.rootfsPropagation = "rprivate"
|
|
|
|
|
local json_spec = dkjson.encode(spec)
|
|
|
|
|
local cont, err = luacrun.new_container_from_string(json_spec)
|
|
|
|
|
assert(cont, err)
|
|
|
|
|
local stat, err = luacrun.create_container(ctx, cont)
|
|
|
|
|
assert(stat == 0, string.format("%s, %d", err, stat))
|
|
|
|
|
local names = {}
|
|
|
|
|
for i, name in ctx:iter_names() do names[#names + 1] = name end
|
|
|
|
|
assert(#names == 1, string.format("names length is %d", #names))
|
|
|
|
|
local status, err = ctx:status(names[1])
|
|
|
|
|
assert(status, err)
|
|
|
|
|
assert.are.equals("created", status.status)
|
|
|
|
|
local stat, err = ctx:start(names[1])
|
|
|
|
|
assert(stat, err)
|
|
|
|
|
local status, err = ctx:status(names[1])
|
|
|
|
|
assert(status, err)
|
|
|
|
|
assert.are.equals("running", status.status)
|
|
|
|
|
while true do -- wait for container exited
|
|
|
|
|
local status, err = ctx:status(names[1])
|
|
|
|
|
assert(status, err)
|
|
|
|
|
if status.status == "created" or status.status == "stopped" then
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local stat, err = luacrun.delete_container(ctx, names[1])
|
|
|
|
|
assert(stat, err)
|
2023-09-03 17:41:03 +08:00
|
|
|
|
|
|
|
|
-- Make sure no container after deleted
|
|
|
|
|
local names_after_deleted = {}
|
|
|
|
|
for i, name in ctx:iter_names() do names_after_deleted[#names_after_deleted + 1] = name end
|
|
|
|
|
assert(#names_after_deleted == 0, string.format("names length is %d", #names))
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
describe("new_ctx()", function ()
|
|
|
|
|
it("error if the argument has invalid type", function ()
|
|
|
|
|
for i, v in ipairs({1, 1.0, false}) do
|
|
|
|
|
assert.has_error((function ()
|
|
|
|
|
luacrun.new_ctx(v)
|
|
|
|
|
end))
|
|
|
|
|
end
|
|
|
|
|
end)
|
2023-03-16 15:12:21 +08:00
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
describe("Ctx", function()
|
|
|
|
|
it("id() can get id", function()
|
|
|
|
|
local ctx = luacrun.new_ctx {id = "luacrun-test"}
|
|
|
|
|
assert.are.equals("luacrun-test", ctx:id())
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
it("id() can read the id set by set_id()", function()
|
|
|
|
|
local ctx = luacrun.new_ctx {id = "luacrun-test1"}
|
|
|
|
|
assert.are.equals("luacrun-test1", ctx:id())
|
|
|
|
|
ctx:set_id("luacrun-test2")
|
|
|
|
|
assert.are.equals("luacrun-test2", ctx:id())
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
it("systemd_cgroup() can get a boolean", function()
|
|
|
|
|
local ctx = luacrun.new_ctx {id = "luacrun-test"}
|
|
|
|
|
assert.are.equals("boolean", type(ctx:systemd_cgroup()))
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
it("systemd_cgroup() can read the value set by set_system_cgroupd()",
|
|
|
|
|
function()
|
|
|
|
|
local ctx = luacrun.new_ctx {
|
|
|
|
|
id = "luacrun-test",
|
|
|
|
|
systemd_cgroup = false
|
|
|
|
|
}
|
|
|
|
|
assert.is_false(ctx:systemd_cgroup())
|
|
|
|
|
ctx:set_systemd_cgroup(true)
|
|
|
|
|
assert.is_true(ctx:systemd_cgroup())
|
|
|
|
|
end)
|
2023-09-03 17:41:03 +08:00
|
|
|
|
|
|
|
|
describe("iter_names()", function ()
|
|
|
|
|
it("acts as empty iterator if no container", function ()
|
|
|
|
|
local touch = false
|
|
|
|
|
local ctx = luacrun.new_ctx {
|
|
|
|
|
id = "luacrun-test-empty",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i, name in ctx:iter_names() do
|
|
|
|
|
touch = true
|
|
|
|
|
end
|
|
|
|
|
assert.is_false(touch)
|
|
|
|
|
end)
|
|
|
|
|
end)
|
2023-03-16 15:12:21 +08:00
|
|
|
end)
|
|
|
|
|
end)
|