dockerman: merge lisaac's commits (#3308)

* luci-lib-docker: merge lisaac's commits:

* last commit:

df1588052311069b95e3e567621cc9d125bd1b54

* luci-app-dockerman: merge lisaac's commits:

* last commit:

86e354f18b0c95bc168d9266a7c1137ce4a988f0
This commit is contained in:
ZhenYu 2020-02-25 00:17:29 +08:00 committed by GitHub
parent 30d8784a09
commit ef9d5f9ec1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 433 additions and 289 deletions

View File

@ -1,11 +1,18 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-dockerman
PKG_VERSION:=v0.2.2
PKG_VERSION:=v0.3.0
PKG_RELEASE:=beta
PKG_MAINTAINER:=lisaac <https://github.com/lisaac/luci-app-dockerman>
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
PKG_LICENSE:=Apache-2.0
PKG_LICENSE:=AGPL-3.0
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/lisaac/luci-app-dockerman.git
PKG_SOURCE_VERSION:=$(PKG_VERSION)
PKG_SOURCE_SUBDIR:=$(PKG_NAME)
PKG_SOURCE:=$(PKG_SOURCE_SUBDIR)-$(PKG_VERSION).tar.gz
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR)
include $(INCLUDE_DIR)/package.mk
@ -23,6 +30,7 @@ define Package/$(PKG_NAME)/description
endef
define Build/Prepare
tar -xzvf $(DL_DIR)/$(PKG_SOURCE) -C $(BUILD_DIR)
endef
define Build/Compile
@ -34,12 +42,16 @@ rm -fr /tmp/luci-indexcache /tmp/luci-modulecache
endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/lib/lua/luci
cp -pR ./luasrc/* $(1)/usr/lib/lua/luci
$(INSTALL_DIR) $(1)/
cp -pR ./root/* $(1)/
cp -pR $(PKG_BUILD_DIR)/root/* $(1)/
# $(INSTALL_DIR) $(1)/www
# cp -pR $(PKG_BUILD_DIR)/htdoc/* $(1)/www
$(INSTALL_DIR) $(1)/usr/lib/lua/luci
cp -pR $(PKG_BUILD_DIR)/luasrc/* $(1)/usr/lib/lua/luci/
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n
po2lmo ./po/zh-cn/dockerman.po $(1)/usr/lib/lua/luci/i18n/dockerman.zh-cn.lmo
$(foreach po, $(shell find $(PKG_BUILD_DIR)/po/*/*.po),\
po2lmo $(po) $(1)/usr/lib/lua/luci/i18n/dockerman.$(shell echo $(po) | awk -F'/' '{print $$(NF-1)}').lmo;)
#po2lmo $(PKG_BUILD_DIR)/po/zh-cn/dockerman.po $(1)/usr/lib/lua/luci/i18n/dockerman.zh-cn.lmo
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View File

@ -11,20 +11,22 @@ module("luci.controller.dockerman",package.seeall)
function index()
entry({"admin", "services","docker"}, firstchild(), "Docker", 40).dependent = false
entry({"admin","services","docker","overview"},cbi("docker/overview"),_("Overview"),0).leaf=true
entry({"admin","services","docker","overview"},cbi("dockerman/overview"),_("Overview"),0).leaf=true
local socket = luci.model.uci.cursor():get("docker", "local", "socket_path")
local socket = luci.model.uci.cursor():get("dockerman", "local", "socket_path")
if not nixio.fs.access(socket) then return end
if (require "luci.model.docker").new():_ping().code ~= 200 then return end
entry({"admin","services","docker","containers"},form("docker/containers"),_("Containers"),1).leaf=true
entry({"admin","services","docker","images"},form("docker/images"),_("Images"),2).leaf=true
entry({"admin","services","docker","networks"},form("docker/networks"),_("Networks"),3).leaf=true
entry({"admin","services","docker","volumes"},form("docker/volumes"),_("Volumes"),4).leaf=true
entry({"admin","services","docker","containers"},form("dockerman/containers"),_("Containers"),1).leaf=true
entry({"admin","services","docker","images"},form("dockerman/images"),_("Images"),2).leaf=true
entry({"admin","services","docker","networks"},form("dockerman/networks"),_("Networks"),3).leaf=true
entry({"admin","services","docker","volumes"},form("dockerman/volumes"),_("Volumes"),4).leaf=true
entry({"admin","services","docker","events"},call("action_events"),_("Events"),5)
entry({"admin","services","docker","newcontainer"},form("docker/newcontainer")).leaf=true
entry({"admin","services","docker","newnetwork"},form("docker/newnetwork")).leaf=true
entry({"admin","services","docker","container"},form("docker/container")).leaf=true
entry({"admin","services","docker","newcontainer"},form("dockerman/newcontainer")).leaf=true
entry({"admin","services","docker","newnetwork"},form("dockerman/newnetwork")).leaf=true
entry({"admin","services","docker","container"},form("dockerman/container")).leaf=true
entry({"admin","services","docker","container_stats"},call("action_get_container_stats")).leaf=true
entry({"admin","services","docker","container_get_archive"},call("download_archive")).leaf=true
entry({"admin","services","docker","container_put_archive"},call("upload_archive")).leaf=true
entry({"admin","services","docker","confirm"},call("action_confirm")).leaf=true
end
@ -35,7 +37,7 @@ function action_events()
local dk = docker.new()
local query ={}
query["until"] = os.time()
local events = dk:events(nil, query)
local events = dk:events({query = query})
for _, v in ipairs(events.body) do
if v.Type == "container" then
logs = (logs ~= "" and (logs .. "\n") or logs) .. "[" .. os.date("%Y-%m-%d %H:%M:%S", v.time) .."] "..v.Type.. " " .. (v.Action or "null") .. " Container ID:".. (v.Actor.ID or "null") .. " Container Name:" .. (v.Actor.Attributes.name or "null")
@ -45,7 +47,7 @@ function action_events()
logs = (logs ~= "" and (logs .. "\n") or logs) .. "[" .. os.date("%Y-%m-%d %H:%M:%S", v.time) .."] "..v.Type.. " " .. v.Action .. " Image:".. (v.Actor.ID or "null").. " Image Name:" .. (v.Actor.Attributes.name or "null")
end
end
luci.template.render("docker/logs", {self={syslog = logs, title="Docker Events"}})
luci.template.render("dockerman/logs", {self={syslog = logs, title="Docker Events"}})
end
local calculate_cpu_percent = function(d)
@ -94,9 +96,9 @@ end
function action_get_container_stats(container_id)
if container_id then
local dk = docker.new()
local response = dk.containers:inspect(container_id)
local response = dk.containers:inspect({id = container_id})
if response.code == 200 and response.body.State.Running then
response = dk.containers:stats(container_id, {stream=false})
response = dk.containers:stats({id = container_id, query = {stream = false}})
if response.code == 200 then
local container_stats = response.body
local cpu_percent = calculate_cpu_percent(container_stats)
@ -136,7 +138,7 @@ function action_get_container_stats(container_id)
end
function action_confirm()
local status_path=luci.model.uci.cursor():get("docker", "local", "status_path")
local status_path=luci.model.uci.cursor():get("dockerman", "local", "status_path")
local data = nixio.fs.readfile(status_path)
if data then
code = 202
@ -151,3 +153,49 @@ function action_confirm()
luci.http.prepare_content("application/json")
luci.http.write_json({info = data})
end
function download_archive()
local id = luci.http.formvalue("id")
local path = luci.http.formvalue("path")
local dk = docker.new()
local first
local cb = function(res, chunk)
if res.code == 200 then
if not first then
first = true
luci.http.header('Content-Disposition', 'inline; filename="archive.tar"')
luci.http.header('Content-Type', 'application\/x-tar')
end
luci.ltn12.pump.all(chunk, luci.http.write)
else
if not first then
first = true
luci.http.prepare_content("text/plain")
end
luci.ltn12.pump.all(chunk, luci.http.write)
end
end
local res = dk.containers:get_archive({id = id, query = {path = path}}, cb)
end
function upload_archive(container_id)
local path = luci.http.formvalue("upload-path")
local dk = docker.new()
local ltn12 = require "luci.ltn12"
rec_send = function(sinkout)
luci.http.setfilehandler(function (meta, chunk, eof)
if chunk then
ltn12.pump.step(ltn12.source.string(chunk), sinkout)
end
end)
end
local res = dk.containers:put_archive({id = container_id, query = {path = path}, body = rec_send})
local msg = res and res.body and res.body.message or nil
luci.http.status(res.code, msg)
luci.http.prepare_content("application/json")
luci.http.write_json({message = msg})
end

