summaryrefslogtreecommitdiffhomepage
path: root/collections/luci-lib-docker/luasrc/docker.lua
diff options
context:
space:
mode:
Diffstat (limited to 'collections/luci-lib-docker/luasrc/docker.lua')
-rw-r--r--collections/luci-lib-docker/luasrc/docker.lua788
1 files changed, 436 insertions, 352 deletions
diff --git a/collections/luci-lib-docker/luasrc/docker.lua b/collections/luci-lib-docker/luasrc/docker.lua
index 0361c05382..6424653708 100644
--- a/collections/luci-lib-docker/luasrc/docker.lua
+++ b/collections/luci-lib-docker/luasrc/docker.lua
@@ -2,6 +2,7 @@
LuCI - Lua Configuration Interface
Copyright 2019 lisaac <https://github.com/lisaac/luci-lib-docker>
]]--
+
require "nixio.util"
require "luci.util"
local jsonc = require "luci.jsonc"
@@ -14,301 +15,376 @@ local json_stringify = jsonc.stringify
local json_parse = jsonc.parse
local chunksource = function(sock, buffer)
- buffer = buffer or ""
- return function()
- local output
- local _, endp, count = buffer:find("^([0-9a-fA-F]+)\r\n")
- if not count then
- local newblock, code = sock:recv(1024)
- if not newblock then return nil, code end
- buffer = buffer .. newblock
- _, endp, count = buffer:find("^([0-9a-fA-F]+)\r\n")
- end
- count = tonumber(count, 16)
- if not count then
- return nil, -1, "invalid encoding"
- elseif count == 0 then -- finial
- return nil
- elseif count <= #buffer - endp then
- --data >= count
- output = buffer:sub(endp + 1, endp + count)
- if count == #buffer - endp then -- [data]
- buffer = buffer:sub(endp + count + 1)
- count, code = sock:recvall(2) --read \r\n
- if not count then return nil, code end
- elseif count + 1 == #buffer - endp then -- [data]\r
- buffer = buffer:sub(endp + count + 2)
- count, code = sock:recvall(1) --read \n
- if not count then return nil, code end
- else -- [data]\r\n[count]\r\n[data]...
- buffer = buffer:sub(endp + count + 3) -- cut buffer
- end
- return output
- else
- -- data < count
- output = buffer:sub(endp + 1, endp + count)
- buffer = buffer:sub(endp + count + 1)
- local remain, code = sock:recvall(count - #output) --need read remaining
- if not remain then return nil, code end
- output = output .. remain
- count, code = sock:recvall(2) --read \r\n
- if not count then return nil, code end
- return output
- end
- end
+ buffer = buffer or ""
+
+ return function()
+ local output
+ local _, endp, count = buffer:find("^([0-9a-fA-F]+)\r\n")
+
+ if not count then
+ local newblock, code = sock:recv(1024)
+ if not newblock then
+ return nil, code
+ end
+ buffer = buffer .. newblock
+ _, endp, count = buffer:find("^([0-9a-fA-F]+)\r\n")
+ end
+
+ count = tonumber(count, 16)
+ if not count then
+ return nil, -1, "invalid encoding"
+ elseif count == 0 then -- finial
+ return nil
+ elseif count <= #buffer - endp then -- data >= count
+ output = buffer:sub(endp + 1, endp + count)
+ if count == #buffer - endp then -- [data]
+ buffer = buffer:sub(endp + count + 1)
+ count, code = sock:recvall(2) --read \r\n
+ if not count then
+ return nil, code
+ end
+ elseif count + 1 == #buffer - endp then -- [data]\r
+ buffer = buffer:sub(endp + count + 2)
+ count, code = sock:recvall(1) --read \n
+ if not count then
+ return nil, code
+ end
+ else -- [data]\r\n[count]\r\n[data]...
+ buffer = buffer:sub(endp + count + 3) -- cut buffer
+ end
+ return output
+ else -- data < count
+ output = buffer:sub(endp + 1, endp + count)
+ buffer = buffer:sub(endp + count + 1)
+ local remain, code = sock:recvall(count - #output) --need read remaining
+ if not remain then
+ return nil, code
+ end
+ output = output .. remain
+ count, code = sock:recvall(2) --read \r\n
+ if not count then
+ return nil, code
+ end
+ return output
+ end
+ 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
+ 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
- return ""
- end
- local stream_type = ((string.byte(buffer, 1) == 1) and "stdout") or ((string.byte(buffer, 1) == 2) and "stderr") or ((string.byte(buffer, 1) == 0) and "stdin") or "stream_err"
- local valid_length =
- tonumber(string.byte(buffer, 5)) * 256 * 256 * 256 + tonumber(string.byte(buffer, 6)) * 256 * 256 + tonumber(string.byte(buffer, 7)) * 256 + tonumber(string.byte(buffer, 8))
- if valid_length > #buffer + 8 then
- return ""
- end
- return stream_type .. ": " .. string.sub(buffer, 9, valid_length + 8)
- -- return string.sub(buffer, 9, valid_length + 8)
+ buffer = buffer or ""
+ if #buffer < 8 then
+ return ""
+ end
+ local stream_type = ((string.byte(buffer, 1) == 1) and "stdout") or ((string.byte(buffer, 1) == 2) and "stderr") or ((string.byte(buffer, 1) == 0) and "stdin") or "stream_err"
+ local valid_length = tonumber(string.byte(buffer, 5)) * 256 * 256 * 256 + tonumber(string.byte(buffer, 6)) * 256 * 256 + tonumber(string.byte(buffer, 7)) * 256 + tonumber(string.byte(buffer, 8))
+ if valid_length > #buffer + 8 then
+ return ""
+ end
+ return stream_type .. ": " .. string.sub(buffer, 9, valid_length + 8)
end
local open_socket = function(req_options)
- local socket
- if type(req_options) ~= "table" then return socket end
- if req_options.socket_path then
- socket = nixio.socket("unix", "stream")
- if socket:connect(req_options.socket_path) ~= true then return nil end
- elseif req_options.host and req_options.port then
- socket = nixio.connect(req_options.host, req_options.port)
- end
- if socket then
- return socket
- else
- return nil
- end
+ local socket
+ if type(req_options) ~= "table" then
+ return socket
+ end
+ if req_options.socket_path then
+ socket = nixio.socket("unix", "stream")
+ if socket:connect(req_options.socket_path) ~= true then
+ return nil
+ end
+ elseif req_options.host and req_options.port then
+ socket = nixio.connect(req_options.host, req_options.port)
+ end
+ if socket then
+ return socket
+ else
+ return nil
+ end
end
-local send_http_socket = function(docker_socket, req_header, req_body, callback)
- if docker_socket:send(req_header) == 0 then
- return {
- headers={code=498,message="bad path", protocol="HTTP/1.1"},
- body={message="can\'t send data to socket"}
- }
- end
-
- 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))
- if options.debug then io.popen("echo '".. json_stringify(req_body) .. "' >> " .. options.debug_path) end
- elseif req_body then
- docker_socket:send(req_body)
- if options.debug then io.popen("echo '".. req_body .. "' >> " .. options.debug_path) end
- 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"}
- }
- end
- local response = {code = 0, headers = {}, body = {}}
-
- local p, code, msg = line:match("^([%w./]+) ([0-9]+) (.*)")
- response.protocol = p
- response.code = tonumber(code)
- response.message = msg
- line = linesrc()
- while line and line ~= "" do
- local key, val = line:match("^([%w-]+)%s?:%s?(.*)")
- if key and key ~= "Status" then
- if type(response.headers[key]) == "string" then
- response.headers[key] = {response.headers[key], val}
- elseif type(response.headers[key]) == "table" then
- response.headers[key][#response.headers[key] + 1] = val
- else
- response.headers[key] = val
- end
- end
- line = linesrc()
- end
- -- handle response body
- local body_buffer = linesrc(true)
- response.body = {}
- 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
- 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
+local send_http_socket = function(options, docker_socket, req_header, req_body, callback)
+ if docker_socket:send(req_header) == 0 then
+ return {
+ headers={
+ code=498,
+ message="bad path",
+ protocol="HTTP/1.1"
+ },
+ body={
+ message="can\'t send data to socket"
+ }
+ }
+ end
+
+ 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))
+ if options.debug then
+ io.popen("echo '".. json_stringify(req_body) .. "' >> " .. options.debug_path)
+ end
+ elseif req_body then
+ docker_socket:send(req_body)
+ if options.debug then
+ io.popen("echo '".. req_body .. "' >> " .. options.debug_path)
+ end
+ 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"
+ }
+ }
+ end
+
+ local response = {
+ code = 0,
+ headers = {},
+ body = {}
+ }
+
+ local p, code, msg = line:match("^([%w./]+) ([0-9]+) (.*)")
+ response.protocol = p
+ response.code = tonumber(code)
+ response.message = msg
+ line = linesrc()
+
+ while line and line ~= "" do
+ local key, val = line:match("^([%w-]+)%s?:%s?(.*)")
+ if key and key ~= "Status" then
+ if type(response.headers[key]) == "string" then
+ response.headers[key] = {
+ response.headers[key],
+ val
+ }
+ elseif type(response.headers[key]) == "table" then
+ response.headers[key][#response.headers[key] + 1] = val
+ else
+ response.headers[key] = val
+ end
+ end
+ line = linesrc()
+ end
+
+ -- handle response body
+ local body_buffer = linesrc(true)
+ response.body = {}
+
+ 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
+ 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 gen_header = function(options, http_method, api_group, api_action, name_or_id, request)
- local header, query, path
- name_or_id = (name_or_id ~= "") and name_or_id or nil
-
- if request and type(request.query) == "table" then
- local k, v
- for k, v in pairs(request.query) do
- if type(v) == "table" then
- query = (query and query .. "&" or "?") .. k .. "=" .. urlencode(json_stringify(v))
- elseif type(v) == "boolean" then
- query = (query and query .. "&" or "?") .. k .. "=" .. (v and "true" or "false")
- elseif type(v) == "number" or type(v) == "string" then
- query = (query and query .. "&" or "?") .. k .. "=" .. v
- end
- end
- end
- 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
+ local header, query, path
+ name_or_id = (name_or_id ~= "") and name_or_id or nil
+
+ if request and type(request.query) == "table" then
+ local k, v
+ for k, v in pairs(request.query) do
+ if type(v) == "table" then
+ query = (query and query .. "&" or "?") .. k .. "=" .. urlencode(json_stringify(v))
+ elseif type(v) == "boolean" then
+ query = (query and query .. "&" or "?") .. k .. "=" .. (v and "true" or "false")
+ elseif type(v) == "number" or type(v) == "string" then
+ query = (query and query .. "&" or "?") .. k .. "=" .. v
+ end
+ end
+ end
+
+ 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
- local docker_socket = open_socket(req_options)
-
- if docker_socket then
- return send_http_socket(docker_socket, req_header, req_body, callback)
- else
- return {
- headers = {code=497, message="bad socket path or host", protocol="HTTP/1.1"},
- body = {message="can\'t connect to socket"}
- }
- end
+ 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
+ local docker_socket = open_socket(req_options)
+
+ if docker_socket then
+ return send_http_socket(options, docker_socket, req_header, req_body, callback)
+ else
+ return {
+ headers = {
+ code=497,
+ message="bad socket path or host",
+ protocol="HTTP/1.1"
+ },
+ body = {
+ message="can\'t connect to socket"
+ }
+ }
+ end
end
local gen_api = function(_table, http_method, api_group, api_action)
- local _api_action
- if api_action == "get_archive" or api_action == "put_archive" then
- _api_action = "archive"
- elseif api_action == "df" then
- _api_action = "system/df"
- elseif api_action ~= "list" and api_action ~= "inspect" and api_action ~= "remove" then
- _api_action = api_action
- elseif (api_group == "containers" or api_group == "images" or api_group == "exec") and (api_action == "list" or api_action == "inspect") then
- _api_action = "json"
- end
-
- 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.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.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 = 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])
- end
- response.body = body_buffer
- end
- return response
- end
- 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])
- else
- local tmp = {}
- for _, v in ipairs(response.body) do
- tmp[#tmp+1] = json_parse(v)
- end
- response.body = tmp
- end
- end
- return response
- end
-
- if api_group then
- _table[api_group][api_action] = fp
- else
- _table[api_action] = fp
- end
+ local _api_action
+
+ if api_action == "get_archive" or api_action == "put_archive" then
+ api_action = "archive"
+ elseif api_action == "df" then
+ _api_action = "system/df"
+ elseif api_action ~= "list" and api_action ~= "inspect" and api_action ~= "remove" then
+ _api_action = api_action
+ elseif (api_group == "containers" or api_group == "images" or api_group == "exec") and (api_action == "list" or api_action == "inspect") then
+ _api_action = "json"
+ end
+
+ 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.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.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 = 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])
+ end
+ response.body = body_buffer
+ end
+ return response
+ end
+
+ 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])
+ else
+ local tmp = {}
+ for _, v in ipairs(response.body) do
+ tmp[#tmp+1] = json_parse(v)
+ end
+ response.body = tmp
+ end
+ end
+ return response
+ end
+
+ if api_group then
+ _table[api_group][api_action] = fp
+ else
+ _table[api_action] = fp
+ end
end
-local _docker = {containers = {}, exec = {}, images = {}, networks = {}, volumes = {}}
+local _docker = {
+ containers = {},
+ exec = {},
+ images = {},
+ networks = {},
+ volumes = {}
+}
gen_api(_docker, "GET", "containers", "list")
gen_api(_docker, "POST", "containers", "create")
@@ -368,85 +444,93 @@ gen_api(_docker, "GET", nil, "_ping")
gen_api(_docker, "GET", nil, "df")
function _docker.new(options)
- local docker = {}
- local _options = options or {}
- docker.options = {
- socket_path = _options.socket_path or nil,
- host = _options.socket_path and "localhost" or _options.host,
- port = not _options.socket_path and _options.port or nil,
- tls = _options.tls or nil,
- tls_cacert = _options.tls and _options.tls_cacert or nil,
- tls_cert = _options.tls and _options.tls_cert or nil,
- tls_key = _options.tls and _options.tls_key or nil,
- version = _options.version or "v1.40",
- user_agent = _options.user_agent or "LuCI",
- protocol = _options.protocol or "HTTP/1.1",
- debug = _options.debug or false,
- debug_path = _options.debug and _options.debug_path or nil
- }
- setmetatable(
- docker,
- {
- __index = function(t, key)
- if _docker[key] ~= nil then
- return _docker[key]
- else
- return _docker.containers[key]
- end
- end
- }
- )
- setmetatable(
- docker.containers,
- {
- __index = function(t, key)
- if key == "options" then
- return docker.options
- end
- end
- }
- )
- setmetatable(
- docker.networks,
- {
- __index = function(t, key)
- if key == "options" then
- return docker.options
- end
- end
- }
- )
- setmetatable(
- docker.images,
- {
- __index = function(t, key)
- if key == "options" then
- return docker.options
- end
- end
- }
- )
- setmetatable(
- docker.volumes,
- {
- __index = function(t, key)
- if key == "options" then
- return docker.options
- end
- end
- }
- )
- setmetatable(
- docker.exec,
- {
- __index = function(t, key)
- if key == "options" then
- return docker.options
- end
- end
- }
- )
- return docker
+ local docker = {}
+ local _options = options or {}
+
+ docker.options = {
+ socket_path = _options.socket_path or nil,
+ host = _options.socket_path and "localhost" or _options.host,
+ port = not _options.socket_path and _options.port or nil,
+ tls = _options.tls or nil,
+ tls_cacert = _options.tls and _options.tls_cacert or nil,
+ tls_cert = _options.tls and _options.tls_cert or nil,
+ tls_key = _options.tls and _options.tls_key or nil,
+ version = _options.version or "v1.40",
+ user_agent = _options.user_agent or "LuCI",
+ protocol = _options.protocol or "HTTP/1.1",
+ debug = _options.debug or false,
+ debug_path = _options.debug and _options.debug_path or nil
+ }
+
+ setmetatable(
+ docker,
+ {
+ __index = function(t, key)
+ if _docker[key] ~= nil then
+ return _docker[key]
+ else
+ return _docker.containers[key]
+ end
+ end
+ }
+ )
+
+ setmetatable(
+ docker.containers,
+ {
+ __index = function(t, key)
+ if key == "options" then
+ return docker.options
+ end
+ end
+ }
+ )
+
+ setmetatable(
+ docker.networks,
+ {
+ __index = function(t, key)
+ if key == "options" then
+ return docker.options
+ end
+ end
+ }
+ )
+
+ setmetatable(
+ docker.images,
+ {
+ __index = function(t, key)
+ if key == "options" then
+ return docker.options
+ end
+ end
+ }
+ )
+
+ setmetatable(
+ docker.volumes,
+ {
+ __index = function(t, key)
+ if key == "options" then
+ return docker.options
+ end
+ end
+ }
+ )
+
+ setmetatable(
+ docker.exec,
+ {
+ __index = function(t, key)
+ if key == "options" then
+ return docker.options
+ end
+ end
+ }
+ )
+
+ return docker
end
return _docker