util/prosodyctl/check.lua
changeset 11831 2359519260ec
parent 11830 e1c4cc5d0ef8
child 11837 bd86ab8122d9
equal deleted inserted replaced
11830:e1c4cc5d0ef8 11831:2359519260ec
     2 local show_usage = require "util.prosodyctl".show_usage;
     2 local show_usage = require "util.prosodyctl".show_usage;
     3 local show_warning = require "util.prosodyctl".show_warning;
     3 local show_warning = require "util.prosodyctl".show_warning;
     4 local is_prosody_running = require "util.prosodyctl".isrunning;
     4 local is_prosody_running = require "util.prosodyctl".isrunning;
     5 local dependencies = require "util.dependencies";
     5 local dependencies = require "util.dependencies";
     6 local socket = require "socket";
     6 local socket = require "socket";
       
     7 local socket_url = require "socket.url";
     7 local jid_split = require "util.jid".prepped_split;
     8 local jid_split = require "util.jid".prepped_split;
     8 local modulemanager = require "core.modulemanager";
     9 local modulemanager = require "core.modulemanager";
       
    10 local async = require "util.async";
       
    11 local httputil = require "util.http";
     9 
    12 
    10 local function check_ojn(check_type, target_host)
    13 local function check_ojn(check_type, target_host)
    11 	local http = require "net.http"; -- .new({});
    14 	local http = require "net.http"; -- .new({});
    12 	local urlencode = require "util.http".urlencode;
       
    13 	local json = require "util.json";
    15 	local json = require "util.json";
    14 
    16 
    15 	local response, err = async.wait_for(http.request(
    17 	local response, err = async.wait_for(http.request(
    16 		("https://observe.jabber.network/api/v1/check/%s"):format(urlencode(check_type)),
    18 		("https://observe.jabber.network/api/v1/check/%s"):format(httputil.urlencode(check_type)),
    17 		{
    19 		{
    18 			method="POST",
    20 			method="POST",
    19 			headers={["Accept"] = "application/json"; ["Content-Type"] = "application/json"},
    21 			headers={["Accept"] = "application/json"; ["Content-Type"] = "application/json"},
    20 			body=json.encode({target=target_host}),
    22 			body=json.encode({target=target_host}),
    21 		}));
    23 		}));
    33 		return false, ("Failed to parse API JSON: %s"):format(err)
    35 		return false, ("Failed to parse API JSON: %s"):format(err)
    34 	end
    36 	end
    35 
    37 
    36 	local success = decoded_body["success"];
    38 	local success = decoded_body["success"];
    37 	return success == true, nil;
    39 	return success == true, nil;
       
    40 end
       
    41 
       
    42 local function check_probe(base_url, probe_module, target)
       
    43 	local http = require "net.http"; -- .new({});
       
    44 	local params = httputil.formencode({ module = probe_module; target = target })
       
    45 	local response, err = async.wait_for(http.request(base_url .. "?" .. params));
       
    46 
       
    47 	if not response then return false, err; end
       
    48 
       
    49 	if response.code ~= 200 then return false, ("API replied with non-200 code: %d"):format(response.code); end
       
    50 
       
    51 	for line in response.body:gmatch("[^\r\n]+") do
       
    52 		local probe_success = line:match("^probe_success%s+(%d+)");
       
    53 
       
    54 		if probe_success == "1" then
       
    55 			return true;
       
    56 		elseif probe_success == "0" then
       
    57 			return false;
       
    58 		end
       
    59 	end
       
    60 	return false, "Probe endpoint did not return a success status";
    38 end
    61 end
    39 
    62 
    40 local function skip_bare_jid_hosts(host)
    63 local function skip_bare_jid_hosts(host)
    41 	if jid_split(host) then
    64 	if jid_split(host) then
    42 		-- See issue #779
    65 		-- See issue #779
   739 			print("Prosody does not appear to be running, which is required for this test.");
   762 			print("Prosody does not appear to be running, which is required for this test.");
   740 			print("Start it and then try again.");
   763 			print("Start it and then try again.");
   741 			return 1;
   764 			return 1;
   742 		end
   765 		end
   743 
   766 
       
   767 		local checker = "observe.jabber.network";
       
   768 		local probe_instance;
       
   769 		local probe_modules = {
       
   770 			["xmpp-client"] = "c2s_normal_auth";
       
   771 			["xmpp-server"] = "s2s_normal";
       
   772 			["xmpps-client"] = nil; -- TODO
       
   773 			["xmpps-server"] = nil; -- TODO
       
   774 		};
       
   775 		local probe_settings = configmanager.get("*", "connectivity_probe");
       
   776 		if type(probe_settings) == "string" then
       
   777 			probe_instance = probe_settings;
       
   778 		elseif type(probe_settings) == "table" and type(probe_settings.url) == "string" then
       
   779 			probe_instance = probe_settings.url;
       
   780 			if type(probe_settings.modules) == "table" then
       
   781 				probe_modules = probe_settings.modules;
       
   782 			end
       
   783 		elseif probe_settings ~= nil then
       
   784 			print("The 'connectivity_probe' setting not understood.");
       
   785 			print("Expected an URL or a table with 'url' and 'modules' fields");
       
   786 			print("See https://prosody.im/doc/prosodyctl#check for more information."); -- FIXME
       
   787 			return 1;
       
   788 		end
       
   789 
       
   790 		local check_api;
       
   791 		if probe_instance then
       
   792 			local parsed_url = socket_url.parse(probe_instance);
       
   793 			if not parsed_url then
       
   794 				print(("'connectivity_probe' is not a valid URL: %q"):format(probe_instance));
       
   795 				print("Set it to the URL of an XMPP Blackbox Exporter instance and try again");
       
   796 				return 1;
       
   797 			end
       
   798 			checker = parsed_url.host;
       
   799 
       
   800 			function check_api(protocol, host)
       
   801 				local target = socket_url.build({scheme="xmpp",path=host});
       
   802 				local probe_module = probe_modules[protocol];
       
   803 				if not probe_module then
       
   804 					return nil, "Checking protocol '"..protocol.."' is currently unsupported";
       
   805 				end
       
   806 				return check_probe(probe_instance, probe_module, target);
       
   807 			end
       
   808 		else
       
   809 			check_api = check_ojn;
       
   810 		end
       
   811 
   744 		for host in it.filter(skip_bare_jid_hosts, enabled_hosts()) do
   812 		for host in it.filter(skip_bare_jid_hosts, enabled_hosts()) do
   745 			local modules, component_module = modulemanager.get_modules_for_host(host);
   813 			local modules, component_module = modulemanager.get_modules_for_host(host);
   746 			if component_module then
   814 			if component_module then
   747 				modules:add(component_module)
   815 				modules:add(component_module)
   748 			end
   816 			end
   749 
   817 
   750 			print("Checking external connectivity for "..host.." via observe.jabber.network")
   818 			print("Checking external connectivity for "..host.." via "..checker)
   751 			local function check_connectivity(protocol)
   819 			local function check_connectivity(protocol)
   752 				local success, err = check_api(protocol, host);
   820 				local success, err = check_api(protocol, host);
   753 				if not success and err ~= nil then
   821 				if not success and err ~= nil then
   754 					print(("  %s: Failed to request check at API: %s"):format(protocol, err))
   822 					print(("  %s: Failed to request check at API: %s"):format(protocol, err))
   755 				elseif success then
   823 				elseif success then