View File

@ -12,7 +12,7 @@ local action = arg[2] or "info"
local images, networks, containers_info
if not container_id then return end
local res = dk.containers:inspect(container_id)
local res = dk.containers:inspect({id = container_id})
if res.code < 300 then container_info = res.body else return end
res = dk.networks:list()
if res.code < 300 then networks = res.body else return end
@ -110,9 +110,9 @@ local start_stop_remove = function(m, cmd)
docker:append_status("Containers: " .. cmd .. " " .. container_id .. "...")
local res
if cmd ~= "upgrade" then
res = dk.containers[cmd](dk, container_id)
res = dk.containers[cmd](dk, {id = container_id})
else
res = dk.containers_upgrade(dk, container_id)
res = dk.containers_upgrade(dk, {id = container_id})
end
if res and res.code >= 300 then
docker:append_status("fail code:" .. res.code.." ".. (res.body.message and res.body.message or res.message))
@ -128,11 +128,11 @@ local start_stop_remove = function(m, cmd)
end
m=SimpleForm("docker", container_info.Name:sub(2), translate("Docker Container") )
m.template = "docker/cbi/xsimpleform"
m.template = "dockerman/cbi/xsimpleform"
m.redirect = luci.dispatcher.build_url("admin/services/docker/containers")
-- m:append(Template("docker/container"))
-- m:append(Template("dockerman/container"))
docker_status = m:section(SimpleSection)
docker_status.template="docker/apply_widget"
docker_status.template = "dockerman/apply_widget"
docker_status.err=nixio.fs.readfile(dk.options.status_path)
-- luci.util.perror(docker_status.err)
if docker_status.err then docker:clear_status() end
@ -141,35 +141,35 @@ if docker_status.err then docker:clear_status() end
action_section = m:section(Table,{{}})
action_section.notitle=true
action_section.rowcolors=false
action_section.template="cbi/nullsection"
action_section.template = "cbi/nullsection"
btnstart=action_section:option(Button, "_start")
btnstart.template="docker/cbi/inlinebutton"
btnstart.template = "dockerman/cbi/inlinebutton"
btnstart.inputtitle=translate("Start")
btnstart.inputstyle = "apply"
btnstart.forcewrite = true
btnrestart=action_section:option(Button, "_restart")
btnrestart.template="docker/cbi/inlinebutton"
btnrestart.template = "dockerman/cbi/inlinebutton"
btnrestart.inputtitle=translate("Restart")
btnrestart.inputstyle = "reload"
btnrestart.forcewrite = true
btnstop=action_section:option(Button, "_stop")
btnstop.template="docker/cbi/inlinebutton"
btnstop.template = "dockerman/cbi/inlinebutton"
btnstop.inputtitle=translate("Stop")
btnstop.inputstyle = "reset"
btnstop.forcewrite = true
btnupgrade=action_section:option(Button, "_upgrade")
btnupgrade.template="docker/cbi/inlinebutton"
btnupgrade.template = "dockerman/cbi/inlinebutton"
btnupgrade.inputtitle=translate("Upgrade")
btnupgrade.inputstyle = "reload"
btnstop.forcewrite = true
btnduplicate=action_section:option(Button, "_duplicate")
btnduplicate.template="docker/cbi/inlinebutton"
btnduplicate.template = "dockerman/cbi/inlinebutton"
btnduplicate.inputtitle=translate("Duplicate")
btnduplicate.inputstyle = "add"
btnstop.forcewrite = true
btnremove=action_section:option(Button, "_remove")
btnremove.template="docker/cbi/inlinebutton"
btnremove.template = "dockerman/cbi/inlinebutton"
btnremove.inputtitle=translate("Remove")
btnremove.inputstyle = "remove"
btnremove.forcewrite = true
@ -194,7 +194,7 @@ btnduplicate.write = function(self, section)
end
tab_section = m:section(SimpleSection)
tab_section.template="docker/container"
tab_section.template = "dockerman/container"
if action == "info" then
m.submit = false
@ -302,7 +302,7 @@ if action == "info" then
end
dv_opts.render = function(self, section, scope)
if table_info[section]._key==translate("Connect Network") then
self.template="cbi/value"
self.template = "cbi/value"
self.keylist = {}
self.vallist = {}
self.placeholder = "10.1.1.254"
@ -324,7 +324,7 @@ if action == "info" then
self.template = "cbi/button"
Button.render(self, section, scope)
else
self.template = "docker/cbi/dummyvalue"
self.template = "dockerman/cbi/dummyvalue"
self.default=""
DummyValue.render(self, section, scope)
end
@ -336,16 +336,16 @@ if action == "info" then
if section == "01name" then
docker:append_status("Containers: rename " .. container_id .. "...")
local new_name = table_info[section]._value
res = dk.containers:rename(container_id,{name=new_name})
res = dk.containers:rename({id = container_id, query = {name=new_name}})
elseif section == "08restart" then
docker:append_status("Containers: update " .. container_id .. "...")
local new_restart = table_info[section]._value
res = dk.containers:update(container_id, nil, {RestartPolicy = {Name = new_restart}})
res = dk.containers:update({id = container_id, body = {RestartPolicy = {Name = new_restart}}})
elseif table_info[section]._key == translate("Network") then
local _,_,leave_network = table_info[section]._value:find("(.-) | .+")
leave_network = leave_network or table_info[section]._value
docker:append_status("Network: disconnect " .. leave_network .. container_id .. "...")
res = dk.networks:disconnect(leave_network, nil, {Container = container_id})
res = dk.networks:disconnect({name = leave_network, body = {Container = container_id}})
elseif section == "15connect" then
local connect_network = table_info[section]._value
local network_opiton
@ -358,7 +358,7 @@ if action == "info" then
} or nil
end
docker:append_status("Network: connect " .. connect_network .. container_id .. "...")
res = dk.networks:connect(connect_network, nil, {Container = container_id, EndpointConfig= network_opiton})
res = dk.networks:connect({name = connect_network, body = {Container = container_id, EndpointConfig= network_opiton}})
end
if res and res.code > 300 then
docker:append_status("fail code:" .. res.code.." ".. (res.body.message and res.body.message or res.message))
@ -370,7 +370,7 @@ if action == "info" then
-- info end
elseif action == "edit" then
editsection= m:section(SimpleSection)
local editsection= m:section(SimpleSection)
d = editsection:option( Value, "cpus", translate("CPUs"), translate("Number of CPUs. Number is a fractional number. 0.000 means no limit."))
d.placeholder = "1.5"
d.rmempty = true
@ -420,7 +420,7 @@ elseif action == "edit" then
}
docker:clear_status()
docker:append_status("Containers: update " .. container_id .. "...")
local res = dk.containers:update(container_id, nil, request_body)
local res = dk.containers:update({id = container_id, body = request_body})
if res and res.code >= 300 then
docker:append_status("fail code:" .. res.code.." ".. (res.body.message and res.body.message or res.message))
else
@ -429,31 +429,37 @@ elseif action == "edit" then
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/container/"..container_id.."/edit"))
end
end
elseif action == "file" then
local filesection= m:section(SimpleSection)
m.submit = false
m.reset = false
filesection.template = "dockerman/container_file"
filesection.container = container_id
elseif action == "logs" then
logsection= m:section(SimpleSection)
local logsection= m:section(SimpleSection)
local logs = ""
local query ={
stdout = 1,
stderr = 1,
tail = 1000
}
local logs = dk.containers:logs(container_id, query)
local logs = dk.containers:logs({id = container_id, query = query})
if logs.code == 200 then
logsection.syslog=logs.body
else
logsection.syslog="Get Logs ERROR\n"..logs.code..": "..logs.body
end
logsection.title=translate("Container Logs")
logsection.template="docker/logs"
logsection.template = "dockerman/logs"
m.submit = false
m.reset = false
m.reset = false
elseif action == "stats" then
local response = dk.containers:top(container_id, {ps_args="-aux"})
local response = dk.containers:top({id = container_id, query = {ps_args="-aux"}})
local container_top
if response.code == 200 then
container_top=response.body
else
response = dk.containers:top(container_id)
response = dk.containers:top({id = container_id})
if response.code == 200 then
container_top=response.body
end
@ -463,7 +469,7 @@ elseif action == "stats" then
container_top=response.body
stat_section = m:section(SimpleSection)
stat_section.container_id = container_id
stat_section.template="docker/stats"
stat_section.template = "dockerman/stats"
table_stats = {cpu={key=translate("CPU Useage"),value='-'},memory={key=translate("Memory Useage"),value='-'}}
stat_section = m:section(Table, table_stats, translate("Stats"))
stat_section:option(DummyValue, "key", translate("Stats")).width="33%"

