Commit 04f9d8eb authored by Jelte Jansen's avatar Jelte Jansen
Browse files

[master] Merge branch 'trac1678'

parents 90e30a79 bb52e879
......@@ -301,12 +301,14 @@ void kqueue_reactor::run(bool block, op_queue<operation>& ops)
EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data);
else
continue;
break;
case EVFILT_WRITE:
if (!descriptor_data->op_queue_[write_op].empty())
ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_WRITE,
EV_ADD | EV_ONESHOT, 0, 0, descriptor_data);
else
continue;
break;
default:
break;
}
......
......@@ -282,15 +282,26 @@ int close(socket_type s, state_type& state,
int result = 0;
if (s != invalid_socket)
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
if ((state & non_blocking) && (state & user_set_linger))
if (destruction && (state & user_set_linger))
{
ioctl_arg_type arg = 0;
::ioctlsocket(s, FIONBIO, &arg);
state &= ~non_blocking;
::linger opt;
opt.l_onoff = 0;
opt.l_linger = 0;
asio::error_code ignored_ec;
socket_ops::setsockopt(s, state, SOL_SOCKET,
SO_LINGER, &opt, sizeof(opt), ignored_ec);
}
clear_last_error();
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
result = error_wrapper(::closesocket(s), ec);
#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
if (state & non_blocking)
result = error_wrapper(::close(s), ec);
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
if (result != 0
&& (ec == asio::error::would_block
|| ec == asio::error::try_again))
{
#if defined(__SYMBIAN32__)
int flags = ::fcntl(s, F_GETFL, 0);
......@@ -301,18 +312,6 @@ int close(socket_type s, state_type& state,
::ioctl(s, FIONBIO, &arg);
#endif // defined(__SYMBIAN32__)
state &= ~non_blocking;
}
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
if (destruction && (state & user_set_linger))
{
::linger opt;
opt.l_onoff = 0;
opt.l_linger = 0;
asio::error_code ignored_ec;
socket_ops::setsockopt(s, state, SOL_SOCKET,
SO_LINGER, &opt, sizeof(opt), ignored_ec);
}
clear_last_error();
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
......@@ -320,6 +319,7 @@ int close(socket_type s, state_type& state,
#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
result = error_wrapper(::close(s), ec);
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
}
}
if (result == 0)
......
{"version": 2, "Auth": {"listen_on": [{"port": 47806, "address": "0.0.0.0"}]}, "Boss": {"components": {"b10-auth-2": {"kind": "needed", "special": "auth"}, "b10-auth": {"kind": "needed", "special": "auth"}, "b10-cmdctl": {"kind": "needed", "special": "cmdctl"}}}}
Feature: Multiple instances
This feature tests whether multiple instances can be run, and whether
removing them does not affect the running of other instances
Scenario: Multiple instances of Auth
# This config should have two running instances
Given I have bind10 running with configuration multi_instance/multi_auth.config
And bind10 module Auth should be running
A query for example.com should have rcode REFUSED
# this also checks whether the process is running
If I remember the pid of process b10-auth
And remember the pid of process b10-auth-2
When I remove bind10 configuration Boss/components value b10-auth-2
Then the pid of process b10-auth should not have changed
And a query for example.com should have rcode REFUSED
When I send bind10 the following commands
"""
config add Boss/components b10-auth-2
config set Boss/components/b10-auth-2/special auth
config set Boss/components/b10-auth-2/kind needed
config commit
"""
And wait for new bind10 stderr message AUTH_SERVER_STARTED
And remember the pid of process b10-auth-2
Then the pid of process b10-auth should not have changed
A query for example.com should have rcode REFUSED
When I remove bind10 configuration Boss/components value b10-auth
Then the pid of process b10-auth-2 should not have changed
A query for example.com should have rcode REFUSED
......@@ -16,6 +16,7 @@
from lettuce import *
import subprocess
import re
import json
@step('start bind10(?: with configuration (\S+))?' +\
'(?: with cmdctl port (\d+))?' +\
......@@ -167,6 +168,83 @@ def check_bindctl_output(step, stderr, notv, string):
"' was found in bindctl output:\n" +\
output
def parse_bindctl_output_as_data_structure():
"""Helper function for data-related command tests: evaluates the
last output of bindctl as a data structure that can then be
inspected.
If the bindctl output is not valid (json) data, this call will
fail with an assertion failure.
If it is valid, it is parsed and returned as whatever data
structure it represented.
"""
# strip any extra output after a charater that commonly terminates a valid
# JSON expression, i.e., ']', '}' and '"'. (The extra output would
# contain 'Exit from bindctl' message, and depending on environment some
# other control-like characters...but why is this message even there?)
# Note that this filter is not perfect. For example, it cannot recognize
# a simple expression of true/false/null.
output = re.sub("(.*)([^]}\"]*$)", r"\1", world.last_bindctl_stdout)
try:
return json.loads(output)
except ValueError as ve:
assert False, "Last bindctl output does not appear to be a " +\
"parseable data structure: '" + output + "': " + str(ve)
def find_process_pid(step, process_name):
"""Helper function to request the running processes from Boss, and
return the pid of the process with the given process_name.
Fails with an assert if the response from boss is not valid JSON,
or if the process with the given name is not found.
"""
# show_processes output is a list of lists, where the inner lists
# are of the form [ pid, "name" ]
# Not checking data form; errors will show anyway (if these turn
# out to be too vague, we can change this)
step.given('send bind10 the command Boss show_processes')
running_processes = parse_bindctl_output_as_data_structure()
for process in running_processes:
if process[1] == process_name:
return process[0]
assert False, "Process named " + process_name +\
" not found in output of Boss show_processes";
@step("remember the pid of process ([\S]+)")
def remember_pid(step, process_name):
"""Stores the PID of the process with the given name as returned by
Boss show_processes command.
Fails if the process with the given name does not appear to exist.
Stores the component_name->pid value in the dict world.process_pids.
This should only be used by the related step
'the pid of process <name> should (not) have changed'
Arguments:
process name ('process <name>') the name of the component to store
the pid of.
"""
if world.process_pids is None:
world.process_pids = {}
world.process_pids[process_name] = find_process_pid(step, process_name)
@step('pid of process ([\S]+) should not have changed')
def check_pid(step, process_name):
"""Checks the PID of the process with the given name as returned by
Boss show_processes command.
Fails if the process with the given name does not appear to exist.
Fails if the process with the given name exists, but has a different
pid than it had when the step 'remember the pid of process' was
called.
Fails if that step has not been called (since world.process_pids
does not exist).
"""
assert world.process_pids is not None, "No process pids stored"
assert process_name in world.process_pids, "Process named " +\
process_name +\
" was not stored"
pid = find_process_pid(step, process_name)
assert world.process_pids[process_name] == pid,\
"Expected pid: " + str(world.process_pids[process_name]) +\
" Got pid: " + str(pid)
@step('set bind10 configuration (\S+) to (.*)(?: with cmdctl port (\d+))?')
def config_set_command(step, name, value, cmdctl_port):
"""
......@@ -183,6 +261,20 @@ def config_set_command(step, name, value, cmdctl_port):
"quit"]
run_bindctl(commands, cmdctl_port)
@step('send bind10 the following commands(?: with cmdctl port (\d+))?')
def send_multiple_commands(step, cmdctl_port):
"""
Run bindctl, and send it the given multiline set of commands.
A quit command is always appended.
cmdctl_port ('with cmdctl port <portnr>', optional): cmdctl port to send
the command to. Defaults to 47805.
Fails if cmdctl does not exit with status code 0.
"""
commands = step.multiline.split("\n")
# Always add quit
commands.append("quit")
run_bindctl(commands, cmdctl_port)
@step('remove bind10 configuration (\S+)(?: value (\S+))?(?: with cmdctl port (\d+))?')
def config_remove_command(step, name, value, cmdctl_port):
"""
......
......@@ -45,7 +45,9 @@ copylist = [
["configurations/example.org.config.orig",
"configurations/example.org.config"],
["configurations/resolver/resolver_basic.config.orig",
"configurations/resolver/resolver_basic.config"]
"configurations/resolver/resolver_basic.config"],
["configurations/multi_instance/multi_auth.config.orig",
"configurations/multi_instance/multi_auth.config"]
]
# This is a list of files that, if present, will be removed before a scenario
......@@ -343,6 +345,10 @@ def initialize(scenario):
# Convenience variable to access the last query result from querying.py
world.last_query_result = None
# For slightly better errors, initialize a process_pids for the relevant
# steps
world.process_pids = None
# Some tests can modify the settings. If the tests fail half-way, or
# don't clean up, this can leave configurations or data in a bad state,
# so we copy them from originals before each scenario
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment