deckhand/tools/functional-tests.sh
Felipe Monteiro 453927facf Improve document validation module.
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
2018-01-15 16:51:52 -05:00

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