View File

@ -14,7 +14,7 @@ local res = dk.images:list()
if res.code <300 then images = res.body else return end
res = dk.networks:list()
if res.code <300 then networks = res.body else return end
res = dk.containers:list(nil, {all=true})
res = dk.containers:list({query = {all=true}})
if res.code <300 then containers = res.body else return end
local urlencode = luci.http.protocol and luci.http.protocol.urlencode or luci.util.urlencode
@ -68,12 +68,12 @@ local c_lists = get_containers()
-- list Containers
-- m = Map("docker", translate("Docker"))
m = SimpleForm("docker", translate("Docker"))
m.template = "docker/cbi/xsimpleform"
m.template = "dockerman/cbi/xsimpleform"
m.submit=false
m.reset=false
docker_status = m:section(SimpleSection)
docker_status.template="docker/apply_widget"
docker_status.template = "dockerman/apply_widget"
docker_status.err=nixio.fs.readfile(dk.options.status_path)
-- luci.util.perror(docker_status.err)
if docker_status.err then docker:clear_status() end
@ -91,7 +91,7 @@ container_id = c_table:option(DummyValue, "_id", translate("ID"))
container_id.width="10%"
container_name = c_table:option(DummyValue, "_name", translate("Container Name"))
container_name.width="20%"
container_name.template="docker/cbi/dummyvalue"
container_name.template = "dockerman/cbi/dummyvalue"
container_name.href = function (self, section)
return luci.dispatcher.build_url("admin/services/docker/container/" .. urlencode(container_id:cfgvalue(section)))
end
@ -103,7 +103,7 @@ container_ip.width="15%"
container_ports = c_table:option(DummyValue, "_ports", translate("Ports"))
container_ports.width="10%"
container_image = c_table:option(DummyValue, "_image", translate("Image"))
container_image.template="docker/cbi/dummyvalue"
container_image.template = "dockerman/cbi/dummyvalue"
container_image.width="10%"
-- container_image.href = function (self, section)
-- return luci.dispatcher.build_url("admin/services/docker/image/" .. urlencode(c_lists[section]._image_id))
@ -131,12 +131,11 @@ local start_stop_remove = function(m,cmd)
end
end
if #c_selected >0 then
-- luci.util.perror(dk.options.status_path)
docker:clear_status()
local success = true
for _,cont in ipairs(c_selected) do
docker:append_status("Containers: " .. cmd .. " " .. cont .. "...")
local res = dk.containers[cmd](dk, cont)
local res = dk.containers[cmd](dk, {id = cont})
if res and res.code >= 300 then
success = false
docker:append_status("fail code:" .. res.code.." ".. (res.body.message and res.body.message or res.message).. "<br>")
@ -156,26 +155,26 @@ action_section.template="cbi/nullsection"
btnnew=action_section:option(Button, "_new")
btnnew.inputtitle= translate("New")
btnnew.template="docker/cbi/inlinebutton"
btnnew.template = "dockerman/cbi/inlinebutton"
btnnew.inputstyle = "add"
btnnew.forcewrite = true
btnstart=action_section:option(Button, "_start")
btnstart.template="docker/cbi/inlinebutton"
btnstart.template = "dockerman/cbi/inlinebutton"
btnstart.inputtitle=translate("Start")
btnstart.inputstyle = "apply"
btnstart.forcewrite = true
btnrestart=action_section:option(Button, "_restart")
btnrestart.template="docker/cbi/inlinebutton"
btnrestart.template = "dockerman/cbi/inlinebutton"
btnrestart.inputtitle=translate("Restart")
btnrestart.inputstyle = "reload"
btnrestart.forcewrite = true
btnstop=action_section:option(Button, "_stop")
btnstop.template="docker/cbi/inlinebutton"
btnstop.template = "dockerman/cbi/inlinebutton"
btnstop.inputtitle=translate("Stop")
btnstop.inputstyle = "reset"
btnstop.forcewrite = true
btnremove=action_section:option(Button, "_remove")
btnremove.template="docker/cbi/inlinebutton"
btnremove.template = "dockerman/cbi/inlinebutton"
btnremove.inputtitle=translate("Remove")
btnremove.inputstyle = "remove"
btnremove.forcewrite = true

View File

@ -11,7 +11,7 @@ local dk = docker.new()
local containers, images
local res = dk.images:list()
if res.code <300 then images = res.body else return end
res = dk.containers:list(nil, {all=true})
res = dk.containers:list({query = {all=true}})
if res.code <300 then containers = res.body else return end
function get_images()
@ -45,7 +45,7 @@ local image_list = get_images()
-- m = Map("docker", translate("Docker"))
m = SimpleForm("docker", translate("Docker"))
m.template = "docker/cbi/xsimpleform"
m.template = "dockerman/cbi/xsimpleform"
m.submit=false
m.reset=false
@ -53,14 +53,17 @@ local pull_value={{_image_tag_name="", _registry="index.docker.io"}}
local pull_section = m:section(Table,pull_value, translate("Pull Image"))
pull_section.template="cbi/nullsection"
local tag_name = pull_section:option(Value, "_image_tag_name")
tag_name.template="docker/cbi/inlinevalue"
tag_name.template = "dockerman/cbi/inlinevalue"
tag_name.placeholder="hello-world:latest"
local registry = pull_section:option(Value, "_registry")
registry.template="docker/cbi/inlinevalue"
registry:value("index.docker.io", "DockerHub")
registry.template = "dockerman/cbi/inlinevalue"
registry:value("index.docker.io", "Docker Hub")
registry:value("hub-mirror.c.163.com", "163 Mirror")
registry:value("mirror.ccs.tencentyun.com", "Tencent Mirror")
registry:value("docker.mirrors.ustc.edu.cn", "USTC Mirror")
local action_pull = pull_section:option(Button, "_pull")
action_pull.inputtitle= translate("Pull")
action_pull.template="docker/cbi/inlinebutton"
action_pull.template = "dockerman/cbi/inlinebutton"
action_pull.inputstyle = "add"
tag_name.write = function(self, section,value)
local hastag = value:find(":")
@ -80,19 +83,21 @@ action_pull.write = function(self, section)
if not tmp then
_,_,server = server:find("([%.%w%-%_]+)")
end
local json_stringify = luci.json and luci.json.encode or luci.jsonc.stringify
if tag then
local json_stringify = luci.jsonc and luci.jsonc.stringify
if tag and tag ~= "" then
docker:clear_status()
docker:append_status("Images: " .. "pulling" .. " " .. tag .. "...")
local x_auth = nixio.bin.b64encode(json_stringify({serveraddress= server}))
local res = dk.images:create(nil, {fromImage=tag,_header={["X-Registry-Auth"]=x_auth}})
local res = dk.images:create({query = {fromImage=tag}, header={["X-Registry-Auth"] = x_auth}})
if res and res.code >=300 then
docker:append_status("fail code:" .. res.code.." ".. (res.body.message and res.body.message or res.message).. "<br>")
else
docker:append_status("done<br>")
end
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/images"))
else
docker:append_status("fail code: 400 please input the name of image name!")
end
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/images"))
end
image_table = m:section(Table, image_list, translate("Images"))
@ -126,11 +131,9 @@ local remove_action = function(force)
docker:clear_status()
for _,img in ipairs(image_selected) do
docker:append_status("Images: " .. "remove" .. " " .. img .. "...")
local query_body ={}
if force then
query_body.force = true
end
local msg = dk.images["remove"](dk, img, query_body)
local query
if force then query = {force = true} end
local msg = dk.images:remove({id = img, query = query})
if msg.code ~= 200 then
docker:append_status("fail code:" .. msg.code.." ".. (msg.body.message and msg.body.message or msg.message).. "<br>")
success = false
@ -144,7 +147,7 @@ local remove_action = function(force)
end
docker_status = m:section(SimpleSection)
docker_status.template="docker/apply_widget"
docker_status.template = "dockerman/apply_widget"
docker_status.err=nixio.fs.readfile(dk.options.status_path)
if docker_status.err then docker:clear_status() end
@ -155,7 +158,7 @@ action.template="cbi/nullsection"
btnremove = action:option(Button, "remove")
btnremove.inputtitle= translate("Remove")
btnremove.template="docker/cbi/inlinebutton"
btnremove.template = "dockerman/cbi/inlinebutton"
btnremove.inputstyle = "remove"
btnremove.forcewrite = true
btnremove.write = function(self, section)
@ -164,7 +167,7 @@ end
btnforceremove = action:option(Button, "forceremove")
btnforceremove.inputtitle= translate("Force Remove")
btnforceremove.template="docker/cbi/inlinebutton"
btnforceremove.template = "dockerman/cbi/inlinebutton"
btnforceremove.inputstyle = "remove"
btnforceremove.forcewrite = true
btnforceremove.write = function(self, section)

