summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2021-07-22 10:14:55 +0000
committerLars Wirzenius <liw@liw.fi>2021-07-22 10:14:55 +0000
commit2fc9e33ccfbe388fe226d6b01f7e96e5c6cf79cb (patch)
treec9edf5f6db66d4031c0989d573fc7c2550c832cb
parentd8fdbbd8134acc9b27ad3cfe68a5abf9059e9d57 (diff)
parent0c46aec694080052a980ef7c95c642bb86124791 (diff)
downloadewww-2fc9e33ccfbe388fe226d6b01f7e96e5c6cf79cb.tar.gz
Merge branch 'chore' into 'main'
chore: add rustfmt.toml to force rustfmt to use 2018 edition See merge request larswirzenius/ewww!20
-rwxr-xr-xcheck20
-rwxr-xr-xdebian/rules2
-rw-r--r--ewww-vars.yml30
-rw-r--r--ewww.md12
-rw-r--r--ewww.service5
-rw-r--r--ewww.yaml2
-rw-r--r--ewww.yml116
-rw-r--r--hosts1
-rw-r--r--rustfmt.toml1
-rw-r--r--src/main.rs13
-rw-r--r--subplot/daemon.py77
-rw-r--r--subplot/ewww.py10
-rw-r--r--subplot/vendored/daemon.py262
-rw-r--r--subplot/vendored/daemon.yaml37
-rw-r--r--subplot/vendored/files.py194
-rw-r--r--subplot/vendored/files.yaml83
-rw-r--r--subplot/vendored/runcmd.py252
-rw-r--r--subplot/vendored/runcmd.yaml83
18 files changed, 187 insertions, 1013 deletions
diff --git a/check b/check
index b55a411..272ee63 100755
--- a/check
+++ b/check
@@ -1,9 +1,8 @@
-#!/bin/sh
+#!/bin/bash
set -eu
verbose=false
-runtest=true
moar=true
while [ "$#" -gt 0 ] && $moar
do
@@ -12,10 +11,6 @@ do
verbose=true
shift 1
;;
- -c | --codegen)
- runtest=false
- shift 1
- ;;
esac
done
@@ -33,12 +28,9 @@ fi
codegen() {
- local run=
- if $runtest
- then
- run=--run
- fi
- $hideok subplot codegen "$1" --output "$2" $run
+ $hideok subplot codegen "$1" --output "$2"
+ rm -f test.log
+ $hideok python3 test.py --log test.log
}
docgen() {
@@ -48,9 +40,13 @@ docgen() {
$hideok cargo build --all-targets
if cargo --list | awk '{ print $1 }' | grep 'clippy$' > /dev/null
then
+ # shellcheck disable=SC2086
cargo clippy $quiet
fi
+
+# shellcheck disable=SC2086
$hideok cargo test $quiet
+
if cargo fmt --help > /dev/null 2> /dev/null
then
$hideok cargo fmt -- --check
diff --git a/debian/rules b/debian/rules
index aaa5a27..e3d7fd8 100755
--- a/debian/rules
+++ b/debian/rules
@@ -8,6 +8,8 @@ override_dh_auto_build:
override_dh_auto_install:
cargo install --path=. --root=debian/ewww
+ install -d debian/ewww/lib/systemd/system
+ install -m 0644 ewww.service debian/ewww/lib/systemd/system/ewww.service
rm -f debian/ewww/.crates.toml
rm -f debian/ewww/.crates2.json
diff --git a/ewww-vars.yml b/ewww-vars.yml
new file mode 100644
index 0000000..e66d32c
--- /dev/null
+++ b/ewww-vars.yml
@@ -0,0 +1,30 @@
+ci_prod_signing_key: |
+ -----BEGIN PGP PUBLIC KEY BLOCK-----
+
+ mQINBFrLO7kBEADdz6mHstYmKU5Dp6OSjxWtWaqTDOX1sJdmmaIK/9EKVIH0Maxp
+ 5kvVO5G6mULLAjv/kLG0MxasHPrq8I2A/y8AqKAGVL8QelwLjQMIFZ30/VbGQPHS
+ +T5TZXEnoQtNce1GUhFwJ38ZyjjwHBFV9tSec7rZ2Q3YeM3nNnGPf6DacXGfEOPO
+ HIN4sXAN2hzNXNjKRzTIvxQseb6nr7afUh/SlZ3yhQOCrIzmYlD7tP9WJe7ofL0p
+ JY4pDQYw8rT6nC2BE/ioemh84kERCT1vCe+OVFlSRuMlqfEv+ZpKQ+itOmPDQ/lM
+ jpUm1K2hrW/lWpxT/ZxHKo/w1K36J5WshgMZxfUu5BMCL9LMqMcrXNhNjDMfxDMM
+ 3yBPOvQ4ls6fecOZ/bsFo1p8VzMk/w/eG8vPs5yuNa5XxN95yFMXoOHGb5Xbu8D4
+ 6yiW+Af70LbiSNpGdmNdneiGB2fY38NxBukPw5u3S5qG8HedSmMr1RvSr5kHoAAe
+ UbOY+BYaaKsTAT7+1skUW1o3FJSqoRKCHAzTsMWC6zzhR8hRn7jVrrguH1hGbqq5
+ TZSCFQZExuTJ7uXrTLG0WoBXIjB5wWNcSeXn8myUWYB51nJNF4tJBouZOz9JwWGl
+ kiAQkrHnBttLQWdW9FyjbIoTZMtpvVx+m6ObGTGdGL1cNlLAvWprMXGc+QARAQAB
+ tDJJY2sgQVBUIHJlcG9zaXRvcnkgc2lnbmluZyBrZXkgKDIwMTgpIDxsaXdAbGl3
+ LmZpPokCTgQTAQgAOBYhBKL1uyDoXyxUH3O717Wr+TZVS6PGBQJayzu5AhsDBQsJ
+ CAcCBhUICQoLAgQWAgMBAh4BAheAAAoJELWr+TZVS6PGB5QQANTcikhRUHwt9N4h
+ dGc/Hp6CbqdshMoWlwpFskttoVDxQG5OAobuZl5XyzGcmja1lT85RGkZFfbca0IZ
+ LnXOLLSAu51QBkXNaj4OhjK/0uQ+ITrvL6RQSXNgHiUTR/W2XD1GIUq6nBqe2GSN
+ 31S1baYKKVj5QIMsi7Dq8ls3BBXuPCE+xTSaNmGWjes2t9pPidcRvxsksCLY1qgw
+ P1GFXBeMkBQ29kBP87SUL15SIk7OiQLlEURCy5iRls5rt/YEsdEpRWIb0Tm5Nrjv
+ 2M3VM+iBhfNXTwj0rJ34mlycF1qQmA7YcTEobT7z587GPY0VWzBpQUnEQj7rQWPM
+ cDYY0b+I6kQ8VKOaL4wVAtE98d7HzFIrIrwhTKufnrWrVDPYsmLZ+LPC1jiF7JBD
+ SR6Vftb+SdDR9xoE1yRuXbC6IfoW+5/qQNrdQ2mm9BFw5jOonBqchs18HTTf3441
+ 6SWwP9fY3Vi+IZphPPi0Gf85oMStgnv/Wnw6LacEL32ek39Desero/D8iGLZernK
+ Q2mC9mua5A/bYGVhsNWyURNFkKdbFa+/wW3NfdKYyZnsSfo+jJ2luNewrhAY7Kod
+ GWXTer9RxzTGA3EXFGvNr+BBOOxSj0SfWTl0Olo7J5dnxof+jLAUS1VHpceHGHps
+ GSJSdir7NkZidgwoCPA7BTqsb5LN
+ =dXB0
+ -----END PGP PUBLIC KEY BLOCK-----
diff --git a/ewww.md b/ewww.md
index d1c9faf..8de29d7 100644
--- a/ewww.md
+++ b/ewww.md
@@ -151,15 +151,15 @@ author: Lars Wirzenius
template: python
bindings:
- subplot/ewww.yaml
- - subplot/vendored/daemon.yaml
- - subplot/vendored/files.yaml
- - subplot/vendored/runcmd.yaml
+ - lib/daemon.yaml
+ - lib/files.yaml
+ - lib/runcmd.yaml
functions:
- subplot/ewww.py
- subplot/http.py
- - subplot/vendored/daemon.py
- - subplot/vendored/files.py
- - subplot/vendored/runcmd.py
+ - lib/daemon.py
+ - lib/files.py
+ - lib/runcmd.py
classes:
- scenario-disabled
...
diff --git a/ewww.service b/ewww.service
new file mode 100644
index 0000000..add9477
--- /dev/null
+++ b/ewww.service
@@ -0,0 +1,5 @@
+[Unit]
+Description=Ewww web server
+
+[Service]
+ExecStart=/usr/bin/ewww /etc/ewww.yaml
diff --git a/ewww.yaml b/ewww.yaml
new file mode 100644
index 0000000..5278fee
--- /dev/null
+++ b/ewww.yaml
@@ -0,0 +1,2 @@
+# A vmadm spec file for a ewww VM.
+ewww: {}
diff --git a/ewww.yml b/ewww.yml
new file mode 100644
index 0000000..d8a10af
--- /dev/null
+++ b/ewww.yml
@@ -0,0 +1,116 @@
+# An Ansible playbook to deploy ewww. This is currently for demo
+# purposes only, and installs a self-signed toy TLS certificate.
+
+- hosts: ewww
+ remote_user: debian
+ become: yes
+ roles:
+ - sane_debian_system
+ tasks:
+ - name: "Install ewww"
+ apt:
+ name:
+ - ewww
+ - psmisc
+ - curl
+ state: present
+ - name: "Create /srv/http"
+ file:
+ state: directory
+ path: /srv/http
+ - name: "Create ewww config directory"
+ file:
+ state: directory
+ path: /etc/ewww
+ - name: "Install ewww config"
+ copy:
+ content: |
+ webroot: /srv/http
+ listen: "0.0.0.0:443"
+ tls_cert: /etc/ewww/tls.pem
+ tls_key: /etc/ewww/tls.key
+ dest: /etc/ewww/ewww.yaml
+ - name: "Install TLS cert"
+ copy:
+ content: |
+ -----BEGIN CERTIFICATE-----
+ MIICrzCCAZcCFFusxXoXXAVCzpfNK5VlnS8vFnY/MA0GCSqGSIb3DQEBCwUAMBQx
+ EjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yMTA3MjIwNzMzNThaFw0yMjA3MjIwNzMz
+ NThaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ ADCCAQoCggEBALhfy48gwIslLt5nCDSaPZeg52TwlZ8gWotnoprcv3cgTllDD/t7
+ uLwRrYFJl2AheaNRP+ZOgXYzuS+pOz7YCdLg6bc1d8Dto69gQy848GnTtHINgy3Z
+ Ag0L5d2B8/PcpEagFe2z1cCDzxNxkhjWisb0Rm1AOJcNxQWvICw428wwWEr6SRiO
+ FHTht5UG0oClK88cJSwBnzNSS9Q30q42JfUmua1Dd0PS3FOMibtzMB9aBATeR4uH
+ pQ1qCGU197er0PVfxWYrm8LEyZFQHRviwiaLNMtMRQuOp2rDF3kV/aZuw+aUYqpk
+ zz+H3g0lxU3vYp/NmSRvC7y4HFxr7xlu6DECAwEAATANBgkqhkiG9w0BAQsFAAOC
+ AQEAgpZ0dd+W4v7P6uFZ3R4rbRrHUQEOlFFMUrkf6EyT9xeIk7XjO6+RYbVP6tWX
+ h4T9sEIFypAtR/47JEhFKYzncPBygUQfzXH5hW0JgviMQ8nNQz6NUJ5vPpeI4Tob
+ 7uipx46Lq6nF6h9DbMK/03M7ZeybEa+nknDtry5hKTVzi+xSkVQX1/xgOBY0hhUk
+ xcLCULujN2Lp262aP9hIuI/vaXo5HOh+BavsSauVUsRjScz/8Lgn+q4qRajcgnRa
+ WvK5nH/Ok4am5F9LDcwZOyUXrV+VB9CcbhnzinMuPwCdhPvMr+F7zQP9YXbOeOlP
+ NdZiSNvGZAbEnmMnNCEYMO3wVA==
+ -----END CERTIFICATE-----
+ dest: /etc/ewww/tls.pem
+ - name: "Install TLS key"
+ copy:
+ content: |
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIEpAIBAAKCAQEAuF/LjyDAiyUu3mcINJo9l6DnZPCVnyBai2eimty/dyBOWUMP
+ +3u4vBGtgUmXYCF5o1E/5k6BdjO5L6k7PtgJ0uDptzV3wO2jr2BDLzjwadO0cg2D
+ LdkCDQvl3YHz89ykRqAV7bPVwIPPE3GSGNaKxvRGbUA4lw3FBa8gLDjbzDBYSvpJ
+ GI4UdOG3lQbSgKUrzxwlLAGfM1JL1DfSrjYl9Sa5rUN3Q9LcU4yJu3MwH1oEBN5H
+ i4elDWoIZTX3t6vQ9V/FZiubwsTJkVAdG+LCJos0y0xFC46nasMXeRX9pm7D5pRi
+ qmTPP4feDSXFTe9in82ZJG8LvLgcXGvvGW7oMQIDAQABAoIBAQCTKyP441PNvahj
+ ripGkreHSNBrKf7EPbcIf3iz1HCgThE7/uPLAT68IAA2qt9BxHarfjdbRl7gUvkG
+ qja4OwncYdssemlUfluhqVz3XKPKVUo7n72N4yJX959L6GcpyHz4QuA+FMYSHSQ1
+ iPntCZNMq79rhU+mgz85AkjUA66ulKzkFwYRL6oRJ+fxwYKTCcnRAUbUaihDXb5T
+ AV4wDPMKLse70KL42SPTrQFzTqguDlXzPlKvqOEi2lZkNkiMr8wdN/xZlzLre89K
+ EM/mczCnYnI17dkFrdF+9Wsr63o24H+vUQ3IWIDnVP+dgMXonvCz2Z8mawlb5tt7
+ vuY4b9KBAoGBAOczO740Q/mDk2iQI4Kt+o1unRwz34AEge0hm7kVUb7g2iV9sqNU
+ PovFjIvfCpWTmxVj6NQHyHbKDUfnnYzrpYHuMu2mL5E/1w+WqO1xPgoS287Xs/0I
+ E6N/BozDW4kMgBID0U2qz0JBrDMDFlL/yoziec6kv8f8uvRlQKtSdVSFAoGBAMwm
+ uDCShE4RcCr0PgAhiCSllJF03AVbLioTqdXwiHbIVvu5XvUClgOuI0eUDzU0Dsco
+ eWVaMQYx2Gt26sPPE52duZQNZ8JOZVq8/eSoycxYBn+hxYsjWqR9VvAZ4UMQvQ9g
+ T8La/NJTmzGVqpSD6XA176umCmgB/oeEaNZvchq9AoGAUfmbdDxJ4b1iVc/Nl3ci
+ gGU49Zf65gQzISYqdbx2aIyHLIXeAgVLy/k2dR2XPiPA+BudoRhFXsETZmxcM2wW
+ GfSgQB0Nfp25HkDYEqB1U9MN9tAKdGwZsn3Gj8Bwwy4Ydsq9uqEWrbJlYQz2LGWf
+ psZiU/+cNEeK7j68aEJrcZUCgYAu7zvrVtP6CsJJ7csPRqZBHpwwcLhgtty/KbQj
+ DmChRl/REYYGOCj7AZ70xtJUPfqjyOdX6MtajD0gP7+rcsEkvG0833QaVOGyYb7R
+ Qgja5OXhk/SRj3g4VuSU4K5MN93vWgocVzJGvJfyZ2FHMaiKdqv6P3sm/EZjK4ra
+ udZ21QKBgQDXmMP5sPHBtpHyXybIHk+nJICOtsKAJklXA1msgCk8OqDyPXX3qh8e
+ 4vFU4tgRN1nBMmEG5ROTtING1dQ5+X3aqXOJIO+asE1FkQA1kUhFKg2OSo15liPI
+ cB5//DSHki2Mh1iZxPfZnvFYpEOl9pmedSJ4tlltzKQSY//6kGJ49g==
+ -----END RSA PRIVATE KEY-----
+ dest: /etc/ewww/tls.key
+ - name: "Install systemd service"
+ copy:
+ content: |
+ [Unit]
+ Description=ewww web server
+
+ [Service]
+ ExecStart=/usr/bin/ewww /etc/ewww/ewww.yaml
+ dest: /lib/systemd/system/ewww.service
+ - name: "Enable and start ewww service"
+ systemd:
+ name: ewww
+ state: restarted
+ enabled: yes
+ daemon_reload: yes
+ vars:
+ sane_debian_system_version: 2
+ unix_users_version: 2
+
+ sane_debian_system_hostname: ewww
+ sane_debian_system_codename: buster
+ sane_debian_system_mirror: deb.debian.org
+
+ sane_debian_system_sources_lists:
+ - repo: deb http://ci-prod-controller.vm.liw.fi/debian unstable-ci main
+ signing_key: "{{ ci_prod_signing_key }}"
+
+ unix_users:
+ - username: static
+ comment: Static web site content
+ authorized_keys: |
+ {{ static_ssh_pub }}
diff --git a/hosts b/hosts
new file mode 100644
index 0000000..1166a32
--- /dev/null
+++ b/hosts
@@ -0,0 +1 @@
+ewww \ No newline at end of file
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 100644
index 0000000..c51666e
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1 @@
+edition = "2018" \ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index e120534..38a6c44 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,5 @@
use serde::Deserialize;
+use std::net::{SocketAddr, ToSocketAddrs};
use std::path::{Path, PathBuf};
use structopt::StructOpt;
use warp::Filter;
@@ -8,7 +9,7 @@ extern crate log;
#[derive(Debug, Deserialize)]
struct Config {
webroot: PathBuf,
- port: u16,
+ listen: String,
tls_key: PathBuf,
tls_cert: PathBuf,
}
@@ -34,7 +35,7 @@ enum EwwwError {
}
#[tokio::main]
-async fn main() {
+async fn main() -> anyhow::Result<()> {
pretty_env_logger::init();
info!("ewww starting up");
@@ -42,7 +43,11 @@ async fn main() {
let config = read_config(&opt.config).unwrap();
let webroot = config.webroot.canonicalize().unwrap();
+ let addr: SocketAddr = config.listen.to_socket_addrs().unwrap().next().unwrap();
+
info!("webroot: {:?}", webroot);
+ info!("listen on {}", addr);
+
let log = warp::log("ewww");
let webroot = warp::any().and(warp::fs::dir(webroot)).with(log);
@@ -50,8 +55,10 @@ async fn main() {
.tls()
.key_path(config.tls_key)
.cert_path(config.tls_cert)
- .run(([127, 0, 0, 1], config.port))
+ .run(addr)
.await;
+
+ Ok(())
}
fn read_config(filename: &Path) -> anyhow::Result<Config> {
diff --git a/subplot/daemon.py b/subplot/daemon.py
deleted file mode 100644
index 00c9d2d..0000000
--- a/subplot/daemon.py
+++ /dev/null
@@ -1,77 +0,0 @@
-#############################################################################
-# Start and stop daemons, or background processes.
-
-
-import logging
-import os
-import signal
-
-
-# Start a process in the background.
-def start_daemon(ctx, name, argv):
- runcmd_run = globals()["runcmd_run"]
- runcmd_exit_code_is = globals()["runcmd_exit_code_is"]
-
- logging.debug(f"Starting daemon {name}")
- logging.debug(f" ctx={ctx.as_dict()}")
- logging.debug(f" name={name}")
- logging.debug(f" argv={argv}")
-
- if "daemon" not in ctx.as_dict():
- ctx["daemon"] = {}
- assert name not in ctx["daemon"]
- this = ctx["daemon"][name] = {
- "pid-file": f"{name}.pid",
- "stderr": f"{name}.stderr",
- "stdout": f"{name}.stdout",
- }
- runcmd_run(
- ctx,
- [
- "/usr/sbin/daemonize",
- "-c",
- os.getcwd(),
- "-p",
- this["pid-file"],
- "-e",
- this["stderr"],
- "-o",
- this["stdout"],
- ]
- + argv,
- )
- runcmd_exit_code_is(ctx, 0)
- this["pid"] = int(open("ewww.pid").read().strip())
- assert process_exists(this["pid"])
-
- logging.debug(f"Started daemon {name}")
- logging.debug(f" ctx={ctx.as_dict()}")
-
-
-# Stop a daemon.
-def stop_daemon(ctx, name):
- logging.debug(f"Stopping daemon {name}")
- logging.debug(f" ctx={ctx.as_dict()}")
- logging.debug(f" ctx['daemon']={ctx.as_dict()['daemon']}")
-
- this = ctx["daemon"][name]
- terminate_process(this["pid"], signal.SIGKILL)
-
-
-# Does a process exist?
-def process_exists(pid):
- try:
- os.kill(pid, 0)
- except ProcessLookupError:
- return False
- return True
-
-
-# Terminate process.
-def terminate_process(pid, signalno):
- logging.debug(f"Terminating process {pid} with signal {signalno}")
- try:
- os.kill(pid, signalno)
- except ProcessLookupError:
- logging.debug("Process did not actually exist (anymore?)")
- pass
diff --git a/subplot/ewww.py b/subplot/ewww.py
index e30c219..a6c4c4f 100644
--- a/subplot/ewww.py
+++ b/subplot/ewww.py
@@ -24,10 +24,10 @@ def _write(filename, content):
# Construct a URL that points to server running on localhost by
# replacing the actual scheme and host with ones that work for test.
def _url(ctx, url):
- port = ctx["config"]["port"]
+ listen = ctx["config"]["listen"]
c = urllib.parse.urlparse(url)
host = c[1]
- c = (c[0], "localhost:{}".format(port)) + c[2:]
+ c = (c[0], "{}".format(listen)) + c[2:]
return urllib.parse.urlunparse(c), host
@@ -64,9 +64,11 @@ def start_server(ctx, filename=None):
logging.debug(f"Starting ewww with config file {filename}")
config = get_file(filename).decode("UTF-8")
config = yaml.safe_load(config)
- port = config["port"] = random.randint(2000, 30000)
- logging.debug(f"Picked randomly port for ewww: {config['port']}")
+ port = random.randint(2000, 30000)
+ logging.debug(f"Picked randomly port for ewww: {port}")
+ config["listen"] = f"localhost:{port}"
ctx["config"] = config
+ logging.debug(f"ewww config: {config!r}")
config = yaml.safe_dump(config)
_write(filename, config)
diff --git a/subplot/vendored/daemon.py b/subplot/vendored/daemon.py
deleted file mode 100644
index 11f65bf..0000000
--- a/subplot/vendored/daemon.py
+++ /dev/null
@@ -1,262 +0,0 @@
-import logging
-import os
-import signal
-import socket
-import subprocess
-import time
-
-
-# A helper function for testing lib/daemon itself.
-def _daemon_shell_script(ctx, filename=None):
- get_file = globals()["get_file"]
- data = get_file(filename)
- with open(filename, "wb") as f:
- f.write(data)
- os.chmod(filename, 0o755)
-
-
-# Start a daemon that will open a port on localhost.
-def daemon_start_on_port(ctx, path=None, args=None, name=None, port=None):
- _daemon_start(ctx, path=path, args=args, name=name)
- daemon_wait_for_port("localhost", port)
-
-
-# Start a daemon after a little wait. This is used only for testing the
-# port-waiting code.
-def _daemon_start_soonish(ctx, path=None, args=None, name=None, port=None):
- _daemon_start(ctx, path=os.path.abspath(path), args=args, name=name)
- daemon = ctx.declare("_daemon")
-
- # Store the PID of the process we just started so that _daemon_stop_soonish
- # can kill it during the cleanup phase. This works around the Subplot
- # Python template not giving the step captures to cleanup functions. Note
- # that this code assume at most one _soonish function is called.
- daemon["_soonish"] = daemon[name]["pid"]
-
- try:
- daemon_wait_for_port("localhost", port)
- except Exception as e:
- daemon["_start_error"] = repr(e)
-
- logging.info("pgrep: %r", _daemon_pgrep(path))
-
-
-def _daemon_stop_soonish(ctx, path=None, args=None, name=None, port=None):
- ns = ctx.declare("_daemon")
- pid = ns["_soonish"]
- logging.debug(f"Stopping soonishly-started daemon, {pid}")
- signo = signal.SIGKILL
- try:
- os.kill(pid, signo)
- except ProcessLookupError:
- logging.warning("Process did not actually exist (anymore?)")
-
-
-# Start a daeamon, get its PID. Don't wait for a port or anything. This is
-# meant for background processes that don't have port. Useful for testing the
-# lib/daemon library of Subplot, but not much else.
-def _daemon_start(ctx, path=None, args=None, name=None):
- runcmd_run = globals()["runcmd_run"]
- runcmd_exit_code_is = globals()["runcmd_exit_code_is"]
- runcmd_get_exit_code = globals()["runcmd_get_exit_code"]
- runcmd_get_stderr = globals()["runcmd_get_stderr"]
- runcmd_prepend_to_path = globals()["runcmd_prepend_to_path"]
-
- path = os.path.abspath(path)
- argv = [path] + args.split()
-
- logging.debug(f"Starting daemon {name}")
- logging.debug(f" ctx={ctx.as_dict()}")
- logging.debug(f" name={name}")
- logging.debug(f" path={path}")
- logging.debug(f" args={args}")
- logging.debug(f" argv={argv}")
-
- ns = ctx.declare("_daemon")
-
- this = ns[name] = {
- "pid-file": f"{name}.pid",
- "stderr": f"{name}.stderr",
- "stdout": f"{name}.stdout",
- }
-
- # Debian installs `daemonize` to /usr/sbin, which isn't part of the minimal
- # environment that Subplot sets up. So we add /usr/sbin to the PATH.
- runcmd_prepend_to_path(ctx, "/usr/sbin")
- runcmd_run(
- ctx,
- [
- "daemonize",
- "-c",
- os.getcwd(),
- "-p",
- this["pid-file"],
- "-e",
- this["stderr"],
- "-o",
- this["stdout"],
- ]
- + argv,
- )
-
- # Check that daemonize has exited OK. If it hasn't, it didn't start the
- # background process at all. If so, log the stderr in case there was
- # something useful there for debugging.
- exit = runcmd_get_exit_code(ctx)
- if exit != 0:
- stderr = runcmd_get_stderr(ctx)
- logging.error(f"daemon {name} stderr: {stderr}")
- runcmd_exit_code_is(ctx, 0)
-
- # Get the pid of the background process, from the pid file created by
- # daemonize. We don't need to wait for it, since we know daemonize already
- # exited. If it isn't there now, it's won't appear later.
- if not os.path.exists(this["pid-file"]):
- raise Exception("daemonize didn't create a PID file")
-
- this["pid"] = _daemon_wait_for_pid(this["pid-file"], 10.0)
-
- logging.debug(f"Started daemon {name}")
- logging.debug(f" pid={this['pid']}")
- logging.debug(f" ctx={ctx.as_dict()}")
-
-
-def _daemon_wait_for_pid(filename, timeout):
- start = time.time()
- while time.time() < start + timeout:
- with open(filename) as f:
- data = f.read().strip()
- if data:
- return int(data)
- raise Exception("daemonize created a PID file without a PID")
-
-
-def daemon_wait_for_port(host, port, timeout=5.0):
- addr = (host, port)
- until = time.time() + timeout
- while True:
- try:
- s = socket.create_connection(addr, timeout=timeout)
- s.close()
- return
- except socket.timeout:
- logging.error(
- f"daemon did not respond at port {port} within {timeout} seconds"
- )
- raise
- except socket.error as e:
- logging.info(f"could not connect to daemon at {port}: {e}")
- pass
- if time.time() >= until:
- logging.error(
- f"could not connect to daemon at {port} within {timeout} seconds"
- )
- raise ConnectionRefusedError()
- # Sleep a bit to avoid consuming too much CPU while busy-waiting.
- time.sleep(0.1)
-
-
-# Stop a daemon.
-def daemon_stop(ctx, path=None, args=None, name=None):
- logging.debug(f"Stopping daemon {name}")
-
- ns = ctx.declare("_daemon")
- logging.debug(f" ns={ns}")
- pid = ns[name]["pid"]
- signo = signal.SIGTERM
-
- this = ns[name]
- data = open(this["stdout"]).read()
- logging.debug(f"{name} stdout, before: {data!r}")
- data = open(this["stderr"]).read()
- logging.debug(f"{name} stderr, before: {data!r}")
-
- logging.debug(f"Terminating process {pid} with signal {signo}")
- try:
- os.kill(pid, signo)
- except ProcessLookupError:
- logging.warning("Process did not actually exist (anymore?)")
-
- while True:
- try:
- os.kill(pid, 0)
- logging.debug(f"Daemon {name}, pid {pid} still exists")
- time.sleep(1)
- except ProcessLookupError:
- break
- logging.debug(f"Daemon {name} is gone")
-
- data = open(this["stdout"]).read()
- logging.debug(f"{name} stdout, after: {data!r}")
- data = open(this["stderr"]).read()
- logging.debug(f"{name} stderr, after: {data!r}")
-
-
-def daemon_no_such_process(ctx, args=None):
- assert not _daemon_pgrep(args)
-
-
-def daemon_process_exists(ctx, args=None):
- assert _daemon_pgrep(args)
-
-
-def _daemon_pgrep(pattern):
- logging.info(f"checking if process exists: pattern={pattern}")
- exit = subprocess.call(
- ["pgrep", "-laf", pattern], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
- )
- logging.info(f"exit code: {exit}")
- return exit == 0
-
-
-def daemon_start_fails_with(ctx, message=None):
- daemon = ctx.declare("_daemon")
- error = daemon["_start_error"]
- logging.debug(f"daemon_start_fails_with: error={error!r}")
- logging.debug(f"daemon_start_fails_with: message={message!r}")
- assert message.lower() in error.lower()
-
-
-def daemon_get_stdout(ctx, name):
- return _daemon_get_output(ctx, name, "stdout")
-
-
-def daemon_get_stderr(ctx, name):
- return _daemon_get_output(ctx, name, "stderr")
-
-
-def _daemon_get_output(ctx, name, which):
- ns = ctx.declare("_daemon")
- this = ns[name]
- filename = this[which]
- data = open(filename).read()
- logging.debug(f"Read {which} of daemon {name} from {filename}: {data!r}")
- return data
-
-
-def daemon_has_produced_output(ctx, name=None):
- started = time.time()
- timeout = 5.0
- while time.time() < started + timeout:
- stdout = daemon_get_stdout(ctx, name)
- stderr = daemon_get_stderr(ctx, name)
- if stdout and stderr:
- break
- time.sleep(0.1)
-
-
-def daemon_stdout_is(ctx, name=None, text=None):
- daemon_get_stdout = globals()["daemon_get_stdout"]
- _daemon_output_is(ctx, name, text, daemon_get_stdout)
-
-
-def daemon_stderr_is(ctx, name=None, text=None):
- daemon_get_stderr = globals()["daemon_get_stderr"]
- _daemon_output_is(ctx, name, text, daemon_get_stderr)
-
-
-def _daemon_output_is(ctx, name, text, getter):
- assert_eq = globals()["assert_eq"]
- text = bytes(text, "UTF-8").decode("unicode_escape")
- output = getter(ctx, name)
- assert_eq(text, output)
diff --git a/subplot/vendored/daemon.yaml b/subplot/vendored/daemon.yaml
deleted file mode 100644
index 4fab1f6..0000000
--- a/subplot/vendored/daemon.yaml
+++ /dev/null
@@ -1,37 +0,0 @@
-- given: there is no "{args:text}" process
- function: daemon_no_such_process
-
-- given: a daemon helper shell script {filename}
- function: _daemon_shell_script
-
-- when: I start "{path}{args:text}" as a background process as {name}, on port {port}
- function: daemon_start_on_port
-
-- when: I try to start "{path}{args:text}" as {name}, on port {port}
- function: _daemon_start_soonish
- cleanup: _daemon_stop_soonish
-
-- when: I start "{path}{args:text}" as a background process as {name}
- function: _daemon_start
-
-- when: I stop background process {name}
- function: daemon_stop
-
-- when: daemon {name} has produced output
- function: daemon_has_produced_output
-
-- then: a process "{args:text}" is running
- function: daemon_process_exists
-
-- then: there is no "{args:text}" process
- function: daemon_no_such_process
-
-- then: starting daemon fails with "{message:text}"
- function: daemon_start_fails_with
-
-- then: daemon {name} stdout is "{text:text}"
- function: daemon_stdout_is
-
-- then: daemon {name} stderr is "{text:text}"
- function: daemon_stderr_is
-
diff --git a/subplot/vendored/files.py b/subplot/vendored/files.py
deleted file mode 100644
index dd5b9f8..0000000
--- a/subplot/vendored/files.py
+++ /dev/null
@@ -1,194 +0,0 @@
-import logging
-import os
-import re
-import shutil
-import time
-
-
-def files_create_from_embedded(ctx, filename=None):
- files_make_directory(ctx, path=os.path.dirname(filename) or ".")
- files_create_from_embedded_with_other_name(
- ctx, filename_on_disk=filename, embedded_filename=filename
- )
-
-
-def files_create_from_embedded_with_other_name(
- ctx, filename_on_disk=None, embedded_filename=None
-):
- get_file = globals()["get_file"]
-
- files_make_directory(ctx, path=os.path.dirname(filename_on_disk) or ".")
- with open(filename_on_disk, "wb") as f:
- f.write(get_file(embedded_filename))
-
-
-def files_create_from_text(ctx, filename=None, text=None):
- files_make_directory(ctx, path=os.path.dirname(filename) or ".")
- with open(filename, "w") as f:
- f.write(text)
-
-
-def files_make_directory(ctx, path=None):
- path = "./" + path
- if not os.path.exists(path):
- os.makedirs(path)
-
-
-def files_remove_directory(ctx, path=None):
- path = "./" + path
- shutil.rmtree(path)
-
-
-def files_file_exists(ctx, filename=None):
- assert_eq = globals()["assert_eq"]
- assert_eq(os.path.exists(filename), True)
-
-
-def files_file_does_not_exist(ctx, filename=None):
- assert_eq = globals()["assert_eq"]
- assert_eq(os.path.exists(filename), False)
-
-
-def files_directory_exists(ctx, path=None):
- assert_eq = globals()["assert_eq"]
- assert_eq(os.path.isdir(path), True)
-
-
-def files_directory_does_not_exist(ctx, path=None):
- assert_eq = globals()["assert_eq"]
- assert_eq(os.path.isdir(path), False)
-
-
-def files_directory_is_empty(ctx, path=None):
- assert_eq = globals()["assert_eq"]
- assert_eq(os.listdir(path), [])
-
-
-def files_directory_is_not_empty(ctx, path=None):
- assert_ne = globals()["assert_ne"]
- assert_ne(os.listdir(path), False)
-
-
-def files_only_these_exist(ctx, filenames=None):
- assert_eq = globals()["assert_eq"]
- filenames = filenames.replace(",", "").split()
- assert_eq(set(os.listdir(".")), set(filenames))
-
-
-def files_file_contains(ctx, filename=None, data=None):
- assert_eq = globals()["assert_eq"]
- with open(filename, "rb") as f:
- actual = f.read()
- actual = actual.decode("UTF-8")
- assert_eq(data in actual, True)
-
-
-def files_file_matches_regex(ctx, filename=None, regex=None):
- assert_eq = globals()["assert_eq"]
- with open(filename) as f:
- content = f.read()
- m = re.search(regex, content)
- if m is None:
- logging.debug(f"files_file_matches_regex: no match")
- logging.debug(f" filenamed: {filename}")
- logging.debug(f" regex: {regex}")
- logging.debug(f" content: {regex}")
- logging.debug(f" match: {m}")
- assert_eq(bool(m), True)
-
-
-def files_match(ctx, filename1=None, filename2=None):
- assert_eq = globals()["assert_eq"]
- with open(filename1, "rb") as f:
- data1 = f.read()
- with open(filename2, "rb") as f:
- data2 = f.read()
- assert_eq(data1, data2)
-
-
-def files_touch_with_timestamp(
- ctx,
- filename=None,
- year=None,
- month=None,
- day=None,
- hour=None,
- minute=None,
- second=None,
-):
- t = (
- int(year),
- int(month),
- int(day),
- int(hour),
- int(minute),
- int(second),
- -1,
- -1,
- -1,
- )
- ts = time.mktime(t)
- _files_touch(filename, ts)
-
-
-def files_touch(ctx, filename=None):
- _files_touch(filename, None)
-
-
-def _files_touch(filename, ts):
- if not os.path.exists(filename):
- open(filename, "w").close()
- times = None
- if ts is not None:
- times = (ts, ts)
- os.utime(filename, times=times)
-
-
-def files_mtime_is_recent(ctx, filename=None):
- st = os.stat(filename)
- age = abs(st.st_mtime - time.time())
- assert age < 1.0
-
-
-def files_mtime_is_ancient(ctx, filename=None):
- st = os.stat(filename)
- age = abs(st.st_mtime - time.time())
- year = 365 * 24 * 60 * 60
- required = 39 * year
- logging.debug(f"ancient? mtime={st.st_mtime} age={age} required={required}")
- assert age > required
-
-
-def files_remember_metadata(ctx, filename=None):
- meta = _files_remembered(ctx)
- meta[filename] = _files_get_metadata(filename)
- logging.debug("files_remember_metadata:")
- logging.debug(f" meta: {meta}")
- logging.debug(f" ctx: {ctx}")
-
-
-# Check that current metadata of a file is as stored in the context.
-def files_has_remembered_metadata(ctx, filename=None):
- assert_eq = globals()["assert_eq"]
- meta = _files_remembered(ctx)
- logging.debug("files_has_remembered_metadata:")
- logging.debug(f" meta: {meta}")
- logging.debug(f" ctx: {ctx}")
- assert_eq(meta[filename], _files_get_metadata(filename))
-
-
-def files_has_different_metadata(ctx, filename=None):
- assert_ne = globals()["assert_ne"]
- meta = _files_remembered(ctx)
- assert_ne(meta[filename], _files_get_metadata(filename))
-
-
-def _files_remembered(ctx):
- ns = ctx.declare("_files")
- return ns.get("remembered-metadata", {})
-
-
-def _files_get_metadata(filename):
- st = os.lstat(filename)
- keys = ["st_dev", "st_gid", "st_ino", "st_mode", "st_mtime", "st_size", "st_uid"]
- return {key: getattr(st, key) for key in keys}
diff --git a/subplot/vendored/files.yaml b/subplot/vendored/files.yaml
deleted file mode 100644
index f18b8cd..0000000
--- a/subplot/vendored/files.yaml
+++ /dev/null
@@ -1,83 +0,0 @@
-- given: file {filename}
- function: files_create_from_embedded
- types:
- filename: file
-
-- given: file {filename_on_disk} from {embedded_filename}
- function: files_create_from_embedded_with_other_name
- types:
- embedded_filename: file
-
-- given: file {filename} has modification time {year}-{month}-{day} {hour}:{minute}:{second}
- function: files_touch_with_timestamp
-
-- when: I write "(?P<text>.*)" to file (?P<filename>\S+)
- regex: true
- function: files_create_from_text
-
-- when: I remember metadata for file {filename}
- function: files_remember_metadata
-
-- when: I touch file {filename}
- function: files_touch
-
-- then: file {filename} exists
- function: files_file_exists
-
-- then: file {filename} does not exist
- function: files_file_does_not_exist
-
-- then: only files (?P<filenames>.+) exist
- function: files_only_these_exist
- regex: true
-
-- then: file (?P<filename>\S+) contains "(?P<data>.*)"
- regex: true
- function: files_file_contains
-
-- then: file (?P<filename>\S+) matches regex /(?P<regex>.*)/
- regex: true
- function: files_file_matches_regex
-
-- then: file (?P<filename>\S+) matches regex "(?P<regex>.*)"
- regex: true
- function: files_file_matches_regex
-
-- then: files {filename1} and {filename2} match
- function: files_match
-
-- then: file {filename} has same metadata as before
- function: files_has_remembered_metadata
-
-- then: file {filename} has different metadata from before
- function: files_has_different_metadata
-
-- then: file {filename} has changed from before
- function: files_has_different_metadata
-
-- then: file {filename} has a very recent modification time
- function: files_mtime_is_recent
-
-- then: file {filename} has a very old modification time
- function: files_mtime_is_ancient
-
-- given: a directory {path}
- function: files_make_directory
-
-- when: I create directory {path}
- function: files_make_directory
-
-- when: I remove directory {path}
- function: files_remove_directory
-
-- then: directory {path} exists
- function: files_directory_exists
-
-- then: directory {path} does not exist
- function: files_directory_does_not_exist
-
-- then: directory {path} is empty
- function: files_directory_is_empty
-
-- then: directory {path} is not empty
- function: files_directory_is_not_empty
diff --git a/subplot/vendored/runcmd.py b/subplot/vendored/runcmd.py
deleted file mode 100644
index a2564c6..0000000
--- a/subplot/vendored/runcmd.py
+++ /dev/null
@@ -1,252 +0,0 @@
-import logging
-import os
-import re
-import shlex
-import subprocess
-
-
-#
-# Helper functions.
-#
-
-# Get exit code or other stored data about the latest command run by
-# runcmd_run.
-
-
-def _runcmd_get(ctx, name):
- ns = ctx.declare("_runcmd")
- return ns[name]
-
-
-def runcmd_get_exit_code(ctx):
- return _runcmd_get(ctx, "exit")
-
-
-def runcmd_get_stdout(ctx):
- return _runcmd_get(ctx, "stdout")
-
-
-def runcmd_get_stdout_raw(ctx):
- return _runcmd_get(ctx, "stdout.raw")
-
-
-def runcmd_get_stderr(ctx):
- return _runcmd_get(ctx, "stderr")
-
-
-def runcmd_get_stderr_raw(ctx):
- return _runcmd_get(ctx, "stderr.raw")
-
-
-def runcmd_get_argv(ctx):
- return _runcmd_get(ctx, "argv")
-
-
-# Run a command, given an argv and other arguments for subprocess.Popen.
-#
-# This is meant to be a helper function, not bound directly to a step. The
-# stdout, stderr, and exit code are stored in the "_runcmd" namespace in the
-# ctx context.
-def runcmd_run(ctx, argv, **kwargs):
- ns = ctx.declare("_runcmd")
-
- # The Subplot Python template empties os.environ at startup, modulo a small
- # number of variables with carefully chosen values. Here, we don't need to
- # care about what those variables are, but we do need to not overwrite
- # them, so we just add anything in the env keyword argument, if any, to
- # os.environ.
- env = dict(os.environ)
- for key, arg in kwargs.pop("env", {}).items():
- env[key] = arg
-
- pp = ns.get("path-prefix")
- if pp:
- env["PATH"] = pp + ":" + env["PATH"]
-
- logging.debug(f"runcmd_run")
- logging.debug(f" argv: {argv}")
- logging.debug(f" env: {env}")
- p = subprocess.Popen(
- argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, **kwargs
- )
- stdout, stderr = p.communicate("")
- ns["argv"] = argv
- ns["stdout.raw"] = stdout
- ns["stderr.raw"] = stderr
- ns["stdout"] = stdout.decode("utf-8")
- ns["stderr"] = stderr.decode("utf-8")
- ns["exit"] = p.returncode
- logging.debug(f" ctx: {ctx}")
- logging.debug(f" ns: {ns}")
-
-
-# Step: prepend srcdir to PATH whenever runcmd runs a command.
-def runcmd_helper_srcdir_path(ctx):
- srcdir = globals()["srcdir"]
- runcmd_prepend_to_path(ctx, srcdir)
-
-
-# Step: This creates a helper script.
-def runcmd_helper_script(ctx, filename=None):
- get_file = globals()["get_file"]
- with open(filename, "wb") as f:
- f.write(get_file(filename))
-
-
-#
-# Step functions for running commands.
-#
-
-
-def runcmd_prepend_to_path(ctx, dirname=None):
- ns = ctx.declare("_runcmd")
- pp = ns.get("path-prefix", "")
- if pp:
- pp = f"{pp}:{dirname}"
- else:
- pp = dirname
- ns["path-prefix"] = pp
-
-
-def runcmd_step(ctx, argv0=None, args=None):
- runcmd_try_to_run(ctx, argv0=argv0, args=args)
- runcmd_exit_code_is_zero(ctx)
-
-
-def runcmd_try_to_run(ctx, argv0=None, args=None):
- argv = [shlex.quote(argv0)] + shlex.split(args)
- runcmd_run(ctx, argv)
-
-
-#
-# Step functions for examining exit codes.
-#
-
-
-def runcmd_exit_code_is_zero(ctx):
- runcmd_exit_code_is(ctx, exit=0)
-
-
-def runcmd_exit_code_is(ctx, exit=None):
- assert_eq = globals()["assert_eq"]
- assert_eq(runcmd_get_exit_code(ctx), int(exit))
-
-
-def runcmd_exit_code_is_nonzero(ctx):
- runcmd_exit_code_is_not(ctx, exit=0)
-
-
-def runcmd_exit_code_is_not(ctx, exit=None):
- assert_ne = globals()["assert_ne"]
- assert_ne(runcmd_get_exit_code(ctx), int(exit))
-
-
-#
-# Step functions and helpers for examining output in various ways.
-#
-
-
-def runcmd_stdout_is(ctx, text=None):
- _runcmd_output_is(runcmd_get_stdout(ctx), text)
-
-
-def runcmd_stdout_isnt(ctx, text=None):
- _runcmd_output_isnt(runcmd_get_stdout(ctx), text)
-
-
-def runcmd_stderr_is(ctx, text=None):
- _runcmd_output_is(runcmd_get_stderr(ctx), text)
-
-
-def runcmd_stderr_isnt(ctx, text=None):
- _runcmd_output_isnt(runcmd_get_stderr(ctx), text)
-
-
-def _runcmd_output_is(actual, wanted):
- assert_eq = globals()["assert_eq"]
- wanted = bytes(wanted, "utf8").decode("unicode_escape")
- logging.debug("_runcmd_output_is:")
- logging.debug(f" actual: {actual!r}")
- logging.debug(f" wanted: {wanted!r}")
- assert_eq(actual, wanted)
-
-
-def _runcmd_output_isnt(actual, wanted):
- assert_ne = globals()["assert_ne"]
- wanted = bytes(wanted, "utf8").decode("unicode_escape")
- logging.debug("_runcmd_output_isnt:")
- logging.debug(f" actual: {actual!r}")
- logging.debug(f" wanted: {wanted!r}")
- assert_ne(actual, wanted)
-
-
-def runcmd_stdout_contains(ctx, text=None):
- _runcmd_output_contains(runcmd_get_stdout(ctx), text)
-
-
-def runcmd_stdout_doesnt_contain(ctx, text=None):
- _runcmd_output_doesnt_contain(runcmd_get_stdout(ctx), text)
-
-
-def runcmd_stderr_contains(ctx, text=None):
- _runcmd_output_contains(runcmd_get_stderr(ctx), text)
-
-
-def runcmd_stderr_doesnt_contain(ctx, text=None):
- _runcmd_output_doesnt_contain(runcmd_get_stderr(ctx), text)
-
-
-def _runcmd_output_contains(actual, wanted):
- assert_eq = globals()["assert_eq"]
- wanted = bytes(wanted, "utf8").decode("unicode_escape")
- logging.debug("_runcmd_output_contains:")
- logging.debug(f" actual: {actual!r}")
- logging.debug(f" wanted: {wanted!r}")
- assert_eq(wanted in actual, True)
-
-
-def _runcmd_output_doesnt_contain(actual, wanted):
- assert_ne = globals()["assert_ne"]
- wanted = bytes(wanted, "utf8").decode("unicode_escape")
- logging.debug("_runcmd_output_doesnt_contain:")
- logging.debug(f" actual: {actual!r}")
- logging.debug(f" wanted: {wanted!r}")
- assert_ne(wanted in actual, True)
-
-
-def runcmd_stdout_matches_regex(ctx, regex=None):
- _runcmd_output_matches_regex(runcmd_get_stdout(ctx), regex)
-
-
-def runcmd_stdout_doesnt_match_regex(ctx, regex=None):
- _runcmd_output_doesnt_match_regex(runcmd_get_stdout(ctx), regex)
-
-
-def runcmd_stderr_matches_regex(ctx, regex=None):
- _runcmd_output_matches_regex(runcmd_get_stderr(ctx), regex)
-
-
-def runcmd_stderr_doesnt_match_regex(ctx, regex=None):
- _runcmd_output_doesnt_match_regex(runcmd_get_stderr(ctx), regex)
-
-
-def _runcmd_output_matches_regex(actual, regex):
- assert_ne = globals()["assert_ne"]
- r = re.compile(regex)
- m = r.search(actual)
- logging.debug("_runcmd_output_matches_regex:")
- logging.debug(f" actual: {actual!r}")
- logging.debug(f" regex: {regex!r}")
- logging.debug(f" match: {m}")
- assert_ne(m, None)
-
-
-def _runcmd_output_doesnt_match_regex(actual, regex):
- assert_eq = globals()["assert_eq"]
- r = re.compile(regex)
- m = r.search(actual)
- logging.debug("_runcmd_output_doesnt_match_regex:")
- logging.debug(f" actual: {actual!r}")
- logging.debug(f" regex: {regex!r}")
- logging.debug(f" match: {m}")
- assert_eq(m, None)
diff --git a/subplot/vendored/runcmd.yaml b/subplot/vendored/runcmd.yaml
deleted file mode 100644
index 48dde90..0000000
--- a/subplot/vendored/runcmd.yaml
+++ /dev/null
@@ -1,83 +0,0 @@
-# Steps to run commands.
-
-- given: helper script {filename} for runcmd
- function: runcmd_helper_script
-
-- given: srcdir is in the PATH
- function: runcmd_helper_srcdir_path
-
-- when: I run (?P<argv0>\S+)(?P<args>.*)
- regex: true
- function: runcmd_step
-
-- when: I try to run (?P<argv0>\S+)(?P<args>.*)
- regex: true
- function: runcmd_try_to_run
-
-# Steps to examine exit code of latest command.
-
-- then: exit code is {exit}
- function: runcmd_exit_code_is
-
-- then: exit code is not {exit}
- function: runcmd_exit_code_is_not
-
-- then: command is successful
- function: runcmd_exit_code_is_zero
-
-- then: command fails
- function: runcmd_exit_code_is_nonzero
-
-# Steps to examine stdout/stderr for exact content.
-
-- then: stdout is exactly "(?P<text>.*)"
- regex: true
- function: runcmd_stdout_is
-
-- then: "stdout isn't exactly \"(?P<text>.*)\""
- regex: true
- function: runcmd_stdout_isnt
-
-- then: stderr is exactly "(?P<text>.*)"
- regex: true
- function: runcmd_stderr_is
-
-- then: "stderr isn't exactly \"(?P<text>.*)\""
- regex: true
- function: runcmd_stderr_isnt
-
-# Steps to examine stdout/stderr for sub-strings.
-
-- then: stdout contains "(?P<text>.*)"
- regex: true
- function: runcmd_stdout_contains
-
-- then: "stdout doesn't contain \"(?P<text>.*)\""
- regex: true
- function: runcmd_stdout_doesnt_contain
-
-- then: stderr contains "(?P<text>.*)"
- regex: true
- function: runcmd_stderr_contains
-
-- then: "stderr doesn't contain \"(?P<text>.*)\""
- regex: true
- function: runcmd_stderr_doesnt_contain
-
-# Steps to match stdout/stderr against regular expressions.
-
-- then: stdout matches regex (?P<regex>.*)
- regex: true
- function: runcmd_stdout_matches_regex
-
-- then: stdout doesn't match regex (?P<regex>.*)
- regex: true
- function: runcmd_stdout_doesnt_match_regex
-
-- then: stderr matches regex (?P<regex>.*)
- regex: true
- function: runcmd_stderr_matches_regex
-
-- then: stderr doesn't match regex (?P<regex>.*)
- regex: true
- function: runcmd_stderr_doesnt_match_regex