Skip to content

Commit

Permalink
feat: use luasocket instead of in etcd.lua (#2818)
Browse files Browse the repository at this point in the history
  • Loading branch information
starsz committed Dec 4, 2020
1 parent 9e3c34a commit 5782cc7
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 31 deletions.
50 changes: 50 additions & 0 deletions .travis/apisix_cli_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -817,3 +817,53 @@ fi
done

echo "passed: etcd auth enabled and init kv has been set up correctly"

# check etcd connect refused
git checkout conf/config.yaml

echo '
etcd:
host:
- "http://127.0.0.1:2389"
prefix: "/apisix"
' > conf/config.yaml

make init &>/tmp/apisix_temp &
sleep 1
if [`grep -c "connection refused" /tmp/apisix_temp` -ne '1']; then
echo "failed: not output connection refused"
exit 1
fi

echo "passed: show connection refused info successfully"

# check etcd auth error
git checkout conf/config.yaml

export ETCDCTL_API=3
etcdctl version
etcdctl --endpoints=127.0.0.1:2379 user add "root:apache-api6"
etcdctl --endpoints=127.0.0.1:2379 role add root
etcdctl --endpoints=127.0.0.1:2379 user grant-role root root
etcdctl --endpoints=127.0.0.1:2379 user get root
etcdctl --endpoints=127.0.0.1:2379 auth enable
etcdctl --endpoints=127.0.0.1:2379 --user=root:apache-api6 del /apisix --prefix

echo '
etcd:
host:
- "http://127.0.0.1:2379"
prefix: "/apisix"
timeout: 30
user: root
password: apache-api7
' > conf/config.yaml

make init &>/tmp/apisix_temp &
sleep 1
if [`grep -c "invalid user ID or password" /tmp/apisix_temp` -ne '1']; then
echo "failed: not output invalid user ID or password"
exit 1
fi

echo "passed: show password error successfully"
87 changes: 56 additions & 31 deletions apisix/cli/etcd.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,20 @@ local base64_encode = require("base64").encode
local dkjson = require("dkjson")
local util = require("apisix.cli.util")
local file = require("apisix.cli.file")
local http = require("socket.http")
local ltn12 = require("ltn12")

local type = type
local ipairs = ipairs
local print = print
local tonumber = tonumber
local str_format = string.format
local table_concat = table.concat

local _M = {}

-- Timeout for all I/O operations
http.TIMEOUT = 3

local function parse_semantic_version(ver)
local errmsg = "invalid semantic version: " .. ver
Expand Down Expand Up @@ -106,9 +111,6 @@ function _M.init(env, show_output)

local etcd_conf = yaml_conf.etcd

local timeout = etcd_conf.timeout or 3
local uri

-- convert old single etcd config to multiple etcd config
if type(yaml_conf.etcd.host) == "string" then
yaml_conf.etcd.host = {yaml_conf.etcd.host}
Expand All @@ -132,22 +134,23 @@ function _M.init(env, show_output)

-- check the etcd cluster version
for index, host in ipairs(yaml_conf.etcd.host) do
uri = host .. "/version"
local cmd = str_format("curl -s -m %d %s", timeout * 2, uri)
local res = util.execute_cmd(cmd)
local errmsg = str_format("got malformed version message: \"%s\" from etcd\n",
res)
local version_url = host .. "/version"
local errmsg

local body, _, err = dkjson.decode(res)
if err then
local res, err = http.request(version_url)
if err and type(err) == "string" then
errmsg = str_format("request etcd endpoint \'%s\' error, %s\n", host, err)
util.die(errmsg)
end

local cluster_version = body["etcdcluster"]
if not cluster_version then
local body, _, err = dkjson.decode(res)
if err or (body and not body["etcdcluster"]) then
errmsg = str_format("got malformed version message: \"%s\" from etcd \"%s\"\n", res,
host)
util.die(errmsg)
end

local cluster_version = body["etcdcluster"]
if compare_semantic_version(cluster_version, env.min_etcd_version) then
util.die("etcd cluster version ", cluster_version,
" is less than the required version ",
Expand All @@ -160,27 +163,39 @@ function _M.init(env, show_output)
for index, host in ipairs(yaml_conf.etcd.host) do
local is_success = true

local token_head = ""
local errmsg
local auth_token
local user = yaml_conf.etcd.user
local password = yaml_conf.etcd.password
if user and password then
local uri_auth = host .. "/v3/auth/authenticate"
local auth_url = host .. "/v3/auth/authenticate"
local json_auth = {
name = etcd_conf.user,
password = etcd_conf.password
}

local post_json_auth = dkjson.encode(json_auth)
local cmd_auth = "curl -s " .. uri_auth .. " -X POST -d '" ..
post_json_auth .. "' --connect-timeout " .. timeout
.. " --max-time " .. timeout * 2 .. " --retry 1 2>&1"
local response_body = {}
local _, err = http.request{url = auth_url, method = "POST",
source = ltn12.source.string(post_json_auth),
sink = ltn12.sink.table(response_body),
headers = {["Content-Length"] = #post_json_auth}}

-- err is string type
if err and type(err) == "string" then
errmsg = str_format("request etcd endpoint \"%s\" error, %s\n", host, err)
util.die(errmsg)
end

local res_auth = util.execute_cmd(cmd_auth)
local res_auth = table_concat(response_body)
local body_auth, _, err_auth = dkjson.decode(res_auth)
if err_auth then
util.die(cmd_auth, "\n", res_auth)
if err_auth or (body_auth and not body_auth.token) then
errmsg = str_format("got malformed auth message: \"%s\" from etcd \"%s\"\n",
res_auth, host)
util.die(errmsg)
end

token_head = " -H 'Authorization: " .. body_auth.token .. "'"
auth_token = body_auth.token
end


Expand All @@ -191,26 +206,36 @@ function _M.init(env, show_output)

local key = (etcd_conf.prefix or "") .. dir_name .. "/"

local uri = host .. "/v3/kv/put"
local put_url = host .. "/v3/kv/put"
local post_json = '{"value":"' .. base64_encode("init_dir")
.. '", "key":"' .. base64_encode(key) .. '"}'
local cmd = "curl " .. uri .. token_head .. " -X POST -d '" .. post_json
.. "' --connect-timeout " .. timeout
.. " --max-time " .. timeout * 2 .. " --retry 1 2>&1"
local response_body = {}
local headers = {["Content-Length"] = #post_json}
if auth_token then
headers["Authorization"] = auth_token
end

local res = util.execute_cmd(cmd)
if res:find("error", 1, true) then
local _, err = http.request{url = put_url, method = "POST",
source = ltn12.source.string(post_json),
sink = ltn12.sink.table(response_body),
headers = headers}
if err and type(err) == "string" then
errmsg = str_format("request etcd endpoint \"%s\" error, %s\n", host, err)
util.die(errmsg)
end
local res_put = table_concat(response_body)
if res_put:find("error", 1, true) then
is_success = false
if (index == host_count) then
util.die(cmd, "\n", res)
errmsg = str_format("got malformed key-put message: \"%s\" from etcd\n",
res_put)
util.die(errmsg)
end

break
end

if show_output then
print(cmd)
print(res)
print(res_put)
end
end

Expand Down
1 change: 1 addition & 0 deletions rockspec/apisix-master-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ dependencies = {
"dkjson = 2.5-2",
"resty-redis-cluster = 1.02-4",
"lua-resty-expr = 1.0.0",
"luasocket = 3.0rc1-2",
}

build = {
Expand Down

0 comments on commit 5782cc7

Please sign in to comment.