View File

@ -36,7 +36,7 @@ end
local network_list = get_networks()
-- m = Map("docker", translate("Docker"))
m = SimpleForm("docker", translate("Docker"))
m.template = "docker/cbi/xsimpleform"
m.template = "dockerman/cbi/xsimpleform"
m.submit=false
m.reset=false
@ -44,7 +44,7 @@ network_table = m:section(Table, network_list, translate("Networks"))
network_table.nodescr=true
network_selecter = network_table:option(Flag, "_selected","")
network_selecter.template = "docker/cbi/xfvalue"
network_selecter.template = "dockerman/cbi/xfvalue"
network_id = network_table:option(DummyValue, "_id", translate("ID"))
network_selecter.disabled = 0
network_selecter.enabled = 1
@ -68,7 +68,7 @@ network_selecter.write = function(self, section, value)
end
docker_status = m:section(SimpleSection)
docker_status.template="docker/apply_widget"
docker_status.template = "dockerman/apply_widget"
docker_status.err=nixio.fs.readfile(dk.options.status_path)
if docker_status.err then docker:clear_status() end
@ -78,7 +78,7 @@ action.rowcolors=false
action.template="cbi/nullsection"
btnnew=action:option(Button, "_new")
btnnew.inputtitle= translate("New")
btnnew.template="docker/cbi/inlinebutton"
btnnew.template = "dockerman/cbi/inlinebutton"
btnnew.notitle=true
btnnew.inputstyle = "add"
btnnew.forcewrite = true
@ -87,7 +87,7 @@ btnnew.write = function(self, section)
end
btnremove = action:option(Button, "_remove")
btnremove.inputtitle= translate("Remove")
btnremove.template="docker/cbi/inlinebutton"
btnremove.template = "dockerman/cbi/inlinebutton"
btnremove.inputstyle = "remove"
btnremove.forcewrite = true
btnremove.write = function(self, section)
@ -105,7 +105,7 @@ btnremove.write = function(self, section)
docker:clear_status()
for _,net in ipairs(network_selected) do
docker:append_status("Networks: " .. "remove" .. " " .. net .. "...")
local res = dk.networks["remove"](dk, net)
local res = dk.networks["remove"](dk, {id = net})
if res and res.code >= 300 then
docker:append_status("fail code:" .. res.code.." ".. (res.body.message and res.body.message or res.message).. "<br>")
success = false

View File

@ -13,7 +13,7 @@ local create_body = {}
local images = dk.images:list().body
local networks = dk.networks:list().body
local containers = dk.containers:list(nil, {all=true}).body
local containers = dk.containers:list({query = {all=true}}).body
local is_quot_complete = function(str)
if not str then return true end
@ -158,7 +158,7 @@ if cmd_line and cmd_line:match("^docker.+") then
end
elseif cmd_line and cmd_line:match("^duplicate/[^/]+$") then
local container_id = cmd_line:match("^duplicate/(.+)")
create_body = dk:containers_duplicate_config(container_id)
create_body = dk:containers_duplicate_config({id = container_id})
if not create_body.HostConfig then create_body.HostConfig = {} end
if next(create_body) ~= nil then
default_config.name = nil
@ -204,14 +204,14 @@ elseif cmd_line and cmd_line:match("^duplicate/[^/]+$") then
end
local m = SimpleForm("docker", translate("Docker"))
m.template = "docker/cbi/xsimpleform"
m.template = "dockerman/cbi/xsimpleform"
m.redirect = luci.dispatcher.build_url("admin", "services","docker", "containers")
-- m.reset = false
-- m.submit = false
-- new Container
docker_status = m:section(SimpleSection)
docker_status.template="docker/apply_widget"
docker_status.template = "dockerman/apply_widget"
docker_status.err=nixio.fs.readfile(dk.options.status_path)
if docker_status.err then docker:clear_status() end
@ -221,7 +221,7 @@ s.anonymous = true
local d = s:option(DummyValue,"cmd_line", translate("Resolv CLI"))
d.rawhtml = true
d.template = "docker/resolv_container"
d.template = "dockerman/resolv_container"
d = s:option(Value, "name", translate("Container Name"))
d.rmempty = true
@ -279,14 +279,14 @@ d_ip:depends("network", "nil")
d_ip.default = default_config.ip or nil
d = s:option(DynamicList, "link", translate("Links with other containers"))
d.template = "docker/cbi/xdynlist"
d.template = "dockerman/cbi/xdynlist"
d.placeholder = "container_name:alias"
d.rmempty = true
d:depends("network", "bridge")
d.default = default_config.link or nil
d = s:option(DynamicList, "dns", translate("Set custom DNS servers"))
d.template = "docker/cbi/xdynlist"
d.template = "dockerman/cbi/xdynlist"
d.placeholder = "8.8.8.8"
d.rmempty = true
d.default = default_config.dns or nil
@ -297,19 +297,19 @@ d.rmempty = true
d.default = default_config.user or nil
d = s:option(DynamicList, "env", translate("Environmental Variable(-e)"), translate("Set environment variables to inside the container"))
d.template = "docker/cbi/xdynlist"
d.template = "dockerman/cbi/xdynlist"
d.placeholder = "TZ=Asia/Shanghai"
d.rmempty = true
d.default = default_config.env or nil
d = s:option(DynamicList, "mount", translate("Bind Mount(-v)"), translate("Bind mount a volume"))
d.template = "docker/cbi/xdynlist"
d.template = "dockerman/cbi/xdynlist"
d.placeholder = "/media:/media:slave"
d.rmempty = true
d.default = default_config.mount or nil
local d_ports = s:option(DynamicList, "port", translate("Exposed Ports(-p)"), translate("Publish container's port(s) to the host"))
d_ports.template = "docker/cbi/xdynlist"
d_ports.template = "dockerman/cbi/xdynlist"
d_ports.placeholder = "2200:22/tcp"
d_ports.rmempty = true
d_ports.default = default_config.port or nil
@ -325,20 +325,20 @@ d.disabled = 0
d.enabled = 1
d.default = default_config.advance or 0
d = s:option(Value, "hostname", translate("Host Name"))
d = s:option(Value, "hostname", translate("Host Name"), translate("The hostname to use for the container"))
d.rmempty = true
d.default = default_config.hostname or nil
d:depends("advance", 1)
d = s:option(DynamicList, "device", translate("Device(--device)"), translate("Add host device to the container"))
d.template = "docker/cbi/xdynlist"
d.template = "dockerman/cbi/xdynlist"
d.placeholder = "/dev/sda:/dev/xvdc:rwm"
d.rmempty = true
d:depends("advance", 1)
d.default = default_config.device or nil
d = s:option(DynamicList, "tmpfs", translate("Tmpfs(--tmpfs)"), translate("Mount tmpfs directory"))
d.template = "docker/cbi/xdynlist"
d.template = "dockerman/cbi/xdynlist"
d.placeholder = "/run:rw,noexec,nosuid,size=65536k"
d.rmempty = true
d:depends("advance", 1)
@ -538,10 +538,10 @@ m.handle = function(self, state, data)
end
local pull_image = function(image)
local server = "index.docker.io"
local json_stringify = luci.json and luci.json.encode or luci.jsonc.stringify
local json_stringify = luci.jsonc and luci.jsonc.stringify
docker:append_status("Images: " .. "pulling" .. " " .. image .. "...")
local x_auth = nixio.bin.b64encode(json_stringify({serveraddress= server}))
local res = dk.images:create(nil, {fromImage=image,_header={["X-Registry-Auth"]=x_auth}})
local res = dk.images:create({query = {fromImage=image}, header={["X-Registry-Auth"]=x_auth}})
if res and res.code == 200 then
docker:append_status("done<br>")
else
@ -566,7 +566,7 @@ m.handle = function(self, state, data)
end
docker:append_status("Container: " .. "create" .. " " .. name .. "...")
local res = dk.containers:create(name, nil, create_body)
local res = dk.containers:create({name = name, body = create_body})
if res and res.code == 201 then
docker:clear_status()
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/containers"))

