summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2017-01-13 09:31:03 +0100
committerLukas Slebodnik <lslebodn@redhat.com>2017-03-14 13:31:47 +0100
commit91b0592cdab22915dff27ceae6d8e49c608aea4a (patch)
tree0dbfa2bb14a1c1a0fb23f38318ea103126d5cbe2 /src
parentca90f2102a43a3d49a2ef26610d7b4ff3062a823 (diff)
downloadsssd-91b0592cdab22915dff27ceae6d8e49c608aea4a.tar.gz
sssd-91b0592cdab22915dff27ceae6d8e49c608aea4a.tar.xz
sssd-91b0592cdab22915dff27ceae6d8e49c608aea4a.zip
TESTS: test the curl wrapper with a command-line tool
In order to test the curl integration code, this patch adds a command-line tool and tests that it's possible to drive a conversation with the secrets responder using the tool. Reviewed-by: Pavel Březina <pbrezina@redhat.com> Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
Diffstat (limited to 'src')
-rw-r--r--src/tests/intg/Makefile.am1
-rw-r--r--src/tests/intg/config.py.m41
-rw-r--r--src/tests/intg/test_secrets.py143
-rw-r--r--src/tests/tcurl_test_tool.c230
4 files changed, 374 insertions, 1 deletions
diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am
index 64f90b490..1d36fa0d2 100644
--- a/src/tests/intg/Makefile.am
+++ b/src/tests/intg/Makefile.am
@@ -38,6 +38,7 @@ config.py: config.py.m4
-D "secdbpath=\`$(secdbpath)'" \
-D "libexecpath=\`$(libexecdir)'" \
-D "runstatedir=\`$(runstatedir)'" \
+ -D "abs_builddir=\`$(abs_builddir)'" \
$< > $@
root:
diff --git a/src/tests/intg/config.py.m4 b/src/tests/intg/config.py.m4
index 65e17e55a..841aae01f 100644
--- a/src/tests/intg/config.py.m4
+++ b/src/tests/intg/config.py.m4
@@ -15,3 +15,4 @@ MCACHE_PATH = "mcpath"
SECDB_PATH = "secdbpath"
LIBEXEC_PATH = "libexecpath"
RUNSTATEDIR = "runstatedir"
+ABS_BUILDDIR = "abs_builddir"
diff --git a/src/tests/intg/test_secrets.py b/src/tests/intg/test_secrets.py
index 36f43cd7a..cbc1a1f06 100644
--- a/src/tests/intg/test_secrets.py
+++ b/src/tests/intg/test_secrets.py
@@ -76,6 +76,7 @@ def create_sssd_secrets_fixture(request):
for secdb_file in os.listdir(config.SECDB_PATH):
os.unlink(config.SECDB_PATH + "/" + secdb_file)
request.addfinalizer(sec_teardown)
+ return secpid
@pytest.fixture
@@ -102,13 +103,27 @@ def setup_for_secrets(request):
return None
+def get_secrets_socket():
+ return os.path.join(config.RUNSTATEDIR, "secrets.socket")
+
+
@pytest.fixture
def secrets_cli(request):
- sock_path = os.path.join(config.RUNSTATEDIR, "secrets.socket")
+ sock_path = get_secrets_socket()
cli = SecretsLocalClient(sock_path=sock_path)
return cli
+@pytest.fixture
+def curlwrap_tool(request):
+ curlwrap_path = os.path.join(config.ABS_BUILDDIR,
+ "..", "..", "..", "tcurl-test-tool")
+ if os.access(curlwrap_path, os.X_OK):
+ return curlwrap_path
+
+ return None
+
+
def test_crd_ops(setup_for_secrets, secrets_cli):
"""
Test that the basic Create, Retrieve, Delete operations work
@@ -178,6 +193,132 @@ def test_crd_ops(setup_for_secrets, secrets_cli):
assert str(err413.value).startswith("413")
+def run_curlwrap_tool(args, exp_http_code):
+ cmd = subprocess.Popen(args,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out, _ = cmd.communicate()
+
+ assert cmd.returncode == 0
+
+ out = out.decode('utf-8')
+ exp_http_code_str = "Request HTTP code: %d" % exp_http_code
+ assert exp_http_code_str in out
+
+ return out
+
+
+def test_curlwrap_crd_ops(setup_for_secrets,
+ curlwrap_tool):
+ """
+ Test that the basic Create, Retrieve, Delete operations work using our
+ tevent libcurl code
+ """
+ if not curlwrap_tool:
+ pytest.skip("The tcurl tool is not available, skipping test")
+ sock_path = get_secrets_socket()
+
+ # listing an empty DB yields a 404
+ run_curlwrap_tool([curlwrap_tool,
+ '-v', '-s', sock_path,
+ 'http://localhost/secrets/'],
+ 404)
+
+ # listing a non-existent secret yields a 404
+ run_curlwrap_tool([curlwrap_tool,
+ '-v', '-s', sock_path,
+ 'http://localhost/secrets/foo'],
+ 404)
+
+ # set a secret foo:bar
+ run_curlwrap_tool([curlwrap_tool, '-p',
+ '-v', '-s', sock_path,
+ 'http://localhost/secrets/foo',
+ 'bar'],
+ 200)
+
+ # list secrets
+ output = run_curlwrap_tool([curlwrap_tool,
+ '-v', '-s', sock_path,
+ 'http://localhost/secrets/'],
+ 200)
+ assert "foo" in output
+
+ # get the foo secret
+ output = run_curlwrap_tool([curlwrap_tool,
+ '-v', '-s', sock_path,
+ 'http://localhost/secrets/foo'],
+ 200)
+ assert "bar" in output
+
+ # Overwriting a secret is an error
+ run_curlwrap_tool([curlwrap_tool, '-p',
+ '-v', '-s', sock_path,
+ 'http://localhost/secrets/foo',
+ 'baz'],
+ 409)
+
+ # Delete a secret
+ run_curlwrap_tool([curlwrap_tool, '-d',
+ '-v', '-s', sock_path,
+ 'http://localhost/secrets/foo'],
+ 200)
+
+ # Delete a non-existent secret must yield a 404
+ run_curlwrap_tool([curlwrap_tool, '-d',
+ '-v', '-s', sock_path,
+ 'http://localhost/secrets/foo'],
+ 404)
+
+
+def test_curlwrap_parallel(setup_for_secrets,
+ curlwrap_tool):
+ """
+ The tevent libcurl wrapper is meant to be non-blocking. Test
+ its operation in parallel.
+ """
+ if not curlwrap_tool:
+ pytest.skip("The tcurl tool is not available, skipping test")
+ sock_path = get_secrets_socket()
+
+ secrets = dict()
+ nsecrets = 10
+
+ for i in range(0, nsecrets):
+ secrets["key" + str(i)] = "value" + str(i)
+
+ args = [curlwrap_tool, '-p', '-v', '-s', sock_path]
+ for skey, svalue in secrets.items():
+ args.extend(['http://localhost/secrets/%s' % skey, svalue])
+ run_curlwrap_tool(args, 200)
+
+ output = run_curlwrap_tool([curlwrap_tool,
+ '-v', '-s', sock_path,
+ 'http://localhost/secrets/'],
+ 200)
+ for skey in secrets:
+ assert skey in output
+
+ args = [curlwrap_tool, '-g', '-v', '-s', sock_path]
+ for skey in secrets:
+ args.extend(['http://localhost/secrets/%s' % skey])
+ output = run_curlwrap_tool(args, 200)
+
+ for svalue in secrets.values():
+ assert svalue in output
+
+ args = [curlwrap_tool, '-d', '-v', '-s', sock_path]
+ for skey in secrets:
+ args.extend(['http://localhost/secrets/%s' % skey])
+ output = run_curlwrap_tool(args, 200)
+
+ run_curlwrap_tool([curlwrap_tool,
+ '-v', '-s', sock_path,
+ 'http://localhost/secrets/'],
+ 404)
+
+
def test_containers(setup_for_secrets, secrets_cli):
"""
Test that storing secrets inside containers works
diff --git a/src/tests/tcurl_test_tool.c b/src/tests/tcurl_test_tool.c
new file mode 100644
index 000000000..35ea97978
--- /dev/null
+++ b/src/tests/tcurl_test_tool.c
@@ -0,0 +1,230 @@
+/*
+ SSSD
+
+ libcurl tevent integration test tool
+
+ Copyright (C) Red Hat, 2016
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <popt.h>
+
+#include "util/util.h"
+#include "util/tev_curl.h"
+
+#define MAXREQ 64
+
+struct tool_ctx {
+ bool verbose;
+
+ errno_t error;
+ bool done;
+
+ size_t nreqs;
+};
+
+static void request_done(struct tevent_req *req)
+{
+ int http_code;
+ struct sss_iobuf *outbuf;
+ struct tool_ctx *tool_ctx = tevent_req_callback_data(req,
+ struct tool_ctx);
+
+ tool_ctx->error = tcurl_http_recv(tool_ctx, req,
+ &http_code,
+ &outbuf);
+ talloc_zfree(req);
+
+ if (tool_ctx->error != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "HTTP request failed: %d\n", tool_ctx->error);
+ tool_ctx->done = true;
+ return;
+ } else if (tool_ctx->verbose) {
+ printf("Request HTTP code: %d\n", http_code);
+ printf("Request HTTP body: \n%s\n",
+ (const char *) sss_iobuf_get_data(outbuf));
+ talloc_zfree(outbuf);
+ }
+
+ tool_ctx->nreqs--;
+ if (tool_ctx->nreqs == 0) {
+ tool_ctx->done = true;
+ }
+}
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ poptContext pc;
+
+ int pc_debug = 0;
+ int pc_verbose = 0;
+ const char *socket_path = NULL;
+ const char *extra_arg_ptr;
+
+ static const char *headers[] = {
+ "Content-type: application/octet-stream",
+ NULL,
+ };
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "debug", '\0', POPT_ARG_INT, &pc_debug, 0,
+ "The debug level to run with", NULL },
+ { "socket-path", 's', POPT_ARG_STRING, &socket_path, 0,
+ "The path to the HTTP server socket", NULL },
+ { "get", 'g', POPT_ARG_NONE, NULL, 'g', "Perform a HTTP GET (default)", NULL },
+ { "put", 'p', POPT_ARG_NONE, NULL, 'p', "Perform a HTTP PUT", NULL },
+ { "del", 'd', POPT_ARG_NONE, NULL, 'd', "Perform a HTTP DELETE", NULL },
+ { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "Print response code and body", NULL },
+ POPT_TABLEEND
+ };
+
+ struct tevent_req *req;
+ struct tevent_context *ev;
+ enum tcurl_http_request req_type = TCURL_HTTP_GET;
+ struct tcurl_ctx *ctx;
+ struct tool_ctx *tool_ctx;
+
+ const char *urls[MAXREQ] = { 0 };
+ struct sss_iobuf **inbufs;
+
+ size_t n_reqs = 0;
+
+ debug_prg_name = argv[0];
+ pc = poptGetContext(NULL, argc, argv, long_options, 0);
+ poptSetOtherOptionHelp(pc, "HTTPDATA");
+
+ while ((opt = poptGetNextOpt(pc)) > 0) {
+ switch (opt) {
+ case 'g':
+ req_type = TCURL_HTTP_GET;
+ break;
+ case 'p':
+ req_type = TCURL_HTTP_PUT;
+ break;
+ case 'd':
+ req_type = TCURL_HTTP_DELETE;
+ break;
+ case 'v':
+ pc_verbose = 1;
+ break;
+ default:
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unexpected option\n");
+ return 1;
+ }
+ }
+
+ DEBUG_CLI_INIT(pc_debug);
+
+ tool_ctx = talloc_zero(NULL, struct tool_ctx);
+ if (tool_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not init tool context\n");
+ return 1;
+ }
+
+ inbufs = talloc_zero_array(tool_ctx, struct sss_iobuf *, MAXREQ);
+ if (inbufs == NULL) {
+ talloc_zfree(tool_ctx);
+ return 1;
+ }
+
+ while ((extra_arg_ptr = poptGetArg(pc)) != NULL) {
+ switch (req_type) {
+ case TCURL_HTTP_GET:
+ case TCURL_HTTP_DELETE:
+ urls[n_reqs++] = extra_arg_ptr;
+ break;
+ case TCURL_HTTP_PUT:
+ if (urls[n_reqs] == NULL) {
+ urls[n_reqs] = extra_arg_ptr;
+ } else {
+ inbufs[n_reqs] = sss_iobuf_init_readonly(
+ inbufs,
+ (uint8_t *) discard_const(extra_arg_ptr),
+ strlen(extra_arg_ptr));
+ if (inbufs[n_reqs] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not init input buffer\n");
+ talloc_zfree(tool_ctx);
+ return 1;
+ }
+ n_reqs++;
+ }
+ break;
+ }
+ }
+
+ if (opt != -1) {
+ poptPrintUsage(pc, stderr, 0);
+ fprintf(stderr, "%s", poptStrerror(opt));
+ talloc_zfree(tool_ctx);
+ return 1;
+ }
+
+ if (!socket_path) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Please specify the socket path\n");
+ poptPrintUsage(pc, stderr, 0);
+ talloc_zfree(tool_ctx);
+ return 1;
+ }
+
+ tool_ctx->nreqs = n_reqs;
+ tool_ctx->verbose = !!pc_verbose;
+
+ ev = tevent_context_init(tool_ctx);
+ if (ev == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not init tevent context\n");
+ talloc_zfree(tool_ctx);
+ return 1;
+ }
+
+ ctx = tcurl_init(tool_ctx, ev);
+ if (ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Could not init tcurl context\n");
+ talloc_zfree(tool_ctx);
+ return 1;
+ }
+
+ for (size_t i = 0; i < n_reqs; i++) {
+ req = tcurl_http_send(tool_ctx, ev, ctx,
+ req_type,
+ socket_path,
+ urls[i],
+ headers,
+ inbufs[i],
+ 10);
+ if (ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Could not create request\n");
+ talloc_zfree(tool_ctx);
+ return 1;
+ }
+ tevent_req_set_callback(req, request_done, tool_ctx);
+ }
+
+ while (tool_ctx->done == false) {
+ tevent_loop_once(ev);
+ }
+
+ if (tool_ctx->nreqs > 0) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "The tool finished with some pending requests, fail!\n");
+ talloc_zfree(tool_ctx);
+ return 1;
+ }
+
+ talloc_free(tool_ctx);
+ poptFreeContext(pc);
+ return 0;
+}