From fa6501f87041ca3d3a239988d4b1ae03d7442700 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Fri, 13 Nov 2020 12:35:26 +0200 Subject: refactor: split obnam's bindings, functions for clarity The old subplot/obnam.{yaml,py} were starting to get large enough that it was hard to understand them. Also, were partly overlapping in functionality with runcmd. --- obnam.md | 32 +++--- src/bin/obnam-server.rs | 4 +- subplot/client.py | 60 ++++++++++ subplot/client.yaml | 14 +++ subplot/data.py | 18 +++ subplot/data.yaml | 10 ++ subplot/obnam.py | 290 ------------------------------------------------ subplot/obnam.yaml | 77 ------------- subplot/server.py | 178 +++++++++++++++++++++++++++++ subplot/server.yaml | 39 +++++++ 10 files changed, 341 insertions(+), 381 deletions(-) create mode 100644 subplot/client.py create mode 100644 subplot/client.yaml create mode 100644 subplot/data.py create mode 100644 subplot/data.yaml delete mode 100644 subplot/obnam.py delete mode 100644 subplot/obnam.yaml create mode 100644 subplot/server.py create mode 100644 subplot/server.yaml diff --git a/obnam.md b/obnam.md index 21511de..b3646d3 100644 --- a/obnam.md +++ b/obnam.md @@ -222,7 +222,8 @@ it, and verify the results, and finally terminate the server. We must be able to create a new chunk. ~~~scenario -given a chunk server +given an installed obnam +and a running chunk server and a file data.dat containing some random data when I POST data.dat to /chunks, with chunk-meta: {"sha256":"abc"} then HTTP status code is 201 @@ -271,7 +272,8 @@ We must get the right error if we try to retrieve a chunk that does not exist. ~~~scenario -given a chunk server +given an installed obnam +and a running chunk server when I try to GET /chunks/any.random.string then HTTP status code is 404 ~~~ @@ -281,7 +283,8 @@ then HTTP status code is 404 We must get an empty result if searching for chunks that don't exist. ~~~scenario -given a chunk server +given an installed obnam +and a running chunk server when I GET /chunks?sha256=abc then HTTP status code is 200 and content-type is application/json @@ -293,7 +296,8 @@ and the JSON body matches {} We must get the right error when deleting a chunk that doesn't exist. ~~~scenario -given a chunk server +given an installed obnam +and a running chunk server when I try to DELETE /chunks/any.random.string then HTTP status code is 404 ~~~ @@ -307,15 +311,13 @@ possible, but still useful requirement for a backup system. ~~~scenario given an installed obnam -and a chunk server +and a running chunk server and a client config based on smoke.yaml and a file live/data.dat containing some random data -when I invoke obnam backup smoke.yaml -then backup command is successful -and backup generation is GEN -when I invoke obnam list smoke.yaml -then backup command is successful -and generation list contains +when I run obnam backup smoke.yaml +then backup generation is GEN +when I run obnam list smoke.yaml +then generation list contains when I invoke obnam restore smoke.yaml restore.db rest then data in live and rest match ~~~ @@ -377,10 +379,14 @@ title: "Obnam2—a backup system" author: Lars Wirzenius documentclass: report bindings: - - subplot/obnam.yaml + - subplot/server.yaml + - subplot/client.yaml + - subplot/data.yaml - subplot/vendored/runcmd.yaml functions: - - subplot/obnam.py + - subplot/server.py + - subplot/client.py + - subplot/data.py - subplot/daemon.py - subplot/vendored/runcmd.py classes: diff --git a/src/bin/obnam-server.rs b/src/bin/obnam-server.rs index 4e53953..520d6a9 100644 --- a/src/bin/obnam-server.rs +++ b/src/bin/obnam-server.rs @@ -32,7 +32,8 @@ async fn main() -> anyhow::Result<()> { let index = warp::any().map(move || Arc::clone(&index)); info!("Obnam server starting up"); - debug!("Configuration: {:?}", config_bare); + debug!("opt: {:#?}", opt); + debug!("Configuration: {:#?}", config_bare); let create = warp::post() .and(warp::path("chunks")) @@ -65,6 +66,7 @@ async fn main() -> anyhow::Result<()> { let log = warp::log("obnam"); let webroot = create.or(fetch).or(search).or(delete).with(log); + debug!("starting warp"); warp::serve(webroot) // .tls() // .key_path(config_bare.tls_key) diff --git a/subplot/client.py b/subplot/client.py new file mode 100644 index 0000000..f159e74 --- /dev/null +++ b/subplot/client.py @@ -0,0 +1,60 @@ +import os +import subprocess +import yaml + + +def install_obnam(ctx): + runcmd_prepend_to_path = globals()["runcmd_prepend_to_path"] + srcdir = globals()["srcdir"] + + # Add the directory with built Rust binaries to the path. + runcmd_prepend_to_path(ctx, dirname=os.path.join(srcdir, "target", "debug")) + + +def configure_client(ctx, filename=None): + get_file = globals()["get_file"] + + assert ctx.get("server_name") is not None + assert ctx.get("server_port") is not None + + config = get_file(filename) + config = yaml.safe_load(config) + config["server_name"] = ctx["server_name"] + config["server_port"] = ctx["server_port"] + + with open(filename, "w") as f: + yaml.safe_dump(config, stream=f) + + +def run_obnam_restore(ctx, filename=None, genid=None, dbname=None, todir=None): + runcmd_run = globals()["runcmd_run"] + + genid = ctx["vars"][genid] + runcmd_run( + ctx, + ["env", "RUST_LOG=obnam", "obnam", "restore", filename, genid, dbname, todir], + ) + + +def capture_generation_id(ctx, varname=None): + runcmd_get_stdout = globals()["runcmd_get_stdout"] + + stdout = runcmd_get_stdout(ctx) + gen_id = "unknown" + for line in stdout.splitlines(): + if line.startswith("gen id:"): + gen_id = line.split()[-1] + + v = ctx.get("vars", {}) + v[varname] = gen_id + ctx["vars"] = v + + +def live_and_restored_data_match(ctx, live=None, restore=None): + subprocess.check_call(["diff", "-rq", f"{live}/.", f"{restore}/{live}/."]) + + +def generation_list_contains(ctx, gen_id=None): + runcmd_stdout_contains = globals()["runcmd_stdout_contains"] + gen_id = ctx["vars"][gen_id] + runcmd_stdout_contains(ctx, text=gen_id) diff --git a/subplot/client.yaml b/subplot/client.yaml new file mode 100644 index 0000000..80b69f2 --- /dev/null +++ b/subplot/client.yaml @@ -0,0 +1,14 @@ +- given: "an installed obnam" + function: install_obnam + +- given: "a client config based on {filename}" + function: configure_client + +- when: "I invoke obnam restore {filename} <{genid}> {dbname} {todir}" + function: run_obnam_restore + +- then: "backup generation is {varname}" + function: capture_generation_id + +- then: "generation list contains <{gen_id}>" + function: generation_list_contains diff --git a/subplot/data.py b/subplot/data.py new file mode 100644 index 0000000..a1b9032 --- /dev/null +++ b/subplot/data.py @@ -0,0 +1,18 @@ +import logging +import os +import random +import subprocess + + +def create_file_with_random_data(ctx, filename=None): + N = 128 + data = "".join(chr(random.randint(0, 255)) for i in range(N)).encode("UTF-8") + dirname = os.path.dirname(filename) or "." + logging.debug(f"create_file_with_random_data: dirname={dirname}") + os.makedirs(dirname, exist_ok=True) + with open(filename, "wb") as f: + f.write(data) + + +def live_and_restored_data_match(ctx, live=None, restore=None): + subprocess.check_call(["diff", "-rq", f"{live}/.", f"{restore}/{live}/."]) diff --git a/subplot/data.yaml b/subplot/data.yaml new file mode 100644 index 0000000..8006240 --- /dev/null +++ b/subplot/data.yaml @@ -0,0 +1,10 @@ +- given: > + a file (?P\\S+) containing "(?P.*)" + regex: true + function: create_file_with_given_data + +- given: "a file {filename} containing some random data" + function: create_file_with_random_data + +- then: "data in {live} and {restore} match" + function: live_and_restored_data_match diff --git a/subplot/obnam.py b/subplot/obnam.py deleted file mode 100644 index 7df283a..0000000 --- a/subplot/obnam.py +++ /dev/null @@ -1,290 +0,0 @@ -import json -import logging -import os -import random -import re -import requests -import shutil -import socket -import subprocess -import tarfile -import time -import urllib3 -import yaml - - -urllib3.disable_warnings() - - -def start_chunk_server(ctx): - start_daemon = globals()["start_daemon"] - srcdir = globals()["srcdir"] - - logging.debug(f"Starting obnam-server") - - for x in ["test.pem", "test.key"]: - shutil.copy(os.path.join(srcdir, x), x) - - chunks = "chunks" - os.mkdir(chunks) - - config = {"chunks": chunks, "tls_key": "test.key", "tls_cert": "test.pem"} - port = config["port"] = random.randint(2000, 30000) - filename = "config.yaml" - yaml.safe_dump(config, stream=open(filename, "w")) - logging.debug(f"Picked randomly port for obnam-server: {config['port']}") - ctx["config"] = config - - ctx["server_name"] = "localhost" - ctx["server_port"] = port - ctx["url"] = f"http://localhost:{port}" - - start_daemon(ctx, "obnam-server", [_binary("obnam-server"), filename]) - - if not port_open("localhost", port, 5.0): - stderr = open(ctx["daemon"]["obnam-server"]["stderr"]).read() - logging.debug(f"Stderr from daemon: {stderr!r}") - - -def stop_chunk_server(ctx): - logging.debug("Stopping obnam-server") - stop_daemon = globals()["stop_daemon"] - stop_daemon(ctx, "obnam-server") - - -def create_file_with_random_data(ctx, filename=None): - N = 128 - data = "".join(chr(random.randint(0, 255)) for i in range(N)).encode("UTF-8") - dirname = os.path.dirname(filename) or "." - logging.debug(f"create_file_with_random_data: dirname={dirname}") - os.makedirs(dirname, exist_ok=True) - with open(filename, "wb") as f: - f.write(data) - - -def post_file(ctx, filename=None, path=None, header=None, json=None): - url = f"{ctx['url']}/chunks" - headers = {header: json} - data = open(filename, "rb").read() - _request(ctx, requests.post, url, headers=headers, data=data) - - -def get_chunk_via_var(ctx, var=None): - chunk_id = ctx["vars"][var] - get_chunk_by_id(ctx, chunk_id=chunk_id) - - -def get_chunk_by_id(ctx, chunk_id=None): - url = f"{ctx['url']}/chunks/{chunk_id}" - _request(ctx, requests.get, url) - - -def find_chunks_with_sha(ctx, sha=None): - url = f"{ctx['url']}/chunks?sha256={sha}" - _request(ctx, requests.get, url) - - -def delete_chunk_via_var(ctx, var=None): - chunk_id = ctx["vars"][var] - delete_chunk_by_id(ctx, chunk_id=chunk_id) - - -def delete_chunk_by_id(ctx, chunk_id=None): - url = f"{ctx['url']}/chunks/{chunk_id}" - _request(ctx, requests.delete, url) - - -def status_code_is(ctx, status=None): - assert_eq = globals()["assert_eq"] - assert_eq(ctx["http.status"], int(status)) - - -def header_is(ctx, header=None, value=None): - assert_eq = globals()["assert_eq"] - assert_eq(ctx["http.headers"][header], value) - - -def remember_json_field(ctx, field=None, var=None): - v = ctx.get("vars", {}) - v[var] = ctx["http.json"][field] - ctx["vars"] = v - - -def body_matches_file(ctx, filename=None): - assert_eq = globals()["assert_eq"] - content = open(filename, "rb").read() - logging.debug(f"body_matches_file:") - logging.debug(f" filename: {filename}") - logging.debug(f" content: {content!r}") - logging.debug(f" body: {ctx['http.raw']!r}") - assert_eq(ctx["http.raw"], content) - - -def json_body_matches(ctx, wanted=None): - assert_eq = globals()["assert_eq"] - wanted = _expand_vars(ctx, wanted) - wanted = json.loads(wanted) - body = ctx["http.json"] - logging.debug(f"json_body_matches:") - logging.debug(f" wanted: {wanted!r} ({type(wanted)}") - logging.debug(f" body : {body!r} ({type(body)}") - for key in wanted: - assert_eq(body.get(key, "not.there"), wanted[key]) - - -def back_up_directory(ctx, dirname=None): - runcmd_run = globals()["runcmd_run"] - - runcmd_run(ctx, ["pgrep", "-laf", "obnam"]) - - config = {"server_name": "localhost", "server_port": ctx["config"]["port"]} - config = yaml.safe_dump(config) - logging.debug(f"back_up_directory: {config}") - filename = "client.yaml" - with open(filename, "w") as f: - f.write(config) - - tarball = f"{dirname}.tar" - t = tarfile.open(name=tarball, mode="w") - t.add(dirname, arcname=".") - t.close() - - with open(tarball, "rb") as f: - runcmd_run(ctx, [_binary("obnam-backup"), filename], stdin=f) - - -def command_is_successful(ctx): - runcmd_exit_code_is_zero = globals()["runcmd_exit_code_is_zero"] - runcmd_exit_code_is_zero(ctx) - - -# Name of Rust binary, debug-build. -def _binary(name): - srcdir = globals()["srcdir"] - return os.path.abspath(os.path.join(srcdir, "target", "debug", name)) - - -# Wait for a port to be open -def port_open(host, port, timeout): - logging.debug(f"Waiting for port localhost:{port} to be available") - started = time.time() - while time.time() < started + timeout: - try: - socket.create_connection((host, port), timeout=timeout) - return True - except socket.error: - pass - logging.error(f"Port localhost:{port} is not open") - return False - - -# Make an HTTP request. -def _request(ctx, method, url, headers=None, data=None): - r = method(url, headers=headers, data=data, verify=False) - ctx["http.status"] = r.status_code - ctx["http.headers"] = dict(r.headers) - try: - ctx["http.json"] = dict(r.json()) - except ValueError: - ctx["http.json"] = None - ctx["http.raw"] = r.content - logging.debug("HTTP request:") - logging.debug(f" url: {url}") - logging.debug(f" header: {headers!r}") - logging.debug("HTTP response:") - logging.debug(f" status: {r.status_code}") - logging.debug(f" json: {ctx['http.json']!r}") - logging.debug(f" text: {r.content!r}") - if not r.ok: - stderr = open(ctx["daemon"]["obnam-server"]["stderr"], "rb").read() - logging.debug(f" server stderr: {stderr!r}") - - -# Expand variables ("") in a string with values from ctx. -def _expand_vars(ctx, s): - v = ctx.get("vars") - if v is None: - return s - result = [] - while True: - m = re.search(f"<(\\S+)>", s) - if not m: - result.append(s) - break - result.append(s[: m.start()]) - value = v[m.group(1)] - result.append(value) - s = s[m.end() :] - return "".join(result) - - -def install_obnam(ctx): - runcmd_prepend_to_path = globals()["runcmd_prepend_to_path"] - srcdir = globals()["srcdir"] - - # Add the directory with built Rust binaries to the path. - runcmd_prepend_to_path(ctx, dirname=os.path.join(srcdir, "target", "debug")) - - -def configure_client(ctx, filename=None): - get_file = globals()["get_file"] - - config = get_file(filename) - ctx["client-config"] = yaml.safe_load(config) - - -def run_obnam_backup(ctx, filename=None): - runcmd_run = globals()["runcmd_run"] - - _write_obnam_client_config(ctx, filename) - runcmd_run(ctx, ["env", "RUST_LOG=obnam", "obnam", "backup", filename]) - - -def run_obnam_list(ctx, filename=None): - runcmd_run = globals()["runcmd_run"] - - _write_obnam_client_config(ctx, filename) - runcmd_run(ctx, ["env", "RUST_LOG=obnam", "obnam", "list", filename]) - - -def _write_obnam_client_config(ctx, filename): - config = ctx["client-config"] - config["server_name"] = ctx["server_name"] - config["server_port"] = ctx["server_port"] - with open(filename, "w") as f: - yaml.safe_dump(config, stream=f) - - -def run_obnam_restore(ctx, filename=None, genid=None, dbname=None, todir=None): - runcmd_run = globals()["runcmd_run"] - - genid = ctx["vars"][genid] - _write_obnam_client_config(ctx, filename) - runcmd_run( - ctx, - ["env", "RUST_LOG=obnam", "obnam", "restore", filename, genid, dbname, todir], - ) - - -def capture_generation_id(ctx, varname=None): - runcmd_get_stdout = globals()["runcmd_get_stdout"] - - stdout = runcmd_get_stdout(ctx) - gen_id = "unknown" - for line in stdout.splitlines(): - if line.startswith("gen id:"): - gen_id = line.split()[-1] - - v = ctx.get("vars", {}) - v[varname] = gen_id - ctx["vars"] = v - - -def live_and_restored_data_match(ctx, live=None, restore=None): - subprocess.check_call(["diff", "-rq", f"{live}/.", f"{restore}/{live}/."]) - - -def generation_list_contains(ctx, gen_id=None): - runcmd_stdout_contains = globals()["runcmd_stdout_contains"] - gen_id = ctx["vars"][gen_id] - runcmd_stdout_contains(ctx, text=gen_id) diff --git a/subplot/obnam.yaml b/subplot/obnam.yaml deleted file mode 100644 index 8bde009..0000000 --- a/subplot/obnam.yaml +++ /dev/null @@ -1,77 +0,0 @@ -- given: "an installed obnam" - function: install_obnam - -- given: "a client config based on {filename}" - function: configure_client - -- given: "a chunk server" - function: start_chunk_server - cleanup: stop_chunk_server - -- given: > - a file (?P\\S+) containing "(?P.*)" - regex: true - function: create_file_with_given_data - -- given: "a file {filename} containing some random data" - function: create_file_with_random_data - -- when: "I POST (?P\\S+) to (?P\\S+), with (?P
\\S+): (?P.*)" - regex: true - function: post_file - -- when: "I GET /chunks/<{var}>" - function: get_chunk_via_var - -- when: "I try to GET /chunks/{chunk_id}" - function: get_chunk_by_id - -- when: "I GET /chunks?sha256={sha}" - regex: false - function: find_chunks_with_sha - -- when: "I DELETE /chunks/<{var}>" - function: delete_chunk_via_var - -- when: "I try to DELETE /chunks/{chunk_id}" - function: delete_chunk_by_id - -- when: "I back up {dirname} with obnam-backup" - function: back_up_directory - -- when: "I invoke obnam backup {filename}" - function: run_obnam_backup - -- when: "I invoke obnam list {filename}" - function: run_obnam_list - -- when: "I invoke obnam restore {filename} <{genid}> {dbname} {todir}" - function: run_obnam_restore - -- then: "HTTP status code is {status}" - function: status_code_is - -- then: "{header} is {value}" - function: header_is - -- then: "the JSON body has a field {field}, henceforth {var}" - function: remember_json_field - -- then: "the JSON body matches (?P.*)" - regex: true - function: json_body_matches - -- then: "the body matches file {filename}" - function: body_matches_file - -- then: "backup command is successful" - function: command_is_successful - -- then: "backup generation is {varname}" - function: capture_generation_id - -- then: "data in {live} and {restore} match" - function: live_and_restored_data_match - -- then: "generation list contains <{gen_id}>" - function: generation_list_contains diff --git a/subplot/server.py b/subplot/server.py new file mode 100644 index 0000000..c159798 --- /dev/null +++ b/subplot/server.py @@ -0,0 +1,178 @@ +import json +import logging +import os +import random +import re +import requests +import shutil +import socket +import time +import urllib3 +import yaml + + +urllib3.disable_warnings() + + +def start_chunk_server(ctx): + start_daemon = globals()["start_daemon"] + srcdir = globals()["srcdir"] + + logging.debug(f"Starting obnam-server") + + for x in ["test.pem", "test.key"]: + shutil.copy(os.path.join(srcdir, x), x) + + chunks = "chunks" + os.mkdir(chunks) + + config = {"chunks": chunks, "tls_key": "test.key", "tls_cert": "test.pem"} + port = config["port"] = random.randint(2000, 30000) + filename = "config.yaml" + yaml.safe_dump(config, stream=open(filename, "w")) + logging.debug(f"Picked randomly port for obnam-server: {config['port']}") + ctx["config"] = config + + ctx["server_name"] = "localhost" + ctx["server_port"] = port + ctx["url"] = f"http://localhost:{port}" + + start_daemon( + ctx, + "obnam-server", + [os.path.join(srcdir, "target", "debug", "obnam-server"), filename], + ) + + if not port_open("localhost", port, 5.0): + stderr = open(ctx["daemon"]["obnam-server"]["stderr"]).read() + logging.debug(f"Stderr from daemon: {stderr!r}") + + +def stop_chunk_server(ctx): + logging.debug("Stopping obnam-server") + stop_daemon = globals()["stop_daemon"] + stop_daemon(ctx, "obnam-server") + + +def post_file(ctx, filename=None, path=None, header=None, json=None): + url = f"{ctx['url']}/chunks" + headers = {header: json} + data = open(filename, "rb").read() + _request(ctx, requests.post, url, headers=headers, data=data) + + +def get_chunk_via_var(ctx, var=None): + chunk_id = ctx["vars"][var] + get_chunk_by_id(ctx, chunk_id=chunk_id) + + +def get_chunk_by_id(ctx, chunk_id=None): + url = f"{ctx['url']}/chunks/{chunk_id}" + _request(ctx, requests.get, url) + + +def find_chunks_with_sha(ctx, sha=None): + url = f"{ctx['url']}/chunks?sha256={sha}" + _request(ctx, requests.get, url) + + +def delete_chunk_via_var(ctx, var=None): + chunk_id = ctx["vars"][var] + delete_chunk_by_id(ctx, chunk_id=chunk_id) + + +def delete_chunk_by_id(ctx, chunk_id=None): + url = f"{ctx['url']}/chunks/{chunk_id}" + _request(ctx, requests.delete, url) + + +def status_code_is(ctx, status=None): + assert_eq = globals()["assert_eq"] + assert_eq(ctx["http.status"], int(status)) + + +def header_is(ctx, header=None, value=None): + assert_eq = globals()["assert_eq"] + assert_eq(ctx["http.headers"][header], value) + + +def remember_json_field(ctx, field=None, var=None): + v = ctx.get("vars", {}) + v[var] = ctx["http.json"][field] + ctx["vars"] = v + + +def body_matches_file(ctx, filename=None): + assert_eq = globals()["assert_eq"] + content = open(filename, "rb").read() + logging.debug(f"body_matches_file:") + logging.debug(f" filename: {filename}") + logging.debug(f" content: {content!r}") + logging.debug(f" body: {ctx['http.raw']!r}") + assert_eq(ctx["http.raw"], content) + + +def json_body_matches(ctx, wanted=None): + assert_eq = globals()["assert_eq"] + wanted = _expand_vars(ctx, wanted) + wanted = json.loads(wanted) + body = ctx["http.json"] + logging.debug(f"json_body_matches:") + logging.debug(f" wanted: {wanted!r} ({type(wanted)}") + logging.debug(f" body : {body!r} ({type(body)}") + for key in wanted: + assert_eq(body.get(key, "not.there"), wanted[key]) + + +# Wait for a port to be open +def port_open(host, port, timeout): + logging.debug(f"Waiting for port localhost:{port} to be available") + started = time.time() + while time.time() < started + timeout: + try: + socket.create_connection((host, port), timeout=timeout) + return True + except socket.error: + pass + logging.error(f"Port localhost:{port} is not open") + return False + + +# Make an HTTP request. +def _request(ctx, method, url, headers=None, data=None): + r = method(url, headers=headers, data=data, verify=False) + ctx["http.status"] = r.status_code + ctx["http.headers"] = dict(r.headers) + try: + ctx["http.json"] = dict(r.json()) + except ValueError: + ctx["http.json"] = None + ctx["http.raw"] = r.content + logging.debug("HTTP request:") + logging.debug(f" url: {url}") + logging.debug(f" header: {headers!r}") + logging.debug("HTTP response:") + logging.debug(f" status: {r.status_code}") + logging.debug(f" json: {ctx['http.json']!r}") + logging.debug(f" text: {r.content!r}") + if not r.ok: + stderr = open(ctx["daemon"]["obnam-server"]["stderr"], "rb").read() + logging.debug(f" server stderr: {stderr!r}") + + +# Expand variables ("") in a string with values from ctx. +def _expand_vars(ctx, s): + v = ctx.get("vars") + if v is None: + return s + result = [] + while True: + m = re.search(f"<(\\S+)>", s) + if not m: + result.append(s) + break + result.append(s[: m.start()]) + value = v[m.group(1)] + result.append(value) + s = s[m.end() :] + return "".join(result) diff --git a/subplot/server.yaml b/subplot/server.yaml new file mode 100644 index 0000000..e7a72b2 --- /dev/null +++ b/subplot/server.yaml @@ -0,0 +1,39 @@ +- given: "a running chunk server" + function: start_chunk_server + cleanup: stop_chunk_server + +- when: "I POST (?P\\S+) to (?P\\S+), with (?P
\\S+): (?P.*)" + regex: true + function: post_file + +- when: "I GET /chunks/<{var}>" + function: get_chunk_via_var + +- when: "I try to GET /chunks/{chunk_id}" + function: get_chunk_by_id + +- when: "I GET /chunks?sha256={sha}" + regex: false + function: find_chunks_with_sha + +- when: "I DELETE /chunks/<{var}>" + function: delete_chunk_via_var + +- when: "I try to DELETE /chunks/{chunk_id}" + function: delete_chunk_by_id + +- then: "HTTP status code is {status}" + function: status_code_is + +- then: "{header} is {value}" + function: header_is + +- then: "the JSON body has a field {field}, henceforth {var}" + function: remember_json_field + +- then: "the JSON body matches (?P.*)" + regex: true + function: json_body_matches + +- then: "the body matches file {filename}" + function: body_matches_file -- cgit v1.2.1