View File

@ -9,11 +9,11 @@ local docker = require "luci.model.docker"
local dk = docker.new()
m = SimpleForm("docker", translate("Docker"))
m.template = "docker/cbi/xsimpleform"
m.template = "dockerman/cbi/xsimpleform"
m.redirect = luci.dispatcher.build_url("admin", "services","docker", "networks")
docker_status = m:section(SimpleSection)
docker_status.template="docker/apply_widget"
docker_status.template = "dockerman/apply_widget"
docker_status.err=nixio.fs.readfile(dk.options.status_path)
if docker_status.err then docker:clear_status() end
@ -60,7 +60,7 @@ d.default = 0
d:depends("dirver", "overlay")
d = s:option(DynamicList, "options", translate("Options"))
d.template = "docker/cbi/xdynlist"
d.template = "dockerman/cbi/xdynlist"
d.rmempty = true
d.placeholder="com.docker.network.driver.mtu=1500"
@ -86,7 +86,7 @@ d.placeholder="10.1.1.0/24"
d.datatype="ip4addr"
d = s:option(DynamicList, "aux_address", translate("Exclude IPs"))
d.template = "docker/cbi/xdynlist"
d.template = "dockerman/cbi/xdynlist"
d.rmempty = true
d.placeholder="my-route=10.1.1.1"
@ -191,7 +191,7 @@ m.handle = function(self, state, data)
end
docker:append_status("Network: " .. "create" .. " " .. create_body.Name .. "...")
local res = dk.networks:create(nil, nil, create_body)
local res = dk.networks:create({body = create_body})
if res and res.code == 201 then
docker:clear_status()
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/networks"))

View File

