453927facf
This PS rewrites the document_validation module in Deckhand to achieve the following goals: * better validation resiliency * add support for different document schema versions * better support for DataSchema validation * separation of concerns by splitting up validations into separate classes * support for validating documents that rely on a DataSchema passed in via the same payload * support for generating multiple validation errors rather than returning after the first one found * increase testing validations for unit/functional tests Better validation resiliency is achieved through more robust exception handling. For example, it is possible for a ``DataSchema`` to be 100% valid from the POV of built-in schema validation, but if the "data" section itself is utterly invalid, then an exception will be raised -- such an exception is treated as a critical failure. Better generation of error messages is achieved by creation more validation error message results. DataSchema validation was previously wonky. A DataSchema had to first be created in 1 revision before it could be referenced by a batch of documents in sequential revisions. Now, a DataSchema can be created in the same (or previous) revision as documents that rely on it and used to validate said documents. Finally, the module was heavily rewritten so that more nuanced validations can be built by inheriting from ``BaseValidator`` so as to allow for easier code readability and maintainability. Change-Id: Ie75742b984b7ad392cb41decc203d42842050c80
257 lines
6.4 KiB
Bash
Executable File
257 lines
6.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Script intended for running Deckhand functional tests via gabbi. Requires
|
|
# Docker CE (at least) to run.
|
|
|
|
# Meant for capturing output of Deckhand image. This requires that logging
|
|
# in the image be set up to pipe everything out to stdout/stderr.
|
|
STDOUT=$(mktemp)
|
|
# NOTE(fmontei): `DECKHAND_IMAGE` should only be specified if the desire is to
|
|
# run Deckhand functional tests against a specific Deckhand image, which is
|
|
# useful for CICD (as validating the image is vital). However, if the
|
|
# `DECKHAND_IMAGE` is not specified, then this implies that the most current
|
|
# version of the code should be used, which is in the repo itself.
|
|
DECKHAND_IMAGE=${DECKHAND_IMAGE:-}
|
|
|
|
function log_section {
|
|
set +x
|
|
echo 1>&2
|
|
echo 1>&2
|
|
echo === $* === 1>&2
|
|
set -x
|
|
}
|
|
|
|
set -ex
|
|
|
|
function cleanup {
|
|
sudo docker stop $POSTGRES_ID
|
|
if [ -n "$DECKHAND_ID" ]; then
|
|
sudo docker stop $DECKHAND_ID
|
|
fi
|
|
rm -rf $CONF_DIR
|
|
|
|
if [ -z "$DECKHAND_IMAGE" ]; then
|
|
# Kill all processes and child processes (for example, if workers > 1)
|
|
# if using uwsgi only.
|
|
PGID=$(ps -o comm -o pgid | grep uwsgi | grep -o [0-9]* | head -n 1)
|
|
setsid kill -- -$PGID
|
|
fi
|
|
}
|
|
|
|
|
|
trap cleanup EXIT
|
|
|
|
|
|
POSTGRES_ID=$(
|
|
sudo docker run \
|
|
--detach \
|
|
--publish :5432 \
|
|
-e POSTGRES_DB=deckhand \
|
|
-e POSTGRES_USER=deckhand \
|
|
-e POSTGRES_PASSWORD=password \
|
|
postgres:9.5
|
|
)
|
|
|
|
POSTGRES_IP=$(
|
|
sudo docker inspect \
|
|
--format='{{ .NetworkSettings.Networks.bridge.IPAddress }}' \
|
|
$POSTGRES_ID
|
|
)
|
|
|
|
|
|
CONF_DIR=$(mktemp -d -p $(pwd))
|
|
sudo chmod 777 -R $CONF_DIR
|
|
|
|
function gen_config {
|
|
log_section Creating config file
|
|
|
|
export DECKHAND_TEST_URL=http://localhost:9000
|
|
export DATABASE_URL=postgresql+psycopg2://deckhand:password@$POSTGRES_IP:5432/deckhand
|
|
# Used by Deckhand's initialization script to search for config files.
|
|
export OS_DECKHAND_CONFIG_DIR=$CONF_DIR
|
|
|
|
cp etc/deckhand/logging.conf.sample $CONF_DIR/logging.conf
|
|
|
|
# Create a logging config file to dump everything to stdout/stderr.
|
|
cat <<EOCONF > $CONF_DIR/logging.conf
|
|
[loggers]
|
|
keys = root, deckhand, error
|
|
|
|
[handlers]
|
|
keys = null, stderr, stdout
|
|
|
|
[formatters]
|
|
keys = simple, context
|
|
|
|
[logger_deckhand]
|
|
level = DEBUG
|
|
handlers = stdout
|
|
qualname = deckhand
|
|
|
|
[logger_error]
|
|
level = ERROR
|
|
handlers = stderr
|
|
|
|
[logger_root]
|
|
level = WARNING
|
|
handlers = null
|
|
|
|
[handler_stderr]
|
|
class = StreamHandler
|
|
args = (sys.stderr,)
|
|
formatter = context
|
|
|
|
[handler_stdout]
|
|
class = StreamHandler
|
|
args = (sys.stdout,)
|
|
formatter = context
|
|
|
|
[handler_null]
|
|
class = logging.NullHandler
|
|
formatter = context
|
|
args = ()
|
|
|
|
[formatter_context]
|
|
class = oslo_log.formatters.ContextFormatter
|
|
|
|
[formatter_simple]
|
|
format=%(asctime)s.%(msecs)03d %(process)d %(levelname)s: %(message)s
|
|
EOCONF
|
|
|
|
# Create a Deckhand config file with bare minimum options.
|
|
cat <<EOCONF > $CONF_DIR/deckhand.conf
|
|
[DEFAULT]
|
|
debug = true
|
|
publish_errors = true
|
|
use_stderr = true
|
|
# NOTE: allow_anonymous_access allows these functional tests to get around
|
|
# Keystone authentication, but the context that is provided has zero privileges
|
|
# so we must also override the policy file for authorization to pass.
|
|
allow_anonymous_access = true
|
|
|
|
[oslo_policy]
|
|
policy_file = policy.yaml
|
|
|
|
[barbican]
|
|
|
|
[database]
|
|
connection = $DATABASE_URL
|
|
|
|
[keystone_authtoken]
|
|
# Populate keystone_authtoken with values like the following should Keystone
|
|
# integration be needed here.
|
|
# project_domain_name = Default
|
|
# project_name = admin
|
|
# user_domain_name = Default
|
|
# password = devstack
|
|
# username = admin
|
|
# auth_url = http://127.0.0.1/identity
|
|
# auth_type = password
|
|
EOCONF
|
|
|
|
# Only set up logging if running Deckhand via uwsgi. The container already has
|
|
# values for logging.
|
|
if [ -z "$DECKHAND_IMAGE" ]; then
|
|
sed '1 a log_config_append = '"$CONF_DIR"'/logging.conf' $CONF_DIR/deckhand.conf
|
|
fi
|
|
|
|
# Only set up logging if running Deckhand via uwsgi. The container already has
|
|
# values for logging.
|
|
if [ -z "$DECKHAND_IMAGE" ]; then
|
|
sed '1 a log_config_append = '"$CONF_DIR"'/logging.conf' $CONF_DIR/deckhand.conf
|
|
fi
|
|
|
|
echo $CONF_DIR/deckhand.conf 1>&2
|
|
cat $CONF_DIR/deckhand.conf 1>&2
|
|
|
|
echo $CONF_DIR/logging.conf 1>&2
|
|
cat $CONF_DIR/logging.conf 1>&2
|
|
|
|
log_section Starting server
|
|
}
|
|
|
|
function gen_paste {
|
|
log_section Creating paste config without [filter:authtoken]
|
|
# NOTE(fmontei): Since this script does not currently support Keystone
|
|
# integration, we remove ``filter:authtoken`` from the ``deckhand_api``
|
|
# pipeline to avoid any kind of auth issues.
|
|
sed 's/authtoken api/api/' etc/deckhand/deckhand-paste.ini &> $CONF_DIR/deckhand-paste.ini
|
|
}
|
|
|
|
function gen_policy {
|
|
log_section Creating policy file with liberal permissions
|
|
|
|
policy_file='etc/deckhand/policy.yaml.sample'
|
|
policy_pattern="deckhand\:"
|
|
|
|
touch $CONF_DIR/policy.yaml
|
|
|
|
sed -n "/$policy_pattern/p" "$policy_file" \
|
|
| sed 's/^../\"/' \
|
|
| sed 's/rule\:[A-Za-z\_\-]*/@/' > $CONF_DIR/policy.yaml
|
|
|
|
echo $CONF_DIR/'policy.yaml' 1>&2
|
|
cat $CONF_DIR/'policy.yaml' 1>&2
|
|
}
|
|
|
|
gen_config
|
|
gen_paste
|
|
gen_policy
|
|
|
|
if [ -z "$DECKHAND_IMAGE" ]; then
|
|
log_section "Running Deckhand via uwsgi"
|
|
|
|
# Set --workers 2, so that concurrency is always tested.
|
|
uwsgi \
|
|
--http :9000 \
|
|
-w deckhand.cmd \
|
|
--callable deckhand_callable \
|
|
--enable-threads \
|
|
--workers 2 \
|
|
--threads 1 \
|
|
-L \
|
|
--pyargv "--config-file $CONF_DIR/deckhand.conf" &
|
|
else
|
|
log_section "Running Deckhand via Docker"
|
|
sudo docker run \
|
|
--rm \
|
|
--net=host \
|
|
-p 9000:9000 \
|
|
-v $CONF_DIR:/etc/deckhand \
|
|
$DECKHAND_IMAGE &> $STDOUT &
|
|
fi
|
|
|
|
# Give the server a chance to come up. Better to poll a health check.
|
|
sleep 5
|
|
|
|
DECKHAND_ID=$(sudo docker ps | grep deckhand | awk '{print $1}')
|
|
echo $DECKHAND_ID
|
|
|
|
log_section Running tests
|
|
|
|
ROOTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
|
|
# Create folder for saving HTML test results.
|
|
if [ ! -d $ROOTDIR/results ]; then
|
|
mkdir $ROOTDIR/results
|
|
fi
|
|
|
|
set +e
|
|
posargs=$@
|
|
if [ ${#posargs} -ge 1 ]; then
|
|
py.test -k $1 -svx $( dirname $ROOTDIR )/deckhand/tests/functional/test_gabbi.py --html=results/index.html
|
|
else
|
|
py.test -svx $( dirname $ROOTDIR )/deckhand/tests/functional/test_gabbi.py --html=results/index.html
|
|
fi
|
|
TEST_STATUS=$?
|
|
set -e
|
|
|
|
if [ "x$TEST_STATUS" = "x0" ]; then
|
|
log_section Done SUCCESS
|
|
else
|
|
log_section Deckhand Server Log
|
|
cat $STDOUT
|
|
log_section Done FAILURE
|
|
exit $TEST_STATUS
|
|
fi
|