luci-app-dockerman: sync with upstream source

This commit is contained in:
CN_SZTL 2020-03-22 23:57:46 +08:00
parent c0fd0410cc
commit 28992ba615
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
34 changed files with 574 additions and 489 deletions

View File

@ -1,13 +1,76 @@
include $(TOPDIR)/rules.mk
LUCI_TITLE:=Docker Manager interface for LuCI
LUCI_DEPENDS:=+luci-lib-docker +docker-ce +e2fsprogs +fdisk +ttyd
PKG_NAME:=luci-app-dockerman
PKG_VERSION:=v0.4.7
PKG_RELEASE:=leanmod
PKG_VERSION:=v0.5.4
PKG_RELEASE:=beta
PKG_MAINTAINER:=lisaac <https://github.com/lisaac/luci-app-dockerman>
PKG_LICENSE:=AGPL-3.0
include $(TOPDIR)/feeds/luci/luci.mk
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/lisaac/luci-app-dockerman.git
PKG_SOURCE_VERSION:=$(PKG_VERSION)
# call BuildPackage - OpenWrt buildroot signature
PKG_SOURCE_SUBDIR:=$(PKG_NAME)
PKG_SOURCE:=$(PKG_SOURCE_SUBDIR)-$(PKG_SOURCE_VERSION).tar.gz
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR)
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)/config
config PACKAGE_$(PKG_NAME)_INCLUDE_docker_ce
bool "Include Docker-CE"
default n
config PACKAGE_$(PKG_NAME)_INCLUDE_ttyd
bool "Include ttyd"
default y
endef
define Package/$(PKG_NAME)
SECTION:=luci
CATEGORY:=LuCI
SUBMENU:=3. Applications
TITLE:=Docker Manager interface for LuCI
PKGARCH:=all
DEPENDS:=+luci-lib-docker \
+PACKAGE_$(PKG_NAME)_INCLUDE_docker_ce:docker-ce \
+PACKAGE_$(PKG_NAME)_INCLUDE_ttyd:ttyd
endef
define Package/$(PKG_NAME)/description
Docker Manager interface for LuCI
endef
define Build/Prepare
tar -xzvf $(DL_DIR)/$(PKG_SOURCE) -C $(BUILD_DIR)
endef
define Build/Compile
endef
define Package/$(PKG_NAME)/postinst
#!/bin/sh
if [ -z "$${IPKG_INSTROOT}" ]; then
( . /etc/uci-defaults/luci-app-dockerman ) && rm -f /etc/uci-defaults/luci-app-dockerman
fi
exit 0
endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/etc/config
cp -pR $(PKG_BUILD_DIR)/root/etc/config/* $(1)/etc/config/
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) $(PKG_BUILD_DIR)/root/etc/init.d/* $(1)/etc/init.d/
$(INSTALL_DIR) $(1)/etc/uci-defaults
$(INSTALL_BIN) $(PKG_BUILD_DIR)/root/etc/uci-defaults/* $(1)/etc/uci-defaults/
# $(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
$$(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_Hans/dockerman.po $(1)/usr/lib/lua/luci/i18n/dockerman.zh-cn.lmo
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View File

@ -0,0 +1 @@
ttyd

View File

@ -10,8 +10,8 @@ module("luci.controller.dockerman",package.seeall)
function index()
entry({"admin", "services","docker"}, firstchild(), "Docker", 40).dependent = false
entry({"admin","services","docker","overview"},cbi("dockerman/overview"),_("Overview"),0).leaf=true
entry({"admin", "docker"}, firstchild(), "Docker", 40).dependent = false
entry({"admin","docker","overview"},cbi("dockerman/overview"),_("Overview"),0).leaf=true
local remote = luci.model.uci.cursor():get("dockerman", "local", "remote_endpoint")
if remote == nil then
@ -24,24 +24,24 @@ function index()
end
if (require "luci.model.docker").new():_ping().code ~= 200 then return end
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("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","images_save"},call("save_images")).leaf=true
entry({"admin","services","docker","images_load"},call("load_images")).leaf=true
entry({"admin","services","docker","images_import"},call("import_images")).leaf=true
entry({"admin","services","docker","images_get_tags"},call("get_image_tags")).leaf=true
entry({"admin","services","docker","images_tag"},call("tag_image")).leaf=true
entry({"admin","services","docker","images_untag"},call("untag_image")).leaf=true
entry({"admin","services","docker","confirm"},call("action_confirm")).leaf=true
entry({"admin","docker","containers"},form("dockerman/containers"),_("Containers"),1).leaf=true
entry({"admin","docker","images"},form("dockerman/images"),_("Images"),2).leaf=true
entry({"admin","docker","networks"},form("dockerman/networks"),_("Networks"),3).leaf=true
entry({"admin","docker","volumes"},form("dockerman/volumes"),_("Volumes"),4).leaf=true
entry({"admin","docker","events"},call("action_events"),_("Events"),5)
entry({"admin","docker","newcontainer"},form("dockerman/newcontainer")).leaf=true
entry({"admin","docker","newnetwork"},form("dockerman/newnetwork")).leaf=true
entry({"admin","docker","container"},form("dockerman/container")).leaf=true
entry({"admin","docker","container_stats"},call("action_get_container_stats")).leaf=true
entry({"admin","docker","container_get_archive"},call("download_archive")).leaf=true
entry({"admin","docker","container_put_archive"},call("upload_archive")).leaf=true
entry({"admin","docker","images_save"},call("save_images")).leaf=true
entry({"admin","docker","images_load"},call("load_images")).leaf=true
entry({"admin","docker","images_import"},call("import_images")).leaf=true
entry({"admin","docker","images_get_tags"},call("get_image_tags")).leaf=true
entry({"admin","docker","images_tag"},call("tag_image")).leaf=true
entry({"admin","docker","images_untag"},call("untag_image")).leaf=true
entry({"admin","docker","confirm"},call("action_confirm")).leaf=true
end
function action_events()
@ -272,39 +272,39 @@ function load_images()
luci.http.write_json({message = msg})
end
-- function import_images()
-- local src = luci.http.formvalue("src")
-- local itag = luci.http.formvalue("tag")
-- local dk = docker.new()
-- local ltn12 = require "luci.ltn12"
-- local 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
-- docker:write_status("Images: importing".. " ".. itag .."...\n")
-- local repo = itag and itag:match("^([^:]+)")
-- local tag = itag and itag:match("^[^:]-:([^:]+)")
-- local res = dk.images:create({query = {fromSrc = src or "-", repo = repo or nil, tag = tag or nil }, body = not src and rec_send or nil}, docker.import_image_show_status_cb)
-- local msg = res and res.body and ( res.body.message )or nil
-- if not msg and #res.body == 0 then
-- -- res.body = {"status":"sha256:d5304b58e2d8cc0a2fd640c05cec1bd4d1229a604ac0dd2909f13b2b47a29285"}
-- msg = res.body.status or res.body.error
-- elseif not msg and #res.body >= 1 then
-- -- res.body = [...{"status":"sha256:d5304b58e2d8cc0a2fd640c05cec1bd4d1229a604ac0dd2909f13b2b47a29285"}]
-- msg = res.body[#res.body].status or res.body[#res.body].error
-- end
-- if res.code == 200 and msg and msg:match("sha256:") then
-- docker:clear_status()
-- else
-- docker:append_status("code:" .. res.code.." ".. msg)
-- end
-- luci.http.status(res.code, msg)
-- luci.http.prepare_content("application/json")
-- luci.http.write_json({message = msg})
-- end
function import_images()
local src = luci.http.formvalue("src")
local itag = luci.http.formvalue("tag")
local dk = docker.new()
local ltn12 = require "luci.ltn12"
local 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
docker:write_status("Images: importing".. " ".. itag .."...\n")
local repo = itag and itag:match("^([^:]+)")
local tag = itag and itag:match("^[^:]-:([^:]+)")
local res = dk.images:create({query = {fromSrc = src or "-", repo = repo or nil, tag = tag or nil }, body = not src and rec_send or nil}, docker.import_image_show_status_cb)
local msg = res and res.body and ( res.body.message )or nil
if not msg and #res.body == 0 then
-- res.body = {"status":"sha256:d5304b58e2d8cc0a2fd640c05cec1bd4d1229a604ac0dd2909f13b2b47a29285"}
msg = res.body.status or res.body.error
elseif not msg and #res.body >= 1 then
-- res.body = [...{"status":"sha256:d5304b58e2d8cc0a2fd640c05cec1bd4d1229a604ac0dd2909f13b2b47a29285"}]
msg = res.body[#res.body].status or res.body[#res.body].error
end
if res.code == 200 and msg and msg:match("sha256:") then
docker:clear_status()
else
docker:append_status("code:" .. res.code.." ".. msg)
end
luci.http.status(res.code, msg)
luci.http.prepare_content("application/json")
luci.http.write_json({message = msg})
end
function get_image_tags(image_id)
if not image_id then

View File

@ -4,7 +4,6 @@ Copyright 2019 lisaac <https://github.com/lisaac/luci-app-dockerman>
]]--
require "luci.util"
local uci = luci.model.uci.cursor()
local docker = require "luci.model.docker"
local dk = docker.new()
container_id = arg[1]
@ -146,20 +145,19 @@ local start_stop_remove = function(m, cmd)
end
if res and res.code >= 300 then
docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message))
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/container/"..container_id))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/container/"..container_id))
else
docker:clear_status()
if cmd ~= "remove" and cmd ~= "upgrade" then
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/container/"..container_id))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/container/"..container_id))
else
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/containers"))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/containers"))
end
end
end
m=SimpleForm("docker", container_info.Name:sub(2), translate("Docker Container") )
m.template = "dockerman/cbi/xsimpleform"
m.redirect = luci.dispatcher.build_url("admin/services/docker/containers")
m.redirect = luci.dispatcher.build_url("admin/docker/containers")
-- m:append(Template("dockerman/container"))
docker_status = m:section(SimpleSection)
docker_status.template = "dockerman/apply_widget"
@ -220,7 +218,7 @@ btnstop.write = function(self, section)
start_stop_remove(m,"stop")
end
btnduplicate.write = function(self, section)
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/newcontainer/duplicate/"..container_id))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/newcontainer/duplicate/"..container_id))
end
tab_section = m:section(SimpleSection)
@ -305,7 +303,9 @@ if action == "info" then
self:reset_values()
self.size = nil
for k,v in pairs(list_networks) do
self:value(k,v)
if k ~= "host" then
self:value(k,v)
end
end
self.default=table_info[section]._value
ListValue.render(self, section, scope)
@ -364,7 +364,6 @@ if action == "info" then
end
end
btn_update.write = function(self, section, value)
-- luci.util.perror(section)
local res
docker:clear_status()
if section == "01name" then
@ -384,7 +383,6 @@ if action == "info" then
local connect_network = table_info[section]._value
local network_opiton
if connect_network ~= "none" and connect_network ~= "bridge" and connect_network ~= "host" then
-- luci.util.perror(table_info[section]._opts)
network_opiton = table_info[section]._opts ~= "" and {
IPAMConfig={
IPv4Address=table_info[section]._opts
@ -399,7 +397,7 @@ if action == "info" then
else
docker:clear_status()
end
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/container/"..container_id.."/info"))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/container/"..container_id.."/info"))
end
-- info end
@ -459,7 +457,7 @@ elseif action == "edit" then
else
docker:clear_status()
end
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/container/"..container_id.."/edit"))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/container/"..container_id.."/edit"))
end
end
elseif action == "file" then
@ -491,7 +489,7 @@ elseif action == "console" then
m.reset = false
local cmd_docker = luci.util.exec("which docker"):match("^.+docker") or nil
local cmd_ttyd = luci.util.exec("which ttyd"):match("^.+ttyd") or nil
if cmd_docker and cmd_ttyd then
if cmd_docker and cmd_ttyd and container_info.State.Status == "running" then
local consolesection= m:section(SimpleSection)
local cmd = "/bin/sh"
local uid
@ -521,9 +519,11 @@ elseif action == "console" then
local cmd_ttyd = luci.util.exec("which ttyd"):match("^.+ttyd") or nil
if not cmd_docker or not cmd_ttyd or cmd_docker:match("^%s+$") or cmd_ttyd:match("^%s+$") then return end
local kill_ttyd = 'netstat -lnpt | grep ":7682[ \t].*ttyd$" | awk \'{print $NF}\' | awk -F\'/\' \'{print "kill -9 " $1}\' | sh > /dev/null'
luci.util.exec(kill_ttyd)
local hosts
local uci = (require "luci.model.uci").cursor()
local remote = uci:get("dockerman", "local", "remote_endpoint")
local socket_path = (remote == "false") and uci:get("dockerman", "local", "socket_path") or nil
local socket_path = (remote == "false" or not remote) and uci:get("dockerman", "local", "socket_path") or nil
local host = (remote == "true") and uci:get("dockerman", "local", "remote_host") or nil
local port = (remote == "true") and uci:get("dockerman", "local", "remote_port") or nil
if remote and host and port then
@ -533,9 +533,8 @@ elseif action == "console" then
else
return
end
local start_cmd = cmd_ttyd .. ' -d 2 -p 7682 '.. cmd_docker .. ' -H "'.. hosts ..'" exec -it ' .. (uid and uid ~= "" and (" -u ".. uid .. ' ') or "").. container_id .. ' ' .. cmd .. ' &'
local res = luci.util.exec(start_cmd)
local start_cmd = cmd_ttyd .. ' -d 2 --once -p 7682 '.. cmd_docker .. ' -H "'.. hosts ..'" exec -it ' .. (uid and uid ~= "" and (" -u ".. uid .. ' ') or "").. container_id .. ' ' .. cmd .. ' &'
os.execute(start_cmd)
local console = consolesection:option(DummyValue, "console")
console.container_id = container_id
console.template = "dockerman/container_console"
@ -561,7 +560,6 @@ elseif action == "stats" then
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%"
stat_section:option(DummyValue, "value")
top_section= m:section(Table, container_top.Processes, translate("TOP"))
for i, v in ipairs(container_top.Titles) do
@ -572,4 +570,4 @@ m.submit = false
m.reset = false
end
return m
return m

View File

@ -28,7 +28,7 @@ function get_containers()
data[index]["_selected"] = 0
data[index]["_id"] = v.Id:sub(1,12)
data[index]["name"] = v.Names[1]:sub(2)
data[index]["_name"] = '<a href='..luci.dispatcher.build_url("admin/services/docker/container/"..v.Id)..' class="dockerman_link" title="'..translate("Container detail")..'">'.. v.Names[1]:sub(2).."</a>"
data[index]["_name"] = '<a href='..luci.dispatcher.build_url("admin/docker/container/"..v.Id)..' class="dockerman_link" title="'..translate("Container detail")..'">'.. v.Names[1]:sub(2).."</a>"
data[index]["_status"] = v.Status
if v.Status:find("^Up") then
data[index]["_status"] = '<font color="green">'.. data[index]["_status"] .. "</font>"
@ -69,7 +69,6 @@ local c_lists = get_containers()
-- list Containers
-- m = Map("docker", translate("Docker"))
m = SimpleForm("docker", translate("Docker"))
m.template = "dockerman/cbi/xsimpleform"
m.submit=false
m.reset=false
@ -132,7 +131,7 @@ local start_stop_remove = function(m,cmd)
end
end
if success then docker:clear_status() end
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/containers"))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/containers"))
end
end
@ -167,7 +166,7 @@ btnremove.inputtitle=translate("Remove")
btnremove.inputstyle = "remove"
btnremove.forcewrite = true
btnnew.write = function(self, section)
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/newcontainer"))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/newcontainer"))
end
btnstart.write = function(self, section)
start_stop_remove(m,"start")
@ -182,4 +181,4 @@ btnstop.write = function(self, section)
start_stop_remove(m,"stop")
end
return m
return m

View File

@ -38,7 +38,7 @@ function get_images()
for ci,cv in ipairs(containers) do
if v.Id == cv.ImageID then
data[index]["_containers"] = (data[index]["_containers"] and (data[index]["_containers"] .. " | ") or "")..
'<a href='..luci.dispatcher.build_url("admin/services/docker/container/"..cv.Id)..' class="dockerman_link" title="'..translate("Container detail")..'">'.. cv.Names[1]:sub(2).."</a>"
'<a href='..luci.dispatcher.build_url("admin/docker/container/"..cv.Id)..' class="dockerman_link" title="'..translate("Container detail")..'">'.. cv.Names[1]:sub(2).."</a>"
end
end
data[index]["_size"] = string.format("%.2f", tostring(v.Size/1024/1024)).."MB"
@ -51,7 +51,6 @@ local image_list = get_images()
-- m = Map("docker", translate("Docker"))
m = SimpleForm("docker", translate("Docker"))
m.template = "dockerman/cbi/xsimpleform"
m.submit=false
m.reset=false
@ -60,7 +59,7 @@ local pull_section = m:section(SimpleSection, translate("Pull Image"))
pull_section.template="cbi/nullsection"
local tag_name = pull_section:option(Value, "_image_tag_name")
tag_name.template = "dockerman/cbi/inlinevalue"
tag_name.placeholder="hello-world:latest"
tag_name.placeholder="lisaac/luci:latest"
local action_pull = pull_section:option(Button, "_pull")
action_pull.inputtitle= translate("Pull")
action_pull.template = "dockerman/cbi/inlinebutton"
@ -88,12 +87,12 @@ action_pull.write = function(self, section)
else
docker:append_status("code: 400 please input the name of image name!")
end
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/images"))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/images"))
end
-- local import_section = m:section(SimpleSection, translate("Import Images"))
-- local im = import_section:option(DummyValue, "_image_import")
-- im.template = "dockerman/images_import"
local import_section = m:section(SimpleSection, translate("Import Images"))
local im = import_section:option(DummyValue, "_image_import")
im.template = "dockerman/images_import"
local image_table = m:section(Table, image_list, translate("Images"))
@ -138,7 +137,7 @@ local remove_action = function(force)
end
end
if success then docker:clear_status() end
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/images"))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/images"))
end
end
@ -221,4 +220,4 @@ local btnload = action:option(Button, "load")
btnload.inputtitle= translate("Load")
btnload.template = "dockerman/images_load"
btnload.inputstyle = "add"
return m
return m

View File

@ -36,7 +36,6 @@ end
local network_list = get_networks()
-- m = Map("docker", translate("Docker"))
m = SimpleForm("docker", translate("Docker"))
m.template = "dockerman/cbi/xsimpleform"
m.submit=false
m.reset=false
@ -84,7 +83,7 @@ btnnew.notitle=true
btnnew.inputstyle = "add"
btnnew.forcewrite = true
btnnew.write = function(self, section)
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/newnetwork"))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/newnetwork"))
end
btnremove = action:option(Button, "_remove")
btnremove.inputtitle= translate("Remove")
@ -93,18 +92,22 @@ btnremove.inputstyle = "remove"
btnremove.forcewrite = true
btnremove.write = function(self, section)
local network_selected = {}
local network_name_selected = {}
local network_driver_selected = {}
-- 遍历table中sectionid
local network_table_sids = network_table:cfgsections()
for _, network_table_sid in ipairs(network_table_sids) do
-- 得到选中项的名字
if network_list[network_table_sid]._selected == 1 then
network_selected[#network_selected+1] = network_list[network_table_sid]._id --network_name:cfgvalue(network_table_sid)
network_name_selected[#network_name_selected+1] = network_list[network_table_sid]._name
network_driver_selected[#network_driver_selected+1] = network_list[network_table_sid]._driver
end
end
if next(network_selected) ~= nil then
local success = true
docker:clear_status()
for _,net in ipairs(network_selected) do
for ii, net in ipairs(network_selected) do
docker:append_status("Networks: " .. "remove" .. " " .. net .. "...")
local res = dk.networks["remove"](dk, {id = net})
if res and res.code >= 300 then
@ -112,13 +115,16 @@ btnremove.write = function(self, section)
success = false
else
docker:append_status("done\n")
if network_driver_selected[ii] == "macvlan" then
docker.remove_macvlan_interface(network_name_selected[ii])
end
end
end
if success then
docker:clear_status()
end
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/networks"))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/networks"))
end
end
return m
return m

View File

@ -32,7 +32,7 @@ end
local resolve_cli = function(cmd_line)
local config = {advance = 1}
local key_no_val = '|t|d|i|tty|rm|read-only|interactive|init|help|detach|privileged|'
local key_no_val = '|t|d|i|tty|rm|read_only|interactive|init|help|detach|privileged|'
local key_with_val = '|sysctl|add_host|a|attach|blkio_weight_device|cap_add|cap_drop|device|device_cgroup_rule|device_read_bps|device_read_iops|device_write_bps|device_write_iops|dns|dns_option|dns_search|e|env|env_file|expose|group_add|l|label|label_file|link|link_local_ip|log_driver|log_opt|network_alias|p|publish|security_opt|storage_opt|tmpfs|v|volume|volumes_from|blkio_weight|cgroup_parent|cidfile|cpu_period|cpu_quota|cpu_rt_period|cpu_rt_runtime|c|cpu_shares|cpus|cpuset_cpus|cpuset_mems|detach_keys|disable_content_trust|domainname|entrypoint|gpus|health_cmd|health_interval|health_retries|health_start_period|health_timeout|h|hostname|ip|ip6|ipc|isolation|kernel_memory|log_driver|mac_address|m|memory|memory_reservation|memory_swap|memory_swappiness|mount|name|network|no_healthcheck|oom_kill_disable|oom_score_adj|pid|pids_limit|P|publish_all|restart|runtime|shm_size|sig_proxy|stop_signal|stop_timeout|ulimit|u|user|userns|uts|volume_driver|w|workdir|'
local key_abb = {net='network',a='attach',c='cpu-shares',d='detach',e='env',h='hostname',i='interactive',l='label',m='memory',p='publish',P='publish_all',t='tty',u='user',v='volume',w='workdir'}
local key_with_list = '|sysctl|add_host|a|attach|blkio_weight_device|cap_add|cap_drop|device|device_cgroup_rule|device_read_bps|device_read_iops|device_write_bps|device_write_iops|dns|dns_option|dns_search|e|env|env_file|expose|group_add|l|label|label_file|link|link_local_ip|log_driver|log_opt|network_alias|p|publish|security_opt|storage_opt|tmpfs|v|volume|volumes_from|'
@ -55,7 +55,7 @@ local resolve_cli = function(cmd_line)
key = w:match("^%-([%lP%-]+)")
if key then
-- for -dit
if key:match("i") or key:match("t") then
if key:match("i") or key:match("t") or key:match("d") then
if key:match("i") then
config[key_abb["i"]] = true
key:gsub("i", "")
@ -75,14 +75,13 @@ local resolve_cli = function(cmd_line)
end
if key then
key = key:gsub("-","_")
key = key_abb[key] or key
if key_no_val:match("|"..key.."|") then
key = key_abb[key] or key
config[key] = true
val = nil
key = nil
elseif key_with_val:match("|"..key.."|") then
key = key_abb[key] or key
if key == "cap_add" then config.privileged = true end
-- if key == "cap_add" then config.privileged = true end
else
key = nil
val = nil
@ -94,13 +93,39 @@ local resolve_cli = function(cmd_line)
is_cmd = true
end
elseif (key or _key) and not is_cmd then
val = w
if key == "mount" then
-- we need resolve mount options here
-- type=bind,source=/source,target=/app
local _type = w:match("^type=([^,]+),") or "bind"
local source = (_type ~= "tmpfs") and (w:match("source=([^,]+),") or w:match("src=([^,]+),")) or ""
local target = w:match(",target=([^,]+)") or w:match(",dst=([^,]+)") or w:match(",destination=([^,]+)") or ""
local ro = w:match(",readonly") and "ro" or nil
if source and target then
if _type ~= "tmpfs" then
-- bind or volume
local bind_propagation = (_type == "bind") and w:match(",bind%-propagation=([^,]+)") or nil
val = source..":"..target .. ((ro or bind_propagation) and (":" .. (ro and ro or "") .. (((ro and bind_propagation) and "," or "") .. (bind_propagation and bind_propagation or ""))or ""))
else
-- tmpfs
local tmpfs_mode = w:match(",tmpfs%-mode=([^,]+)") or nil
local tmpfs_size = w:match(",tmpfs%-size=([^,]+)") or nil
key = "tmpfs"
val = target .. ((tmpfs_mode or tmpfs_size) and (":" .. (tmpfs_mode and ("mode=" .. tmpfs_mode) or "") .. ((tmpfs_mode and tmpfs_size) and "," or "") .. (tmpfs_size and ("size=".. tmpfs_size) or "")) or "")
if not config[key] then config[key] = {} end
table.insert( config[key], val )
key = nil
val = nil
end
end
else
val = w
end
elseif is_cmd then
config["command"] = (config["command"] and (config["command"] .. " " )or "") .. w
end
if (key or _key) and val then
key = _key or key
if key_with_list:match(key) then
if key_with_list:match("|"..key.."|") then
if not config[key] then config[key] = {} end
if _key then
config[key][#config[key]] = config[key][#config[key]] .. " " .. w
@ -154,6 +179,7 @@ elseif cmd_line and cmd_line:match("^duplicate/[^/]+$") then
default_config.env = create_body.Env
default_config.dns = create_body.HostConfig.Dns
default_config.volume = create_body.HostConfig.Binds
default_config.cap_add = create_body.HostConfig.CapAdd
if create_body.HostConfig.Sysctls and type(create_body.HostConfig.Sysctls) == "table" then
default_config.sysctl = {}
@ -193,8 +219,7 @@ elseif cmd_line and cmd_line:match("^duplicate/[^/]+$") then
end
local m = SimpleForm("docker", translate("Docker"))
m.template = "dockerman/cbi/xsimpleform"
m.redirect = luci.dispatcher.build_url("admin", "services","docker", "containers")
m.redirect = luci.dispatcher.build_url("admin", "docker", "containers")
-- m.reset = false
-- m.submit = false
-- new Container
@ -269,14 +294,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 = "dockerman/cbi/xdynlist"
d.cfgvalue = function (self, section) return default_config.link or nil end
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 = "dockerman/cbi/xdynlist"
d.cfgvalue = function (self, section) return default_config.dns or nil end
d.placeholder = "8.8.8.8"
d.rmempty = true
d.default = default_config.dns or nil
@ -287,19 +312,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 = "dockerman/cbi/xdynlist"
d.cfgvalue = function (self, section) return default_config.env or nil end
d.placeholder = "TZ=Asia/Shanghai"
d.rmempty = true
d.default = default_config.env or nil
d = s:option(DynamicList, "volume", translate("Bind Mount(-v)"), translate("Bind mount a volume"))
d.template = "dockerman/cbi/xdynlist"
d.cfgvalue = function (self, section) return default_config.volume or nil end
d.placeholder = "/media:/media:slave"
d.rmempty = true
d.default = default_config.volume or nil
local d_publish = s:option(DynamicList, "publish", translate("Exposed Ports(-p)"), translate("Publish container's port(s) to the host"))
d_publish.template = "dockerman/cbi/xdynlist"
d_publish.cfgvalue = function (self, section) return default_config.publish or nil end
d_publish.placeholder = "2200:22/tcp"
d_publish.rmempty = true
d_publish.default = default_config.publish or nil
@ -321,26 +346,33 @@ 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 = "dockerman/cbi/xdynlist"
d.cfgvalue = function (self, section) return default_config.device or nil end
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 = "dockerman/cbi/xdynlist"
d.cfgvalue = function (self, section) return default_config.tmpfs or nil end
d.placeholder = "/run:rw,noexec,nosuid,size=65536k"
d.rmempty = true
d:depends("advance", 1)
d.default = default_config.tmpfs or nil
d = s:option(DynamicList, "sysctl", translate("Sysctl(--sysctl)"), translate("Sysctls (kernel parameters) options"))
d.template = "dockerman/cbi/xdynlist"
d.cfgvalue = function (self, section) return default_config.sysctl or nil end
d.placeholder = "net.ipv4.ip_forward=1"
d.rmempty = true
d:depends("advance", 1)
d.default = default_config.sysctl or nil
d = s:option(DynamicList, "cap_add", translate("CAP-ADD(--cap-add)"), translate("A list of kernel capabilities to add to the container"))
d.cfgvalue = function (self, section) return default_config.cap_add or nil end
d.placeholder = "NET_ADMIN"
d.rmempty = true
d:depends("advance", 1)
d.default = default_config.cap_add or nil
d = s: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
@ -368,7 +400,6 @@ d:depends("advance", 1)
d.datatype="uinteger"
d.default = default_config.blkio_weight or nil
for _, v in ipairs (networks) do
if v.Name then
local parent = v.Options and v.Options.parent or nil
@ -403,6 +434,7 @@ m.handle = function(self, state, data)
local restart = data.restart
local env = data.env
local dns = data.dns
local cap_add = data.cap_add
local sysctl = {}
tmp = data.sysctl
if type(tmp) == "table" then
@ -499,16 +531,16 @@ m.handle = function(self, state, data)
create_body.Tty = tty and true or false
create_body.OpenStdin = interactive and true or false
create_body.User = user
create_body.Cmd = (#command ~= 0) and command or nil
create_body.Cmd = command
create_body.Env = env
create_body.Image = image
create_body.ExposedPorts = (next(exposedports) ~= nil) and exposedports or nil
create_body.ExposedPorts = exposedports
create_body.HostConfig = create_body.HostConfig or {}
create_body.HostConfig.Dns = dns
create_body.HostConfig.Binds = (#volume ~= 0) and volume or nil
create_body.HostConfig.Binds = volume
create_body.HostConfig.RestartPolicy = { Name = restart, MaximumRetryCount = 0 }
create_body.HostConfig.Privileged = privileged and true or false
create_body.HostConfig.PortBindings = (next(portbindings) ~= nil) and portbindings or nil
create_body.HostConfig.PortBindings = portbindings
create_body.HostConfig.Memory = tonumber(memory)
create_body.HostConfig.CpuShares = tonumber(cpu_shares)
create_body.HostConfig.NanoCPUs = tonumber(cpus) * 10 ^ 9
@ -537,11 +569,12 @@ m.handle = function(self, state, data)
-- no ip + no duplicate config
create_body.NetworkingConfig = nil
end
create_body["HostConfig"]["Tmpfs"] = (next(tmpfs) ~= nil) and tmpfs or nil
create_body["HostConfig"]["Devices"] = (next(device) ~= nil) and device or nil
create_body["HostConfig"]["Sysctls"] = (next(sysctl) ~= nil) and sysctl or nil
create_body["HostConfig"]["Tmpfs"] = tmpfs
create_body["HostConfig"]["Devices"] = device
create_body["HostConfig"]["Sysctls"] = sysctl
create_body["HostConfig"]["CapAdd"] = cap_add
if network == "bridge" and next(link) ~= nil then
if network == "bridge" then
create_body["HostConfig"]["Links"] = link
end
local pull_image = function(image)
@ -551,8 +584,9 @@ m.handle = function(self, state, data)
if res and res.code == 200 and (res.body[#res.body] and not res.body[#res.body].error and res.body[#res.body].status and (res.body[#res.body].status == "Status: Downloaded newer image for ".. image or res.body[#res.body].status == "Status: Image is up to date for ".. image)) then
docker:append_status("done\n")
else
res.code = (res.code == 200) and 500 or res.code
docker:append_status("code:" .. res.code.." ".. (res.body[#res.body] and res.body[#res.body].error or (res.body.message or res.message)).. "\n")
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/newcontainer"))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/newcontainer"))
end
end
docker:clear_status()
@ -571,15 +605,16 @@ m.handle = function(self, state, data)
end
end
create_body = docker.clear_empty_tables(create_body)
docker:append_status("Container: " .. "create" .. " " .. name .. "...")
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"))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/containers"))
else
docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message))
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/newcontainer"))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/newcontainer"))
end
end
return m
return m

View File

@ -4,13 +4,11 @@ Copyright 2019 lisaac <https://github.com/lisaac/luci-app-dockerman>
]]--
require "luci.util"
local uci = luci.model.uci.cursor()
local docker = require "luci.model.docker"
local dk = docker.new()
m = SimpleForm("docker", translate("Docker"))
m.template = "dockerman/cbi/xsimpleform"
m.redirect = luci.dispatcher.build_url("admin", "services","docker", "networks")
m.redirect = luci.dispatcher.build_url("admin", "docker", "networks")
docker_status = m:section(SimpleSection)
docker_status.template = "dockerman/apply_widget"
@ -35,7 +33,12 @@ d:value("overlay", "overlay")
d = s:option(Value, "parent", translate("Parent Interface"))
d.rmempty = true
d:depends("dirver", "macvlan")
d.placeholder="eth0"
local interfaces = luci.sys and luci.sys.net and luci.sys.net.devices() or {}
for _, v in ipairs(interfaces) do
d:value(v, v)
end
d.default="br-lan"
d.placeholder="br-lan"
d = s:option(Value, "macvlan_mode", translate("Macvlan Mode"))
d.rmempty = true
@ -61,16 +64,24 @@ d.default = 0
d:depends("dirver", "overlay")
d = s:option(DynamicList, "options", translate("Options"))
d.template = "dockerman/cbi/xdynlist"
d.rmempty = true
d.placeholder="com.docker.network.driver.mtu=1500"
d = s:option(Flag, "internal", translate("Internal"), translate("Restrict external access to the network"))
d.rmempty = true
d:depends("dirver", "overlay")
d.disabled = 0
d.enabled = 1
d.default = 0
if nixio.fs.access("/etc/config/network") and nixio.fs.access("/etc/config/firewall")then
d = s:option(Flag, "op_macvlan", translate("Create macvlan interface"), translate("Auto create macvlan interface in Openwrt"))
d:depends("dirver", "macvlan")
d.disabled = 0
d.enabled = 1
d.default = 1
end
d = s:option(Value, "subnet", translate("Subnet"))
d.rmempty = true
d.placeholder="10.1.0.0/16"
@ -87,7 +98,6 @@ d.placeholder="10.1.1.0/24"
d.datatype="ip4addr"
d = s:option(DynamicList, "aux_address", translate("Exclude IPs"))
d.template = "dockerman/cbi/xdynlist"
d.rmempty = true
d.placeholder="my-route=10.1.1.1"
@ -152,14 +162,11 @@ m.handle = function(self, state, data)
Subnet = subnet,
Gateway = gateway,
IPRange = ip_range,
-- AuxAddress = aux_address
-- AuxiliaryAddresses = aux_address
AuxAddress = aux_address,
AuxiliaryAddresses = aux_address
}
}
end
if next(aux_address)~=nil then
create_body["IPAM"]["Config"]["AuxiliaryAddresses"] = aux_address
end
if driver == "macvlan" then
create_body["Options"] = {
macvlan_mode = data.macvlan_mode,
@ -191,16 +198,20 @@ m.handle = function(self, state, data)
end
end
create_body = docker.clear_empty_tables(create_body)
docker:write_status("Network: " .. "create" .. " " .. create_body.Name .. "...")
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"))
if driver == "macvlan" and data.op_macvlan ~= 0 then
docker.create_macvlan_interface(data.name, data.parent, data.gateway, data.ip_range)
end
luci.http.redirect(luci.dispatcher.build_url("admin/docker/networks"))
else
docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message).. "\n")
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/newnetwork"))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/newnetwork"))
end
end
end
return m
return m

View File

@ -18,7 +18,7 @@ function byte_format(byte)
end
end
local map_dockerman = Map("dockerman", translate("Docker"))
local map_dockerman = Map("dockerman", translate("Docker"), translate("DockerMan is a Simple Docker manager client for LuCI, If you have any issue please visit:") .. " ".. [[<a href="https://github.com/lisaac/luci-app-dockerman" target="_blank">]] ..translate("Github") .. [[</a>]])
local docker_info_table = {}
-- docker_info_table['0OperatingSystem'] = {_key=translate("Operating System"),_value='-'}
-- docker_info_table['1Architecture'] = {_key=translate("Architecture"),_value='-'}
@ -41,10 +41,11 @@ s.containers_total = '-'
s.images_total = '-'
s.networks_total = '-'
s.volumes_total = '-'
local containers_list
-- local socket = luci.model.uci.cursor():get("dockerman", "local", "socket_path")
if (require "luci.model.docker").new():_ping().code == 200 then
local dk = docker.new()
local containers_list = dk.containers:list({query = {all=true}}).body
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 {}
@ -81,69 +82,71 @@ if (require "luci.model.docker").new():_ping().code == 200 then
end
s.template = "dockerman/overview"
--tabs
tab_section = map_dockerman:section(SimpleSection)
tab_section.tabs = {
dockerman = translate("DockerMan"),
}
tab_section.default_tab = "dockerman"
tab_section.template="dockerman/overview_tab"
local section_dockerman = map_dockerman:section(NamedSection, "local", "section", translate("Setting"))
section_dockerman:tab("dockerman", translate("DockerMan"), translate("DockerMan Settings"))
section_dockerman:tab("ac", translate("Access Control"), translate("Access Control for the bridge network"))
section_dockerman:tab("daemon", translate("Docker Daemon"), translate("Docker Daemon Settings"))
local section_dockerman = map_dockerman:section(NamedSection, "local", "section")
section_dockerman.config = "dockerman"
section_dockerman.template = "dockerman/cbi/namedsection"
local socket_path = section_dockerman:option(Value, "socket_path", translate("Docker Socket Path"))
local socket_path = section_dockerman:taboption("dockerman", Value, "socket_path", translate("Docker Socket Path"))
socket_path.default = "/var/run/docker.sock"
socket_path.placeholder = "/var/run/docker.sock"
socket_path.rmempty = false
local remote_endpoint = section_dockerman:option(Flag, "remote_endpoint", translate("Remote Endpoint"), translate("Dockerman connect to remote endpoint"))
local remote_endpoint = section_dockerman:taboption("dockerman", Flag, "remote_endpoint", translate("Remote Endpoint"), translate("Dockerman connect to remote endpoint"))
remote_endpoint.rmempty = false
remote_endpoint.enabled = "true"
remote_endpoint.disabled = "false"
local remote_host = section_dockerman:option(Value, "remote_host", translate("Remote Host"))
local remote_host = section_dockerman:taboption("dockerman", Value, "remote_host", translate("Remote Host"))
remote_host.placeholder = "10.1.1.2"
-- remote_host:depends("remote_endpoint", "true")
local remote_port = section_dockerman:option(Value, "remote_port", translate("Remote Port"))
local remote_port = section_dockerman:taboption("dockerman", Value, "remote_port", translate("Remote Port"))
remote_port.placeholder = "2375"
remote_port.default = "2375"
-- remote_port:depends("remote_endpoint", "true")
local status_path = section_dockerman:option(Value, "status_path", translate("Action Status Tempfile Path"), translate("Where you want to save the docker status file"))
local debug = section_dockerman:option(Flag, "debug", translate("Enable Debug"), translate("For debug, It shows all docker API actions of luci-app-dockerman in Debug Tempfile Path"))
debug.enabled="true"
debug.disabled="false"
local debug_path = section_dockerman:option(Value, "debug_path", translate("Debug Tempfile Path"), translate("Where you want to save the debug tempfile"))
-- local status_path = section_dockerman:taboption("dockerman", Value, "status_path", translate("Action Status Tempfile Path"), translate("Where you want to save the docker status file"))
-- local debug = section_dockerman:taboption("dockerman", Flag, "debug", translate("Enable Debug"), translate("For debug, It shows all docker API actions of luci-app-dockerman in Debug Tempfile Path"))
-- debug.enabled="true"
-- debug.disabled="false"
-- local debug_path = section_dockerman:taboption("dockerman", Value, "debug_path", translate("Debug Tempfile Path"), translate("Where you want to save the debug tempfile"))
local map_dockerd
if nixio.fs.access("/etc/config/dockerd") and nixio.fs.access("/usr/bin/dockerd") then
-- map_dockerman:chain("dockerd")
tab_section.tabs.docker_daemon = translate("Docker Daemon")
tab_section.default_tab = "docker_daemon"
map_dockerd = Map("dockerd","")
local section_dockerd = map_dockerd:section(NamedSection, "local", "section")
section_dockerd.config = "docker_daemon"
section_dockerd.template = "dockerman/cbi/namedsection"
local dockerd_enable = section_dockerd:option(Flag, "ea", translate("Enable"))
if nixio.fs.access("/usr/bin/dockerd") then
local allowed_interface = section_dockerman:taboption("ac", DynamicList, "ac_allowed_interface", translate("Allowed access interfaces"), translate("Which interface(s) can access containers under the bridge network, fill-in Interface Name"))
local interfaces = luci.sys and luci.sys.net and luci.sys.net.devices() or {}
for i, v in ipairs(interfaces) do
allowed_interface:value(v, v)
end
local allowed_container = section_dockerman:taboption("ac", DynamicList, "ac_allowed_container", translate("Containers allowed to be accessed"), translate("Which container(s) can be accessed, even from interfaces that are not allowed, fill-in Container Id or Name"))
allowed_container.placeholder = "container name_or_id"
if containers_list then
for i, v in ipairs(containers_list) do
if v.State == "running" and v.NetworkSettings and v.NetworkSettings.Networks and v.NetworkSettings.Networks.bridge and v.NetworkSettings.Networks.bridge.IPAddress then
allowed_container:value(v.Id:sub(1,12), v.Names[1]:sub(2) .. " | " .. v.NetworkSettings.Networks.bridge.IPAddress)
end
end
end
local dockerd_enable = section_dockerman:taboption("daemon", Flag, "daemon_ea", translate("Enable"))
dockerd_enable.enabled = "true"
dockerd_enable.rmempty = true
local data_root = section_dockerd:option(Value, "data_root", translate("Docker Root Dir"))
local data_root = section_dockerman:taboption("daemon", Value, "daemon_data_root", translate("Docker Root Dir"))
data_root.placeholder = "/opt/docker/"
local hosts = section_dockerd:option(DynamicList, "hosts", translate("Server Host"), translate('Daemon unix socket (unix:///var/run/docker.sock) or TCP Remote Hosts (tcp://0.0.0.0:2375), default: unix:///var/run/docker.sock'))
hosts.placeholder = "unix:///var/run/docker.sock | tcp://0.0.0.0:2375"
hosts.rmempty = true
local registry_mirrors = section_dockerd:option(DynamicList, "registry_mirrors", translate("Registry Mirrors"))
local registry_mirrors = section_dockerman:taboption("daemon", DynamicList, "daemon_registry_mirrors", translate("Registry Mirrors"))
registry_mirrors.placeholder = "https://hub-mirror.c.163.com"
local wan_enable = section_dockerd:option(Flag, "en_wan", translate("Enable WAN access"), translate("Enable WAN access container mapped ports"))
wan_enable.enabled = "true"
wan_enable.rmempty = true
local log_level = section_dockerd:option(ListValue, "log_level", translate("Log Level"), translate('Set the logging level'))
registry_mirrors:value("https://hub-mirror.c.163.com", "https://hub-mirror.c.163.com")
local log_level = section_dockerman:taboption("daemon", ListValue, "daemon_log_level", translate("Log Level"), translate('Set the logging level'))
log_level:value("debug", "debug")
log_level:value("info", "info")
log_level:value("warn", "warn")
log_level:value("error", "error")
log_level:value("fatal", "fatal")
local hosts = section_dockerman:taboption("daemon", DynamicList, "daemon_hosts", translate("Server Host"), translate('Daemon unix socket (unix:///var/run/docker.sock) or TCP Remote Hosts (tcp://0.0.0.0:2375), default: unix:///var/run/docker.sock'))
hosts.placeholder = "unix:///var/run/docker.sock"
hosts:value("unix:///var/run/docker.sock", "unix:///var/run/docker.sock")
hosts:value("tcp://0.0.0.0:2375", "tcp://0.0.0.0:2375")
hosts.rmempty = true
end
return map_dockerman, map_dockerd
return map_dockerman

View File

@ -28,7 +28,7 @@ function get_volumes()
for vi, vv in ipairs(cv.Mounts) do
if v.Name == vv.Name then
data[index]["_containers"] = (data[index]["_containers"] and (data[index]["_containers"] .. " | ") or "")..
'<a href='..luci.dispatcher.build_url("admin/services/docker/container/"..cv.Id)..' class="dockerman_link" title="'..translate("Container detail")..'">'.. cv.Names[1]:sub(2)..'</a>'
'<a href='..luci.dispatcher.build_url("admin/docker/container/"..cv.Id)..' class="dockerman_link" title="'..translate("Container detail")..'">'.. cv.Names[1]:sub(2)..'</a>'
end
end
end
@ -50,7 +50,6 @@ local volume_list = get_volumes()
-- m = Map("docker", translate("Docker"))
m = SimpleForm("docker", translate("Docker"))
m.template = "dockerman/cbi/xsimpleform"
m.submit=false
m.reset=false
@ -111,7 +110,7 @@ btnremove.write = function(self, section)
end
end
if success then docker:clear_status() end
luci.http.redirect(luci.dispatcher.build_url("admin/services/docker/volumes"))
luci.http.redirect(luci.dispatcher.build_url("admin/docker/volumes"))
end
end
return m
return m

View File

@ -79,14 +79,14 @@ local map_subtract = function(t1, t2)
return next(res) ~= nil and res or nil
end
local function clear_empty_tables( t )
_docker.clear_empty_tables = function ( t )
local k, v
if next(t) == nil then
t = nil
else
for k, v in pairs(t) do
if type(v) == 'table' then
t[k] = clear_empty_tables(v)
t[k] = _docker.clear_empty_tables(v)
end
end
end
@ -150,8 +150,8 @@ local get_config = function(container_config, image_config)
local create_body = config
create_body["HostConfig"] = host_config
create_body["NetworkingConfig"] = {EndpointsConfig = network_setting}
create_body = clear_empty_tables(create_body) or {}
extra_network = clear_empty_tables(extra_network) or {}
create_body = _docker.clear_empty_tables(create_body) or {}
extra_network = _docker.clear_empty_tables(extra_network) or {}
return create_body, extra_network
end
@ -308,23 +308,23 @@ end
--{"status":"Downloading from https://downloads.openwrt.org/releases/19.07.0/targets/x86/64/openwrt-19.07.0-x86-64-generic-rootfs.tar.gz"}
--{"status":"Importing","progressDetail":{"current":1572391,"total":3821714},"progress":"[====================\u003e ] 1.572MB/3.822MB"}
--{"status":"sha256:d5304b58e2d8cc0a2fd640c05cec1bd4d1229a604ac0dd2909f13b2b47a29285"}
-- _docker.import_image_show_status_cb = function(res, source)
-- return status_cb(res, source, function(chunk)
-- local json_parse = luci.jsonc.parse
-- local step = json_parse(chunk)
-- if type(step) == "table" then
-- local buf = _docker:read_status()
-- local num = 0
-- local str = '\t' .. (step.status and step.status or "") .. (step.progress and (" " .. step.progress) or "").."\n"
-- if step.status then buf, num = buf:gsub("\t"..step.status .. " .-\n", str) end
-- if num == 0 then
-- buf = buf .. str
-- end
-- _docker:write_status(buf)
-- end
-- end
-- )
-- end
_docker.import_image_show_status_cb = function(res, source)
return status_cb(res, source, function(chunk)
local json_parse = luci.jsonc.parse
local step = json_parse(chunk)
if type(step) == "table" then
local buf = _docker:read_status()
local num = 0
local str = '\t' .. (step.status and step.status or "") .. (step.progress and (" " .. step.progress) or "").."\n"
if step.status then buf, num = buf:gsub("\t"..step.status .. " .-\n", str) end
if num == 0 then
buf = buf .. str
end
_docker:write_status(buf)
end
end
)
end
-- _docker.print_status_cb = function(res, source)
-- return status_cb(res, source, function(step)
@ -333,4 +333,70 @@ end
-- )
-- end
return _docker
_docker.create_macvlan_interface = function(name, device, gateway, ip_range)
if not nixio.fs.access("/etc/config/network") or not nixio.fs.access("/etc/config/firewall") then return end
if uci:get("dockerman", "local", "remote_endpoint") == "true" then return end
local ip = require "luci.ip"
local if_name = "docker_"..name
local dev_name = "macvlan_"..name
local net_mask = tostring(ip.new(ip_range):mask())
local lan_interfaces
-- add macvlan device
uci:delete("network", dev_name)
uci:set("network", dev_name, "device")
uci:set("network", dev_name, "name", dev_name)
uci:set("network", dev_name, "ifname", device)
uci:set("network", dev_name, "type", "macvlan")
uci:set("network", dev_name, "mode", "bridge")
-- add macvlan interface
uci:delete("network", if_name)
uci:set("network", if_name, "interface")
uci:set("network", if_name, "proto", "static")
uci:set("network", if_name, "ifname", dev_name)
uci:set("network", if_name, "ipaddr", gateway)
uci:set("network", if_name, "netmask", net_mask)
uci:foreach("firewall", "zone", function(s)
if s.name == "lan" then
local interfaces
if type(s.network) == "table" then
interfaces = table.concat(s.network, " ")
uci:delete("firewall", s[".name"], "network")
else
interfaces = s.network and s.network or ""
end
interfaces = interfaces .. " " .. if_name
interfaces = interfaces:gsub("%s+", " ")
uci:set("firewall", s[".name"], "network", interfaces)
end
end)
uci:commit("firewall")
uci:commit("network")
os.execute("ifup " .. if_name)
end
_docker.remove_macvlan_interface = function(name)
if not nixio.fs.access("/etc/config/network") or not nixio.fs.access("/etc/config/firewall") then return end
if uci:get("dockerman", "local", "remote_endpoint") == "true" then return end
local if_name = "docker_"..name
local dev_name = "macvlan_"..name
uci:foreach("firewall", "zone", function(s)
if s.name == "lan" then
local interfaces
if type(s.network) == "table" then
interfaces = table.concat(s.network, " ")
else
interfaces = s.network and s.network or ""
end
interfaces = interfaces and interfaces:gsub(if_name, "")
interfaces = interfaces and interfaces:gsub("%s+", " ")
uci:set("firewall", s[".name"], "network", interfaces)
end
end)
uci:commit("firewall")
uci:delete("network", dev_name)
uci:delete("network", if_name)
uci:commit("network")
os.execute("ip link del " .. if_name)
end
return _docker

View File

@ -78,7 +78,7 @@ function docker_status_message(type, content) {
XHR.run();
}
}
var loading_msg="Loadding.."
var loading_msg="Loading.."
function uci_confirm_docker() {
var tt;
docker_status_message('notice');
@ -95,7 +95,7 @@ function uci_confirm_docker() {
// var delay = isNaN(duration) ? 0 : Math.max(1000 - duration, 0);
var delay =1000
window.setTimeout(function() {
xhr.get('<%=url("admin/services/docker/confirm")%>', null, call, uci_apply_timeout * 1000);
xhr.get('<%=url("admin/docker/confirm")%>', null, call, uci_apply_timeout * 1000);
}, delay);
};
@ -103,7 +103,7 @@ function uci_confirm_docker() {
var now = Date.now();
docker_status_message('notice',
'<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> <span style="white-space:pre-line; font-family: \'Courier New\', Courier, monospace;">' +
'<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> <span style="white-space:pre-line; word-break:break-all; font-family: \'Courier New\', Courier, monospace;">' +
loading_msg + '</span>');
tt = window.setTimeout(tick, 200);
@ -124,7 +124,7 @@ function fnSubmitForm(el){
}
<% if self.err then -%>
docker_status_message('warning', '<span style="white-space:pre-line; font-family: \'Courier New\', Courier, monospace;">'+`<%=self.err%>`+'</span>');
docker_status_message('warning', '<span style="white-space:pre-line; word-break:break-all; font-family: \'Courier New\', Courier, monospace;">'+`<%=self.err%>`+'</span>');
document.getElementById('docker_apply_overlay').addEventListener("click", (e)=>{
docker_status_message()
})
@ -137,4 +137,4 @@ var buttons = document.querySelectorAll('input[type="submit"]');
});
}
//]]></script>
//]]></script>

View File

@ -1,27 +0,0 @@
<%+cbi/valueheader%>
<div<%=
attr("data-prefix", cbid) ..
attr("data-browser-path", self.default_path) ..
attr("data-dynlist", luci.util.serialize_json({
self.keylist, self.vallist,
self.datatype, self.optional or self.rmempty
})) ..
ifattr(self.size, "data-size", self.size) ..
ifattr(self.placeholder, "data-placeholder", self.placeholder)
%>>
<%
local vals = self:cfgvalue(section) or self.default or {}
for i=1, #vals + 1 do
local val = vals[i]
if (val and #val > 0) or (i == 1) then
%>
<input class="cbi-input-text" value="<%=pcdata(val)%>" data-update="change" type="text"<%=
attr("id", cbid .. "." .. i) ..
attr("name", cbid) ..
ifattr(self.size, "size") ..
ifattr(i == 1 and self.placeholder, "placeholder", self.placeholder)
%> /><br />
<% end end %>
</div>
<%+cbi/valuefooter%>

View File

@ -1,89 +0,0 @@
<%
if not self.embedded then
%><form method="post" enctype="multipart/form-data" action="<%=REQUEST_URI%>"<%=
attr("data-strings", luci.util.serialize_json({
label = {
choose = translate('-- Please choose --'),
custom = translate('-- custom --'),
},
path = {
resource = resource,
browser = url("admin/filebrowser")
}
}))
%>>
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
<input type="hidden" name="token" value="<%=token%>" />
<input type="hidden" name="cbi.submit" value="1" /><%
end
%><div class="cbi-map" id="cbi-<%=self.config%>"><%
if self.title and #self.title > 0 then
%><h2 name="content"><%=self.title%></h2><%
end
if self.description and #self.description > 0 then
%><div class="cbi-map-descr"><%=self.description%></div><%
end
self:render_children()
%></div><%
if self.message then
%><div class="alert-message notice"><%=self.message%></div><%
end
if self.errmessage then
%><div class="alert-message warning"><%=self.errmessage%></div><%
end
if not self.embedded then
if type(self.hidden) == "table" then
local k, v
for k, v in pairs(self.hidden) do
%><input type="hidden" id="<%=k%>" name="<%=k%>" value="<%=pcdata(v)%>" /><%
end
end
local display_back = (self.redirect)
local display_cancel = (self.cancel ~= false and self.on_cancel)
local display_skip = (self.flow and self.flow.skip)
local display_submit = (self.submit ~= false)
local display_reset = (self.reset ~= false)
if display_back or display_cancel or display_skip or display_submit or display_reset then
%><div class="cbi-page-actions"><%
if display_back then
%><input class="cbi-button cbi-button-link" type="button" value="<%:Back to Overview%>" onclick="location.href='<%=pcdata(self.redirect)%>'" /> <%
end
if display_cancel then
local label = pcdata(self.cancel or translate("Cancel"))
%><input class="cbi-button cbi-button-link" type="button" value="<%=label%>" onclick="cbi_submit(this, 'cbi.cancel')" /> <%
end
if display_skip then
%><input class="cbi-button cbi-button-neutral" type="button" value="<%:Skip%>" onclick="cbi_submit(this, 'cbi.skip')" /> <%
end
if display_submit then
local label = pcdata(self.submit or translate("Submit"))
%><input class="cbi-button cbi-button-save" type="submit" value="<%=label%>" /> <%
end
if display_reset then
local label = pcdata(self.reset or translate("Reset"))
%><input class="cbi-button cbi-button-reset" type="reset" value="<%=label%>" /> <%
end
%></div><%
end
%></form><%
end
%>
<script type="text/javascript">cbi_init();</script>

View File

@ -9,18 +9,18 @@
</ul>
<script type="text/javascript">
let re = /\/admin\/services\/docker\/container\//
let re = /\/admin\/docker\/container\//
let p = window.location.href
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","file","console","logs"]
actions.forEach(function(item) {
document.getElementById("a-cbi-tab-container_" + item).href= path[0]+"/admin/services/docker/container/"+container_id+'/'+item
document.getElementById("a-cbi-tab-container_" + item).href= path[0]+"/admin/docker/container/"+container_id+'/'+item
if (action === item) {
document.getElementById("cbi-tab-container_" + item).className="cbi-tab"
} else {
document.getElementById("cbi-tab-container_" + item).className="cbi-tab-disable"
document.getElementById("cbi-tab-container_" + item).className="cbi-tab-disabled"
}
})
</script>
</script>

View File

@ -3,4 +3,4 @@
</div>
<script type="text/javascript">
document.getElementById("terminal").src = "http://" + window.location.hostname + ":7682";
</script>
</script>

View File

@ -33,7 +33,7 @@
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.open("POST", "/cgi-bin/luci/admin/docker/container_put_archive/<%=self.container%>", true)
xhr.onload = function() {
if (xhr.status == 200) {
uploadArchive.value = ''
@ -58,6 +58,6 @@
})
return
}
window.open("/cgi-bin/luci/admin/services/docker/container_get_archive/?id=<%=self.container%>&path=" + encodeURIComponent(downloadPath))
window.open("/cgi-bin/luci/admin/docker/container_get_archive/?id=<%=self.container%>&path=" + encodeURIComponent(downloadPath))
}
</script>
</script>

View File

@ -22,7 +22,7 @@
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
XHR.poll(5, '/cgi-bin/luci/admin/services/docker/container_stats/<%=self.container_id%>', { status: 1 },
XHR.poll(5, '/cgi-bin/luci/admin/docker/container_stats/<%=self.container_id%>', { status: 1 },
function (x, info) {
var e;
@ -57,4 +57,4 @@
}
});
//]]></script>
//]]></script>

View File

@ -1,5 +1,3 @@
<!-- this page has no effect -->
<input type="text" class="cbi-input-text" name="isrc" placeholder="http://host/image.tar" id="isrc" />
<input type="text" class="cbi-input-text" name="itag" placeholder="repository:tag" id="itag" />
<div style="display: inline-block;">
@ -21,7 +19,7 @@
formData.append('tag', valITag.value)
let xhr = new XMLHttpRequest()
uci_confirm_docker()
xhr.open("POST", "<%=url('admin/services/docker/images_import')%>", true)
xhr.open("POST", "<%=url('admin/docker/images_import')%>", true)
xhr.onload = function () {
location.reload()
}
@ -42,7 +40,7 @@
formData.append('upload-archive', fileimport.files[0])
let xhr = new XMLHttpRequest()
uci_confirm_docker()
xhr.open("POST", "<%=url('admin/services/docker/images_import')%>", true)
xhr.open("POST", "<%=url('admin/docker/images_import')%>", true)
xhr.onload = function () {
fileimport.value = ''
location.reload()
@ -53,7 +51,7 @@
let new_tag = function (image_id) {
let new_tag = prompt("<%:New tag%>\n<%:Image%>" + "ID: " + image_id + "\n<%:Please input new tag%>:", "")
if (new_tag) {
(new XHR()).post("<%=url('admin/services/docker/images_tag')%>",
(new XHR()).post("<%=url('admin/docker/images_tag')%>",
{ id: image_id, tag: new_tag },
function (r) {
if (r.status == 201) {
@ -72,7 +70,7 @@
let un_tag = function (tag) {
if (tag.match("<none>")) return
if (confirm("<%:Remove tag%>: " + tag + " ?")) {
(new XHR()).post("<%=url('admin/services/docker/images_untag')%>",
(new XHR()).post("<%=url('admin/docker/images_untag')%>",
{ tag: tag },
function (r) {
if (r.status == 200) {
@ -87,4 +85,4 @@
})
}
}
</script>
</script>

View File

@ -20,7 +20,7 @@
formData.append('upload-archive', fileLoad.files[0])
let xhr = new XMLHttpRequest()
uci_confirm_docker()
xhr.open("POST", "/cgi-bin/luci/admin/services/docker/images_load", true)
xhr.open("POST", "/cgi-bin/luci/admin/docker/images_load", true)
xhr.onload = function() {
location.reload()
}

View File

@ -8,4 +8,4 @@
</div>
<% if self.title == "Events" then %>
<%+footer%>
<% end %>
<% end %>

View File

@ -73,7 +73,7 @@
let reg_rem =/`#.+`/g// the command has `# `
cmd_line = cmd_line.replace(/^docker\s+(run|create)/,"DOCKERCLI").replace(reg_rem, " ").replace(reg_muti_line, " ").replace(reg_space, " ")
console.log(cmd_line)
window.location.href = "/cgi-bin/luci/admin/services/docker/newcontainer/" + encodeURI(cmd_line)
window.location.href = "/cgi-bin/luci/admin/docker/newcontainer/" + encodeURI(cmd_line)
}
function clear_text(){
@ -92,4 +92,4 @@
<%+cbi/valueheader%>
<input type="button" class="cbi-button cbi-button-apply" value="<%:Command line%>" onclick="show_reslov_dialog()" />
<%+cbi/valuefooter%>
<%+cbi/valuefooter%>

View File

@ -136,7 +136,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
<div class="pure-u-3-5">
<h4 style="text-align: right; font-size: 1rem"><%:Containers%></h4>
<h4 style="text-align: right;">
<%- if self.containers_total ~= "-" then -%><a href="/cgi-bin/luci/admin/services/docker/containers"><%- end -%>
<%- if self.containers_total ~= "-" then -%><a href="/cgi-bin/luci/admin/docker/containers"><%- end -%>
<span style="font-size: 2rem; color: #2dce89;"><%=self.containers_running%></span>
<span style="font-size: 1rem; color: #8898aa !important;">/<%=self.containers_total%></span>
<%- if self.containers_total ~= "-" then -%></a><%- end -%>
@ -160,7 +160,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
<div class="pure-u-3-5">
<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 -%>
<%- if self.images_total ~= "-" then -%><a href="/cgi-bin/luci/admin/docker/images"><%- end -%>
<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 -%>
@ -192,7 +192,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
<div class="pure-u-3-5">
<h4 style="text-align: right; font-size: 1rem"><%:Networks%></h4>
<h4 style="text-align: right;">
<%- if self.networks_total ~= "-" then -%><a href="/cgi-bin/luci/admin/services/docker/networks"><%- end -%>
<%- if self.networks_total ~= "-" then -%><a href="/cgi-bin/luci/admin/docker/networks"><%- end -%>
<span style="font-size: 2rem; color: #2dce89;"><%=self.networks_total%></span>
<!-- <span style="font-size: 1rem; color: #8898aa !important;">/20</span> -->
<%- if self.networks_total ~= "-" then -%></a><%- end -%>
@ -269,7 +269,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
<div class="pure-u-3-5">
<h4 style="text-align: right; font-size: 1rem"><%:Volumes%></h4>
<h4 style="text-align: right;">
<%- if self.volumes_total ~= "-" then -%><a href="/cgi-bin/luci/admin/services/docker/volumes"><%- end -%>
<%- if self.volumes_total ~= "-" then -%><a href="/cgi-bin/luci/admin/docker/volumes"><%- end -%>
<span style="font-size: 2rem; color: #2dce89;"><%=self.volumes_total%></span>
<!-- <span style="font-size: 1rem; color: #8898aa !important;">/20</span> -->
<%- if self.volumes_total ~= "-" then -%></a><%- end -%>
@ -277,4 +277,4 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
</div>
</div>
</div>
</div>
</div>

View File

@ -1,31 +0,0 @@
<br>
<h2><%:Setting%></h2>
<ul class="cbi-tabmenu">
<% for k, v in pairs(self.tabs) do
local class = k == self.default_tab and "cbi-tab" or "cbi-tab-disabled"
local id = "tab.overview-tab." .. k
%>
<li id=<%=id%> class=<%=class%> >
<a onclick="this.blur(); return cbi_t_switch('overview-tab', '<%=k%>')" href=""><%=v%></a>
</li>
<% end %>
</ul>
<script type="text/javascript">
window.onload = function () {
<% for k, v in pairs(self.tabs) do
local display = k == self.default_tab and "block" or "none"
local tid = "cbi-"..k.."-local"
local sid = "cbi-"..k.."-local"
local cid = "container.overview-tab." .. k
%>
mount_point_table = document.getElementById("<%=tid%>") || document.getElementById("<%=sid%>") || null
if (mount_point_table) {
mount_point_table.setAttribute("style", "display: <%=display%>;")
mount_point_table.setAttribute("id", "<%=cid%>")
cbi_t_add('overview-tab', '<%=k%>')
// cbi_init();
}
<% end %>
}
</script>

View File

@ -411,3 +411,33 @@ msgstr "错误"
msgid "Console"
msgstr "控制台"
msgid "DockerMan is a Simple Docker manager client for LuCI, If you have any issue please visit:"
msgstr "DockerMan 是一个简单的 Docker 管理客户端,如果您在使用过程中有问题,请访问:"
msgid "Access Control for the bridge network"
msgstr "birdge 网络访问控制"
msgid "Allowed access interfaces"
msgstr "允许访问的接口"
msgid "Containers allowed to be accessed"
msgstr "允许被访问的容器"
msgid "Which container(s) can be accessed, even from interfaces that are not allowed, fill-in Container Id or Name"
msgstr "哪些容器可以被访问即使访问来自不被允许的接口填入容器ID或容器名"
msgid "Which interface(s) can access containers under the bridge network, fill-in Interface Name"
msgstr "哪些接口可以访问 bridge 网络下的容器, 填入接口名"
msgid "Dockerman connect to remote endpoint"
msgstr "Dockerman 将连接到远程节点"
msgid "Access Control"
msgstr "访问控制"
msgid "Create macvlan interface"
msgstr "创建 macvlan 接口"
msgid "Auto create macvlan interface in Openwrt"
msgstr "在 Openwrt 中自动创建 macvlan 接口"

View File

@ -1,5 +0,0 @@
config section 'local'
option ea 'true'
option en_wan 'false'
option data_root '/opt/docker'
option log_level 'warn'

View File

@ -1,5 +1,10 @@
config section 'local'
option socket_path '/var/run/docker.sock'
option status_path '/tmp/.docker_action_status'
option debug_path '/tmp/.docker_debug'
option debug 'false'
option debug_path '/tmp/.docker_debug'
option remote_endpoint 'false'
option daemon_ea 'true'
option daemon_data_root '/opt/docker'
option daemon_log_level 'warn'
list ac_allowed_interface 'br-lan'

View File

@ -1,25 +0,0 @@
#!/bin/sh
dtype=`fdisk -l /dev/sda | grep 'Disklabel type' | awk '{print $3}'`
partid="0"
if [ "$dtype" = "gpt" ]
then
partid=`echo "n
w
" | fdisk /dev/sda | grep 'Created a new partition' | awk '{print $5}'`
elif [ "$dtype" = "dos" ]
then
partid=`echo "n
p
w
" | fdisk /dev/sda | grep 'Created a new partition' | awk '{print $5}'`
fi
echo "y" | mkfs.ext4 /dev/sda$partid

View File

@ -1,66 +0,0 @@
#!/bin/sh /etc/rc.common
USE_PROCD=1
START=99
NAME=dockerd_local
DOCKERD_CONF="/etc/docker/daemon.json"
append_list_item() {
append "$2" "$1" "\",\""
}
get_config() {
config_load $1
config_get ea "local" ea
config_get en_wan "local" en_wan
config_get data_root "local" data_root '/opt/docker'
config_get log_level "local" log_level 'warn'
config_list_foreach "local" registry_mirrors append_list_item registry_mirrors
config_list_foreach "local" hosts append_list_item hosts
}
gen_config(){
cat <<-EOF >$DOCKERD_CONF
{
"data-root": "$data_root",
"log-level": "$log_level",
"registry-mirrors": ["$registry_mirrors"],
"hosts": ["$hosts"]
}
EOF
sed -i 's/\[\"\"\]/\[\]/g' $DOCKERD_CONF
}
_start() {
local nofile=$(cat /proc/sys/fs/nr_open)
get_config dockerd
gen_config
if [ -n "$en_wan" ]; then
iptables -D DOCKER-USER ! -i br-lan -m conntrack --ctstate NEW,INVALID -o docker0 -j DROP >/dev/null 2>&1
iptables -D DOCKER-USER ! -i br-lan -m conntrack --ctstate ESTABLISHED,RELATED -o docker0 -j RETURN >/dev/null 2>&1
else
iptables -D DOCKER-USER ! -i br-lan -m conntrack --ctstate NEW,INVALID -o docker0 -j DROP >/dev/null 2>&1
iptables -D DOCKER-USER ! -i br-lan -m conntrack --ctstate ESTABLISHED,RELATED -o docker0 -j RETURN >/dev/null 2>&1
iptables -I DOCKER-USER ! -i br-lan -m conntrack --ctstate ESTABLISHED,RELATED -o docker0 -j RETURN >/dev/null 2>&1
iptables -I DOCKER-USER ! -i br-lan -m conntrack --ctstate NEW,INVALID -o docker0 -j DROP >/dev/null 2>&1
fi
if [ -n "$ea" ]; then
procd_open_instance $NAME
procd_set_param stderr 1
procd_set_param command /usr/bin/dockerd
procd_set_param limits nofile="${nofile} ${nofile}"
procd_close_instance
fi
}
start_service() {
_start
}
service_triggers() {
procd_add_reload_trigger dockerd
}
reload_service() {
restart
}

View File

@ -0,0 +1,45 @@
#!/bin/sh /etc/rc.common
START=99
DOCKERD_CONF="/etc/docker/daemon.json"
config_load dockerman
config_get daemon_ea "local" daemon_ea
init_dockerman_chain(){
iptables -N DOCKER-MAN >/dev/null 2>&1
iptables -F DOCKER-MAN >/dev/null 2>&1
iptables -D DOCKER-USER -j DOCKER-MAN >/dev/null 2>&1
iptables -I DOCKER-USER -j DOCKER-MAN >/dev/null 2>&1
}
add_allowed_interface(){
iptables -A DOCKER-MAN -i $1 -o docker0 -j RETURN
}
add_allowed_ip(){
iptables -A DOCKER-MAN -d $1 -o docker0 -j RETURN
}
handle_allowed_interface(){
#config_list_foreach "local" allowed_ip add_allowed_ip
config_list_foreach "local" ac_allowed_interface add_allowed_interface
iptables -A DOCKER-MAN -m conntrack --ctstate ESTABLISHED,RELATED -o docker0 -j RETURN >/dev/null 2>&1
iptables -A DOCKER-MAN -m conntrack --ctstate NEW,INVALID -o docker0 -j DROP >/dev/null 2>&1
iptables -A DOCKER-MAN -j RETURN >/dev/null 2>&1
}
start(){
init_dockerman_chain
if [ -n "$daemon_ea" ]; then
handle_allowed_interface
lua /usr/share/dockerman/dockerd-config.lua "$DOCKERD_CONF" && /etc/init.d/dockerd restart && sleep 5 || {
# 1 running, 0 stopped
STATE=$([ -n "$(ps |grep /usr/bin/dockerd | grep -v grep)" ] && echo 1 || echo 0)
[ "$STATE" == "0" ] && /etc/init.d/dockerd start && sleep 5
}
lua /usr/share/dockerman/dockerd-ac.lua
else
/etc/init.d/dockerd stop
fi
}

View File

@ -1,14 +1,14 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
set uhttpd.main.script_timeout="600"
set uhttpd.main.script_timeout="360"
commit uhttpd
delete ucitrack.@dockerd[-1]
add ucitrack dockerd
set ucitrack.@dockerd[-1].init=dockerd
delete ucitrack.@dockerman[-1]
add ucitrack dockerman
set ucitrack.@dockerman[-1].exec='/etc/init.d/dockerman start'
commit ucitrack
EOF
/etc/init.d/dockerd enable >/dev/null 2>&1
[ -x "$(which dockerd)" ] && chmod +x /etc/init.d/dockerman && /etc/init.d/dockerd disable && /etc/init.d/dockerman enable >/dev/null 2>&1
/etc/init.d/uhttpd restart >/dev/null 2>&1
rm -fr /tmp/luci-indexcache /tmp/luci-modulecache >/dev/null 2>&1
exit 0
exit 0

View File

@ -0,0 +1,20 @@
require "luci.util"
docker = require "luci.docker"
uci = (require "luci.model.uci").cursor()
dk = docker.new({socket_path = "/var/run/docker.sock"})
if dk:_ping().code ~= 200 then return end
containers_list = dk.containers:list({query = {all=true}}).body
allowed_container = uci:get("dockerman", "local", "ac_allowed_container")
if not allowed_container or next(allowed_container)==nil then return end
allowed_ip = {}
for i, v in ipairs(containers_list) do
for ii, vv in ipairs(allowed_container) do
if v.Id:sub(1,12) == vv and v.NetworkSettings and v.NetworkSettings.Networks and v.NetworkSettings.Networks.bridge and v.NetworkSettings.Networks.bridge.IPAddress then
print(v.NetworkSettings.Networks.bridge.IPAddress)
luci.util.exec("iptables -I DOCKER-MAN -d "..v.NetworkSettings.Networks.bridge.IPAddress.." -o docker0 -j RETURN")
table.remove(allowed_container, ii)
end
end
end

View File

@ -0,0 +1,50 @@
require "luci.util"
fs = require "nixio.fs"
uci = (require "luci.model.uci").cursor()
raw_file_dir = arg[1]
raw_json_str = fs.readfile(raw_file_dir)
raw_json = luci.jsonc.parse(raw_json_str) or {}
new_json = {}
new_json["data-root"] = uci:get("dockerman", "local", "daemon_data_root")
new_json["hosts"] = uci:get("dockerman", "local", "daemon_hosts")
new_json["registry-mirrors"] = uci:get("dockerman", "local", "daemon_registry_mirrors")
new_json["log-level"] = uci:get("dockerman", "local", "daemon_log_level")
function comp(raw, new)
for k, v in pairs(new) do
if type(v) == "table" and raw[k] then
if #v == #raw[k] then
comp(raw[k], v)
else
changed = true
raw[k] = v
end
elseif raw[k] ~= v then
changed = true
raw[k] = v
end
end
for k, v in ipairs(new) do
if type(v) == "table" and raw[k] then
if #v == #raw[k] then
comp(raw[k], v)
else
changed = true
raw[k] = v
end
elseif raw[k] ~= v then
changed = true
raw[k] = v
end
end
end
comp(raw_json, new_json)
if changed then
fs.writefile(raw_file_dir, luci.jsonc.stringify(raw_json, true):gsub("\\", ""))
os.exit(0)
else
os.exit(1)
end