@ -8,17 +8,17 @@ local docker = require "luci.model.docker"
local uci = require "luci.model.uci"
function byte_format(byte)
local suff = {"B", "KB", "MB", "GB", "TB"}
for i=1, 5 do
if byte > 1024 and i < 5 then
byte = byte / 1024
else
return string.format("%.2f %s", byte, suff[i])
end
end
local suff = {"B", "KB", "MB", "GB", "TB"}
for i=1, 5 do
if byte > 1024 and i < 5 then
byte = byte / 1024
else
return string.format("%.2f %s", byte, suff[i])
end
end
end
local m = Map("docker", translate("Docker"))
local m = Map("dockerman", translate("Docker"))
local docker_info_table = {}
-- docker_info_table['0OperatingSystem'] = {_key=translate("Operating System"),_value='-'}
-- docker_info_table['1Architecture'] = {_key=translate("Architecture"),_value='-'}
@ -41,13 +41,14 @@ s.containers_total = '-'
s.images_total = '-'
s.networks_total = '-'
s.volumes_total = '-'
local socket = luci.model.uci.cursor():get("docker", "local", "socket_path")
local socket = luci.model.uci.cursor():get("dockerman", "local", "socket_path")
if nixio.fs.access(socket) and (require "luci.model.docker").new():_ping().code == 200 then
local dk = docker.new()
-- local containers_list = dk.containers:list(nil, {all=true}).body
-- local images_list = dk.images:list().body
local volumes_list = dk.volumes:list().body.Volumes
local networks_list = dk.networks:list().body
local containers_list = dk.containers:list({query = {all=true}}).body
local images_list = dk.images:list().body
local vol = dk.volumes:list()
local volumes_list = vol and vol.body and vol.body.Volumes or {}
local networks_list = dk.networks:list().body or {}
local docker_info = dk:info()
-- docker_info_table['0OperatingSystem']._value = docker_info.body.OperatingSystem
-- docker_info_table['1Architecture']._value = docker_info.body.Architecture
@ -55,27 +56,27 @@ if nixio.fs.access(socket) and (require "luci.model.docker").new():_ping().code
docker_info_table['3ServerVersion']._value = docker_info.body.ServerVersion
docker_info_table['4ApiVersion']._value = docker_info.headers["Api-Version"]
docker_info_table['5NCPU']._value = tostring(docker_info.body.NCPU)
docker_info_table['6MemTotal']._value = tostring(byte_format(docker_info.body.MemTotal))
docker_info_table['6MemTotal']._value = byte_format(docker_info.body.MemTotal)
docker_info_table['7DockerRootDir']._value = docker_info.body.DockerRootDir
docker_info_table['8IndexServerAddress']._value = docker_info.body.IndexServerAddress
-- s.images_used = 0
-- for i, v in ipairs(images_list) do
-- for ci,cv in ipairs(containers_list) do
-- if v.Id == cv.ImageID then
-- s.images_used = s.images_used + 1
-- break
-- end
-- end
-- end
s.images_used = 0
for i, v in ipairs(images_list) do
for ci,cv in ipairs(containers_list) do
if v.Id == cv.ImageID then
s.images_used = s.images_used + 1
break
end
end
end
s.containers_running = tostring(docker_info.body.ContainersRunning)
-- s.images_used = tostring(s.images_used)
s.images_used = tostring(s.images_used)
s.containers_total = tostring(docker_info.body.Containers)
s.images_total = tostring(docker_info.body.Images)
s.images_total = tostring(#images_list)
s.networks_total = tostring(#networks_list)
s.volumes_total = tostring(#volumes_list)
end
s.template = "docker/overview"
s.template = "dockerman/overview"
s = m:section(NamedSection, "local", "section", translate("Setting"))

View File

@ -11,7 +11,7 @@ local dk = docker.new()
local containers, volumes
local res = dk.volumes:list()
if res.code <300 then volumes = res.body.Volumes else return end
res = dk.containers:list(nil, {all=true})
res = dk.containers:list({query = {all=true}})
if res.code <300 then containers = res.body else return end
function get_volumes()
@ -50,7 +50,7 @@ local volume_list = get_volumes()
-- m = Map("docker", translate("Docker"))
m = SimpleForm("docker", translate("Docker"))
m.template = "docker/cbi/xsimpleform"
m.template = "dockerman/cbi/xsimpleform"
m.submit=false
m.reset=false
@ -72,7 +72,7 @@ volume_selecter.write = function(self, section, value)
end
docker_status = m:section(SimpleSection)
docker_status.template="docker/apply_widget"
docker_status.template = "dockerman/apply_widget"
docker_status.err=nixio.fs.readfile(dk.options.status_path)
if docker_status.err then docker:clear_status() end
@ -82,7 +82,7 @@ action.rowcolors=false
action.template="cbi/nullsection"
btnremove = action:option(Button, "remove")
btnremove.inputtitle= translate("Remove")
btnremove.template="docker/cbi/inlinebutton"
btnremove.template = "dockerman/cbi/inlinebutton"
btnremove.inputstyle = "remove"
btnremove.forcewrite = true
btnremove.write = function(self, section)
@ -101,7 +101,7 @@ btnremove.write = function(self, section)
docker:clear_status()
for _,vol in ipairs(volume_selected) do
docker:append_status("Volumes: " .. "remove" .. " " .. vol .. "...")
local msg = dk.volumes["remove"](dk, vol)
local msg = dk.volumes["remove"](dk, {id = vol})
if msg.code ~= 204 then
docker:append_status("fail code:" .. msg.code.." ".. (msg.body.message and msg.body.message or msg.message).. "<br>")
success = false

View File

@ -13,16 +13,16 @@ local _docker = {}
local update_image = function(self, image_name)
local server = "index.docker.io"
local json_stringify = luci.json and luci.json.encode or luci.jsonc.stringify
local json_stringify = luci.jsonc and luci.jsonc.stringify
_docker:append_status("Images: " .. "pulling" .. " " .. image_name .. "...")
local x_auth = nixio.bin.b64encode(json_stringify({serveraddress= server}))
local res = self.images:create(nil, {fromImage=image_name, _header={["X-Registry-Auth"]=x_auth}})
local res = self.images:create({query = {fromImage=image_name}, header={["X-Registry-Auth"]=x_auth}})
if res and res.code < 300 then
_docker:append_status("done<br>")
else
_docker:append_status("fail code:" .. res.code.." ".. (res.body.message and res.body.message or res.message).. "<br>")
end
new_image_id = self.images:inspect(image_name).body.Id
new_image_id = self.images:inspect({name = image_name}).body.Id
return new_image_id, res
end
@ -126,10 +126,10 @@ local get_config = function(old_config, old_host_config, old_network_setting, im
return create_body, extra_network
end
local upgrade = function(self, container_id)
local upgrade = function(self, request)
_docker:clear_status()
-- get image name, image id, container name, configuration information
local container_info = self.containers:inspect(container_id)
local container_info = self.containers:inspect({id = request.id})
if container_info.code > 300 and type(container_info.body) == "table" then
return container_info
end
@ -148,7 +148,7 @@ local upgrade = function(self, container_id)
end
_docker:append_status("Container: " .. "Stop" .. " " .. container_name .. "...")
res = self.containers:stop(container_name)
res = self.containers:stop({name = container_name})
if res and res.code < 305 then
_docker:append_status("done<br>")
else
@ -156,7 +156,7 @@ local upgrade = function(self, container_id)
end
_docker:append_status("Container: rename" .. " " .. container_name .. " to ".. container_name .. "_old ...")
res = self.containers:rename(container_name,{ name = container_name .. "_old" })
res = self.containers:rename({name = container_name, query = { name = container_name .. "_old" }})
if res and res.code < 300 then
_docker:append_status("done<br>")
else
@ -164,12 +164,12 @@ local upgrade = function(self, container_id)
end
-- handle config
local image_config = self.images:inspect(old_image_id).body.Config
local image_config = self.images:inspect({id = old_image_id}).body.Config
local create_body, extra_network = get_config(old_config, old_host_config, old_network_setting, image_config)
-- create new container
_docker:append_status("Container: Create" .. " " .. container_name .. "...")
res = self.containers:create(container_name, nil, create_body)
res = self.containers:create({name = container_name, body = create_body})
if res and res.code > 300 then return res end
_docker:append_status("done<br>")
@ -180,7 +180,7 @@ local upgrade = function(self, container_id)
if v.Aliases and next(v.Aliases) == nil then v.Aliases =nil end
_docker:append_status("Networks: Connect" .. " " .. container_name .. "...")
res = self.networks:connect(k, nil, {Container = container_name, EndpointConfig = v})
res = self.networks:connect({id = k, body = {Container = container_name, EndpointConfig = v}})
if res.code > 300 then return res end
_docker:append_status("done<br>")
@ -189,32 +189,32 @@ local upgrade = function(self, container_id)
return res
end
local duplicate_config = function (self, container_id)
local container_info = self.containers:inspect(container_id)
local duplicate_config = function (self, request)
local container_info = self.containers:inspect({id = request.id})
if container_info.code > 300 and type(container_info.body) == "table" then return nil end
local old_image_id = container_info.body.Image
local old_config = container_info.body.Config
local old_host_config = container_info.body.HostConfig
local old_network_setting = container_info.body.NetworkSettings.Networks or {}
local image_config = self.images:inspect(old_image_id).body.Config
local image_config = self.images:inspect({id = old_image_id}).body.Config
return get_config(old_config, old_host_config, old_network_setting, image_config)
end
_docker.new = function(option)
local option = option or {}
options = {
socket_path = option.socket_path or uci:get("docker", "local", "socket_path"),
debug = option.debug or uci:get("docker", "local", "debug") == 'true' and true or false,
debug_path = option.debug_path or uci:get("docker", "local", "debug_path")
socket_path = option.socket_path or uci:get("dockerman", "local", "socket_path"),
debug = option.debug or uci:get("dockerman", "local", "debug") == 'true' and true or false,
debug_path = option.debug_path or uci:get("dockerman", "local", "debug_path")
}
local _new = docker.new(options)
_new.options.status_path = uci:get("docker", "local", "status_path")
_new.options.status_path = uci:get("dockerman", "local", "status_path")
_new.containers_upgrade = upgrade
_new.containers_duplicate_config = duplicate_config
return _new
end
_docker.options={}
_docker.options.status_path = uci:get("docker", "local", "status_path")
_docker.options.status_path = uci:get("dockerman", "local", "status_path")
_docker.append_status=function(self,val)
local file_docker_action_status=io.open(self.options.status_path, "a+")

View File

@ -8,7 +8,7 @@
},
path = {
resource = resource,
browser = url("admin/filebrowser")
browser = url("admin/services/filebrowser")
}
}))
%>>

View File

@ -3,6 +3,7 @@
<li id="cbi-tab-container_info"><a id="a-cbi-tab-container_info" href=""><%:Info%></a></li>
<li id="cbi-tab-container_edit"><a id="a-cbi-tab-container_edit" href=""><%:Edit%></a></li>
<li id="cbi-tab-container_stats"><a id="a-cbi-tab-container_stats" href=""><%:Stats%></a></li>
<li id="cbi-tab-container_file"><a id="a-cbi-tab-container_file" href=""><%:File%></a></li>
<li id="cbi-tab-container_logs"><a id="a-cbi-tab-container_logs" href=""><%:Logs%></a></li>
</ul>
@ -12,7 +13,7 @@
let path = p.split(re)
let container_id = path[1].split('/')[0] || path[1]
let action = path[1].split('/')[1] || "info"
let actions=["info","edit","stats","logs"]
let actions=["info","edit","stats","file","logs"]
actions.forEach(function(item) {
document.getElementById("a-cbi-tab-container_" + item).href= path[0]+"/admin/services/docker/container/"+container_id+'/'+item
if (action === item) {

View File

@ -0,0 +1,54 @@
<div id="upload-container" class="cbi-value cbi-value-last">
<label class="cbi-value-title" for="archive"><%:Upload%></label>
<div class="cbi-value-field">
<input type="file" name="upload_archive" accept="application/x-tar" id="upload_archive" />
</div>
<br>
<label class="cbi-value-title" for="path"><%:Path%></label>
<div class="cbi-value-field">
<input type="text" class="cbi-input-text" name="path" placeholder="/home/myfiles" id="path" />
</div>
<br>
<div class="cbi-value-field">
<input type="button"" class="cbi-button cbi-button-action important" id="upload" name="upload" value="<%:Upload%>" />
<input type="button"" class="cbi-button cbi-button-action important" id="download" name="download" value="<%:Download%>" />
</div>
</div>
<script type="text/javascript">
let btnUpload = document.getElementById('upload')
btnUpload.onclick = function (e) {
let uploadArchive = document.getElementById('upload_archive')
let uploadPath = document.getElementById('path').value
if (!uploadArchive.value || !uploadPath) {
alert("<%:Please input the PATH and select the file !%>")
return
}
let fileName = uploadArchive.files[0].name
let formData = new FormData()
formData.append('upload-filename', fileName)
formData.append('upload-path', uploadPath)
formData.append('upload-archive', uploadArchive.files[0])
let xhr = new XMLHttpRequest()
xhr.open("POST", "/cgi-bin/luci/admin/services/docker/container_put_archive/<%=self.container%>", true)
xhr.onload = function() {
if (xhr.status == 200) {
uploadArchive.value = ''
alert("<%:Upload Success%> !")
}
else {
alert("<%:Upload Error%>:" + xhr.statusText)
}
}
xhr.send(formData)
}
let btnDownload = document.getElementById('download')
btnDownload.onclick = function (e) {
let downloadPath = document.getElementById('path').value
if (!downloadPath) {
alert("<%:Please input the PATH !%>")
return
}
window.open("/cgi-bin/luci/admin/services/docker/container_get_archive/?id=<%=self.container%>&path=" + encodeURIComponent(downloadPath))
}
</script>

View File

@ -162,8 +162,8 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
<h4 style="text-align: right; font-size: 1rem"><%:Images%></h4>
<h4 style="text-align: right;">
<%- if self.images_total ~= "-" then -%><a href="/cgi-bin/luci/admin/services/docker/images"><%- end -%>
<span style="font-size: 2rem; color: #2dce89;"><%=self.images_total%></span>
<!-- <span style="font-size: 1rem; color: #8898aa !important;">/<%=self.images_total%></span> -->
<span style="font-size: 2rem; color: #2dce89;"><%=self.images_used%></span>
<span style="font-size: 1rem; color: #8898aa !important;">/<%=self.images_total%></span>
<%- if self.images_total ~= "-" then -%></a><%- end -%>
</h4>
</div>

View File

@ -253,8 +253,8 @@ msgstr "保存docker status文件的位置"
msgid "Enable Debug"
msgstr "启用调试"
msgid "For debug, It shows all docker API actions of luci-app-dockermab in Debug Tempfile Path"
msgstr "用于调试,在调试临时文件路径中显示 luci-app-dockermab 的所有docker API操作"
msgid "For debug, It shows all docker API actions of luci-app-dockerman in Debug Tempfile Path"
msgstr "用于调试,在调试临时文件路径中显示 luci-app-dockerman 的所有 Docker API 操作"
msgid "Debug Tempfile Path"
msgstr "调试临时文件路径"
@ -328,5 +328,26 @@ msgstr "结束时间"
msgid "Command line Error"
msgstr "命令行错误"
msgid "Canceled"
msgstr "已取消"
msgid "Host Name"
msgstr "主机名称"
msgid "The hostname to use for the container"
msgstr "容器使用的主机名"
msgid "File"
msgstr "文件"
msgid "Upload"
msgstr "上传"
msgid "Download"
msgstr "下载"
msgid "Path"
msgstr "路径"
msgid "Upload Error"
msgstr "上传错误"
msgid "Upload Success"
msgstr "上传成功"

View File

@ -1,16 +1,18 @@
#
# Copyright (C) 2019 lisaac <lisaac.cn@gmail.com>
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-lib-docker
PKG_VERSION:=v0.1.1
PKG_VERSION:=v0.3.0
PKG_RELEASE:=beta
PKG_MAINTAINER:=lisaac <https://github.com/lisaac/luci-lib-docker>
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
PKG_LICENSE:=Apache-2.0
PKG_LICENSE:=AGPL-3.0
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/lisaac/luci-lib-docker.git
PKG_SOURCE_VERSION:=$(PKG_VERSION)
PKG_SOURCE_SUBDIR:=$(PKG_NAME)
PKG_SOURCE:=$(PKG_SOURCE_SUBDIR)-$(PKG_VERSION).tar.gz
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR)
include $(INCLUDE_DIR)/package.mk
@ -28,6 +30,7 @@ define Package/$(PKG_NAME)/description
endef
define Build/Prepare
tar -xzvf $(DL_DIR)/$(PKG_SOURCE) -C $(BUILD_DIR)
endef
define Build/Compile
@ -40,7 +43,7 @@ endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/lib/lua/luci
cp -pR ./luasrc/* $(1)/usr/lib/lua/luci
cp -pR $(PKG_BUILD_DIR)/luasrc/* $(1)/usr/lib/lua/luci/
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View File

@ -1,13 +1,13 @@
require "nixio.util"
require "luci.util"
local json = require "luci.json"
local jsonc = require "luci.jsonc"
local nixio = require "nixio"
local ltn12 = require "luci.ltn12"
local fs = require "nixio.fs"
local urlencode = luci.util.urlencode or luci.http and luci.http.protocol and luci.http.protocol.urlencode
local json_stringify = json.encode --luci.json and luci.json.encode or luci.jsonc.stringify
local json_parse = json.decode --luci.json and luci.json.decode or luci.jsonc.parse
local json_stringify = jsonc.stringify
local json_parse = jsonc.parse
local chunksource = function(sock, buffer)
buffer = buffer or ""
@ -58,6 +58,16 @@ local chunksource = function(sock, buffer)
end
end
local chunksink = function (sock)
return function(chunk, err)
if not chunk then
return sock:writeall("0\r\n\r\n")
else
return sock:writeall(("%X\r\n%s\r\n"):format(#chunk, tostring(chunk)))
end
end
end
local docker_stream_filter = function(buffer)
buffer = buffer or ""
if #buffer < 8 then
@ -73,80 +83,44 @@ local docker_stream_filter = function(buffer)
-- return string.sub(buffer, 9, valid_length + 8)
end
local gen_http_req = function(options)
local req
options = options or {}
options.protocol = options.protocol or "HTTP/1.1"
req = (options.method or "GET") .. " " .. options.path .. " " .. options.protocol .. "\r\n"
req = req .. "Host: " .. options.host .. "\r\n"
req = req .. "User-Agent: " .. options.user_agent .. "\r\n"
req = req .. "Connection: close\r\n"
if type(options.header) == "table" then
for k, v in pairs(options.header) do
req = req .. k .. ": " .. v .."\r\n"
end
end
if options.method == "POST" and type(options.conetnt) == "table" then
local conetnt_json = json_stringify(options.conetnt)
req = req .. "Content-Type: application/json\r\n"
req = req .. "Content-Length: " .. #conetnt_json .. "\r\n"
req = req .. "\r\n" .. conetnt_json
elseif options.method == "PUT" and options.conetnt then
req = req .. "Content-Type: application/x-tar\r\n"
req = req .. "Content-Length: " .. #options.conetnt .. "\r\n"
req = req .. "\r\n" .. options.conetnt
else
req = req .. "\r\n"
end
if options.debug then io.popen("echo '".. req .. "' >> " .. options.debug_path) end
return req
end
local send_http_socket = function(socket_path, req)
local send_http_socket = function(socket_path, req_header, req_body, callback)
local docker_socket = nixio.socket("unix", "stream")
if docker_socket:connect(socket_path) ~= true then
return {
headers={
code=497,
message="bad socket path",
protocol="HTTP/1.1"
},
body={
message="can\'t connect to unix socket"
}
headers = {code=497, message="bad socket path", protocol="HTTP/1.1"},
body = {message="can\'t connect to unix socket"}
}
end
if docker_socket:send(req) == 0 then
if docker_socket:send(req_header) == 0 then
return {
headers={
code=498,
message="bad socket path",
protocol="HTTP/1.1"
},
body={
message="can\'t send data to unix socket"
}
headers={code=498,message="bad socket path", protocol="HTTP/1.1"},
body={message="can\'t send data to unix socket"}
}
end
-- local data, err_code, err_msg, data_f = docker_socket:readall()
-- docker_socket:close()
if req_body and type(req_body) == "function" and req_header and req_header:match("chunked") then
-- chunked send
req_body(chunksink(docker_socket))
elseif req_body and type(req_body) == "function" then
-- normal send by req_body function
req_body(docker_socket)
elseif req_body and type(req_body) == "table" then
-- json
docker_socket:send(json_stringify(req_body))
elseif req_body then
docker_socket:send(req_body)
end
local linesrc = docker_socket:linesource()
-- read socket using source http://w3.impa.br/~diego/software/luasocket/ltn12.html
--http://lua-users.org/wiki/FiltersSourcesAndSinks
-- handle response header
local line = linesrc()
if not line then
docker_socket:close()
return {
headers={
code=499,
message="bad socket path",
protocol="HTTP/1.1"
},
body={
message="no data receive from socket"
}
headers = {code=499, message="bad socket path", protocol="HTTP/1.1"},
body = {message="no data receive from socket"}
}
end
local response = {code = 0, headers = {}, body = {}}
@ -172,64 +146,85 @@ local send_http_socket = function(socket_path, req)
-- handle response body
local body_buffer = linesrc(true)
response.body = {}
if response.headers["Transfer-Encoding"] == "chunked" then
local source = chunksource(docker_socket, body_buffer)
code = ltn12.pump.all(source, (ltn12.sink.table(response.body))) and response.code or 555
response.code = code
if type(callback) ~= "function" then
if response.headers["Transfer-Encoding"] == "chunked" then
local source = chunksource(docker_socket, body_buffer)
code = ltn12.pump.all(source, (ltn12.sink.table(response.body))) and response.code or 555
response.code = code
else
local body_source = ltn12.source.cat(ltn12.source.string(body_buffer), docker_socket:blocksource())
code = ltn12.pump.all(body_source, (ltn12.sink.table(response.body))) and response.code or 555
response.code = code
end
else
local body_source = ltn12.source.cat(ltn12.source.string(body_buffer), docker_socket:blocksource())
code = ltn12.pump.all(body_source, (ltn12.sink.table(response.body))) and response.code or 555
response.code = code
if response.headers["Transfer-Encoding"] == "chunked" then
local source = chunksource(docker_socket, body_buffer)
callback(response, source)
else
local body_source = ltn12.source.cat(ltn12.source.string(body_buffer), docker_socket:blocksource())
callback(response, body_source)
end
end
docker_socket:close()
return response
end
local send_http_require = function(options, method, api_group, api_action, name_or_id, request_qurey, request_body)
local qurey
local req_options = setmetatable({}, {__index = options})
local gen_header = function(options, http_method, api_group, api_action, name_or_id, request)
local header, query, path
options = options or {}
options.protocol = options.protocol or "HTTP/1.1"
name_or_id = name_or_id ~= "" and name_or_id or nil
-- for docker action status
-- if options.status_enabled then
-- fs.writefile(options.status_path, api_group or "" .. " " .. api_action or "" .. " " .. name_or_id or "")
-- end
-- request_qurey = request_qurey or {}
-- request_body = request_body or {}
if name_or_id == "" then
name_or_id = nil
end
req_options.method = method
req_options.path = (api_group and ("/" .. api_group) or "") .. (name_or_id and ("/" .. name_or_id) or "") .. (api_action and ("/" .. api_action) or "")
req_options.header = {}
if type(request_qurey) == "table" then
for k, v in pairs(request_qurey) do
if request and type(request.query) == "table" then
local k, v
for k, v in pairs(request.query) do
if type(v) == "table" then
if k ~= "_header" then
qurey = (qurey and qurey .. "&" or "?") .. k .. "=" .. urlencode(json_stringify(v))
else
-- for http header
for k1, v1 in pairs(v) do
req_options.header[k1] = v1
end
end
query = (query and query .. "&" or "?") .. k .. "=" .. urlencode(json_stringify(v))
elseif type(v) == "boolean" then
qurey = (qurey and qurey .. "&" or "?") .. k .. "=" .. (v and "true" or "false")
query = (query and query .. "&" or "?") .. k .. "=" .. (v and "true" or "false")
elseif type(v) == "number" or type(v) == "string" then
qurey = (qurey and qurey .. "&" or "?") .. k .. "=" .. v
query = (query and query .. "&" or "?") .. k .. "=" .. v
end
end
end
req_options.path = req_options.path .. (qurey or "")
-- if type(request_body) == "table" then
req_options.conetnt = request_body
-- end
local response = send_http_socket(req_options.socket_path, gen_http_req(req_options))
-- for docker action status
-- if options.status_enabled then
-- fs.remove(options.status_path)
-- end
return response
path = (api_group and ("/" .. api_group) or "") .. (name_or_id and ("/" .. name_or_id) or "") .. (api_action and ("/" .. api_action) or "") .. (query or "")
header = (http_method or "GET") .. " " .. path .. " " .. options.protocol .. "\r\n"
header = header .. "Host: " .. options.host .. "\r\n"
header = header .. "User-Agent: " .. options.user_agent .. "\r\n"
header = header .. "Connection: close\r\n"
if request and type(request.header) == "table" then
local k, v
for k, v in pairs(request.header) do
header = header .. k .. ": " .. v .. "\r\n"
end
end
-- when requst_body is function, we need to custom header using custom header
if request and request.body and type(request.body) == "function" then
if not header:match("Content-Length:") then
header = header .. "Transfer-Encoding: chunked\r\n"
end
elseif http_method == "POST" and request and request.body and type(request.body) == "table" then
local conetnt_json = json_stringify(request.body)
header = header .. "Content-Type: application/json\r\n"
header = header .. "Content-Length: " .. #conetnt_json .. "\r\n"
elseif request and request.body and type(request.body) == "string" then
header = header .. "Content-Length: " .. #request.body .. "\r\n"
end
header = header .. "\r\n"
if options.debug then io.popen("echo '".. header .. "' >> " .. options.debug_path) end
return header
end
local call_docker = function(options, http_method, api_group, api_action, name_or_id, request, callback)
local req_options = setmetatable({}, {__index = options})
local req_header = gen_header(req_options, http_method, api_group, api_action, name_or_id, request)
local req_body = request and request.body or nil
return send_http_socket(req_options.socket_path, req_header, req_body, callback)
end
local gen_api = function(_table, http_method, api_group, api_action)
@ -244,28 +239,29 @@ local gen_api = function(_table, http_method, api_group, api_action)
_api_action = "json"
end
local fp = function(self, name_or_id, request_qurey, request_body)
local fp = function(self, request, callback)
local name_or_id = request and (request.name or request.id or request.name_or_id) or nil
if api_action == "list" then
if (name_or_id ~= "" and name_or_id ~= nil) then
if api_group == "images" then
name_or_id = nil
else
request_qurey = request_qurey or {}
request_qurey.filters = request_qurey.filters or {}
request_qurey.filters.name = request_qurey.filters.name or {}
request_qurey.filters.name[#request_qurey.filters.name + 1] = name_or_id
request.query = request and request.query or {}
request.query.filters = request.query.filters or {}
request.query.filters.name = request.query.filters.name or {}
request.query.filters.name[#request.query.filters.name + 1] = name_or_id
name_or_id = nil
end
end
elseif api_action == "create" then
if (name_or_id ~= "" and name_or_id ~= nil) then
request_qurey = request_qurey or {}
request_qurey.name = request_qurey.name or name_or_id
request.query = request and request.query or {}
request.query.name = request.query.name or name_or_id
name_or_id = nil
end
elseif api_action == "logs" then
local body_buffer = ""
local response = send_http_require(self.options, http_method, api_group, _api_action, name_or_id, request_qurey, request_body)
local response = call_docker(self.options, http_method, api_group, _api_action, name_or_id, request, callback)
if response.code >= 200 and response.code < 300 then
for i, v in ipairs(response.body) do
body_buffer = body_buffer .. docker_stream_filter(response.body[i])
@ -274,7 +270,7 @@ local gen_api = function(_table, http_method, api_group, api_action)
end
return response
end
local response = send_http_require(self.options, http_method, api_group, _api_action, name_or_id, request_qurey, request_body)
local response = call_docker(self.options, http_method, api_group, _api_action, name_or_id, request, callback)
if response.headers and response.headers["Content-Type"] == "application/json" then
if #response.body == 1 then
response.body = json_parse(response.body[1])