mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2026-03-15 17:37:22 +01:00
Merge branch 'master' into quota
This commit is contained in:
@@ -201,7 +201,25 @@ lx_wait_for_boot() {
|
||||
echo ""
|
||||
echo -n "Wait for cloud-init "
|
||||
lxc --project "$project" exec "$inst" -- cloud-init status --wait
|
||||
local rc=$?
|
||||
|
||||
if [ $rc -eq 0 ]; then
|
||||
echo "Wait for ip address "
|
||||
local ip=""
|
||||
local count=0
|
||||
while [ $count -lt 10 ]; do
|
||||
let count+=1
|
||||
ip="$(lxc --project "$project" exec "$inst" -- hostname -I | awk '{print $1}')"
|
||||
rc=$?
|
||||
echo " [${count}] got: $ip"
|
||||
if [ $rc -ne 0 -o "$ip" != "" ];then
|
||||
break
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
fi
|
||||
echo ""
|
||||
return $rc
|
||||
}
|
||||
|
||||
lx_get_ssh_identity() {
|
||||
|
||||
@@ -67,7 +67,7 @@ provision_shell() {
|
||||
|
||||
else
|
||||
local tmp=$(mktemp)
|
||||
echo "#!/bin/sh" >"$tmp"
|
||||
echo "#!/bin/bash" >"$tmp"
|
||||
cat >>"$tmp"
|
||||
lxc --project "$project" file push "$tmp" "${inst}${remote_path}" $lxc_flags || return 1
|
||||
rm -f "$tmp"
|
||||
|
||||
@@ -37,6 +37,12 @@
|
||||
D=$(dirname "$BASH_SOURCE")
|
||||
. "$D/lx_functions.sh" || exit 1
|
||||
|
||||
show_cl="yes"
|
||||
if [ "$1" = "-q" ]; then
|
||||
show_cl="no"
|
||||
shift
|
||||
fi
|
||||
|
||||
vlx_guess() {
|
||||
if [ $# -eq 2 ]; then
|
||||
LX_PROJECT="$1"
|
||||
@@ -83,7 +89,6 @@ vlx_exec() {
|
||||
if [ "${args[$idx]}" = "--" ]; then
|
||||
if [ $idx -eq 3 ]; then
|
||||
# format 1 with cwd
|
||||
echo "f1"
|
||||
wd="$3"
|
||||
vlx_guess "$1" "$2" || return 1
|
||||
shift; shift; shift; shift;
|
||||
@@ -92,12 +97,10 @@ vlx_exec() {
|
||||
if [ "${2#/}" != "$2" ]; then
|
||||
# wd starts with /, so it's a path
|
||||
# format 2
|
||||
echo "f2"
|
||||
wd="$2"
|
||||
vlx_guess "" "$1" || return 1
|
||||
else
|
||||
# format 1 w/o cwd
|
||||
echo "f1 w/o cwd"
|
||||
vlx_guess "$1" "$2" || return 1
|
||||
fi
|
||||
shift; shift; shift;
|
||||
@@ -106,12 +109,10 @@ vlx_exec() {
|
||||
if [ "${1#/}" != "$1" ]; then
|
||||
# wd starts with /, so it's a path
|
||||
# format 3
|
||||
echo "f3"
|
||||
wd="$1"
|
||||
vlx_guess || return 1
|
||||
else
|
||||
# format 2 w/o cwd
|
||||
echo "f2 w/o cwd"
|
||||
vlx_guess "$1" || return 1
|
||||
fi
|
||||
shift; shift;
|
||||
@@ -121,7 +122,6 @@ vlx_exec() {
|
||||
fi
|
||||
else
|
||||
# format 4
|
||||
echo "f4"
|
||||
vlx_guess || return 1
|
||||
fi
|
||||
|
||||
@@ -129,28 +129,43 @@ vlx_exec() {
|
||||
if [ ! -z "$wd" ]; then
|
||||
xargs="--cwd $wd"
|
||||
fi
|
||||
echo lxc --project "$LX_PROJECT" exec "$LX_INST" $xargs -- "$@"
|
||||
[ "$show_cl" = "yes" ] &&
|
||||
echo lxc --project "$LX_PROJECT" exec "$LX_INST" $xargs -- "$@"
|
||||
lxc --project "$LX_PROJECT" exec "$LX_INST" $xargs -- "$@"
|
||||
}
|
||||
|
||||
vlx_shell() {
|
||||
vlx_guess "$@" || return 1
|
||||
echo lxc --project "$LX_PROJECT" exec "$LX_INST" -- bash
|
||||
[ "$show_cl" = "yes" ] &&
|
||||
echo lxc --project "$LX_PROJECT" exec "$LX_INST" -- bash
|
||||
lxc --project "$LX_PROJECT" exec "$LX_INST" -- bash
|
||||
}
|
||||
|
||||
vlx_hostname() {
|
||||
vlx_guess "$@" || return 1
|
||||
local host
|
||||
lxc --project "$LX_PROJECT" exec "$LX_INST" -- /usr/bin/hostname --fqdn || return 1
|
||||
}
|
||||
|
||||
vlx_ipaddr() {
|
||||
vlx_guess "$@" || return 1
|
||||
local hostip
|
||||
hostip="$(lxc --project "$LX_PROJECT" exec "$LX_INST" -- /usr/bin/hostname -I)"
|
||||
[ $? -ne 0 -o -z "$hostip" ] && return 1
|
||||
awk '{print $1}' <<<"$hostip"
|
||||
}
|
||||
|
||||
|
||||
vlx_ssh() {
|
||||
local host="$1"
|
||||
if [ "$host" = "--" ]; then
|
||||
host=""
|
||||
else
|
||||
shift
|
||||
fi
|
||||
if [ -z "$host" ]; then
|
||||
host="$(vlx_hostname)"
|
||||
host="$(vlx_ipaddr)"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Could not determine hostname, please specify"
|
||||
echo "Could not determine ip address, please specify"
|
||||
host=""
|
||||
fi
|
||||
if [ -z "$host" ]; then
|
||||
@@ -161,20 +176,22 @@ vlx_ssh() {
|
||||
local id="$(lx_get_ssh_identity)"
|
||||
local known_hosts="$(lx_get_ssh_known_hosts)"
|
||||
local vmuser="vmuser"
|
||||
#echo ssh -i "$id" -o UserKnownHostsFile="$known_hosts" -o StrictHostKeyChecking=no "$vmuser@$host"
|
||||
#echo ssh -i "$id" -o UserKnownHostsFile="$known_hosts" -o StrictHostKeyChecking=no "$vmuser@$host" "$@"
|
||||
echo "Connecting to $vmuser@$host ..."
|
||||
ssh -i "$id" -o UserKnownHostsFile="$known_hosts" -o StrictHostKeyChecking=no "$vmuser@$host"
|
||||
ssh -i "$id" -o UserKnownHostsFile="$known_hosts" -o StrictHostKeyChecking=no "$vmuser@$host" "$@"
|
||||
}
|
||||
|
||||
vlx_list() {
|
||||
vlx_guess "$1" || return 1
|
||||
echo lxc --project "$LX_PROJECT" list
|
||||
[ "$show_cl" = "yes" ] &&
|
||||
echo lxc --project "$LX_PROJECT" list
|
||||
lxc --project "$LX_PROJECT" list
|
||||
}
|
||||
|
||||
vlx_images() {
|
||||
vlx_guess "$1" || return 1
|
||||
echo lxc --project "$LX_PROJECT" image list
|
||||
[ "$show_cl" = "yes" ] &&
|
||||
echo lxc --project "$LX_PROJECT" image list
|
||||
lxc --project "$LX_PROJECT" image list
|
||||
}
|
||||
|
||||
@@ -191,19 +208,22 @@ vlx_up() {
|
||||
|
||||
vlx_start() {
|
||||
vlx_guess "$@" || return 1
|
||||
echo lxc --project "$LX_PROJECT" start "$LX_INST"
|
||||
[ "$show_cl" = "yes" ] &&
|
||||
echo lxc --project "$LX_PROJECT" start "$LX_INST"
|
||||
lxc --project "$LX_PROJECT" start "$LX_INST"
|
||||
}
|
||||
|
||||
vlx_stop() {
|
||||
vlx_guess "$@" || return 1
|
||||
echo lxc --project "$LX_PROJECT" stop "$LX_INST"
|
||||
[ "$show_cl" = "yes" ] &&
|
||||
echo lxc --project "$LX_PROJECT" stop "$LX_INST"
|
||||
lxc --project "$LX_PROJECT" stop "$LX_INST"
|
||||
}
|
||||
|
||||
vlx_delete() {
|
||||
vlx_guess "$@" || return 1
|
||||
echo lxc --project "$LX_PROJECT" delete --force --interactive "$LX_INST"
|
||||
[ "$show_cl" = "yes" ] &&
|
||||
echo lxc --project "$LX_PROJECT" delete --force --interactive "$LX_INST"
|
||||
lxc --project "$LX_PROJECT" delete --force --interactive "$LX_INST"
|
||||
}
|
||||
|
||||
@@ -224,7 +244,8 @@ vlx_status() {
|
||||
|
||||
vlx_restart() {
|
||||
vlx_guess "$@" || return 1
|
||||
echo lxc --project "$LX_PROJECT" restart "$LX_INST"
|
||||
[ "$show_cl" = "yes" ] &&
|
||||
echo lxc --project "$LX_PROJECT" restart "$LX_INST"
|
||||
lxc --project "$LX_PROJECT" restart "$LX_INST"
|
||||
}
|
||||
|
||||
|
||||
@@ -26,10 +26,11 @@ class NcContactsAutomation(object):
|
||||
els = d.find_els('div.contacts-list div.list-item-content,div.option__details')
|
||||
d.say_verbose('found %s contacts' % len(els))
|
||||
for el in els:
|
||||
# .line-one (nc 25+)
|
||||
# .list-item-content__name (nc 29+)
|
||||
# .line-one (nc 25-28)
|
||||
# .option__lineone (nc <25)
|
||||
fullname = el.find_el('.line-one,.option__lineone').content().strip()
|
||||
email = el.find_el('.line-two,.option__linetwo').content().strip()
|
||||
fullname = el.find_el('.line-one,.option__lineone,.list-item-content__name').content().strip()
|
||||
email = el.find_el('.line-two,.option__linetwo,.list-item-content__subname').content().strip()
|
||||
d.say_verbose('contact: "%s" <%s>', fullname, email)
|
||||
# NC 28: email not present in html
|
||||
ignore_email = True if email == '' else False
|
||||
@@ -43,7 +44,7 @@ class NcContactsAutomation(object):
|
||||
d = self.d
|
||||
d.say("Wait for contact to load")
|
||||
d.wait_for_el('section.contact-details', secs=secs)
|
||||
|
||||
|
||||
def delete_current_contact(self):
|
||||
d = self.d
|
||||
d.say("Delete current contact")
|
||||
|
||||
@@ -58,7 +58,10 @@ class NextcloudAutomation(object):
|
||||
d.say("Logout of Nextcloud")
|
||||
self.click_avatar()
|
||||
|
||||
el = d.find_el('[data-id="logout"] a', throws=False) # nc < 26
|
||||
el = d.find_el('a#logout', throws=False)
|
||||
if not el:
|
||||
# nc >= 29
|
||||
el = d.find_el('[data-id="logout"] a', throws=False) # nc < 26
|
||||
if not el:
|
||||
# nc >= 26
|
||||
el = d.find_el('#logout > a', throws=False)
|
||||
@@ -72,7 +75,10 @@ class NextcloudAutomation(object):
|
||||
d = self.d
|
||||
d.say("Open contacts")
|
||||
# nc 25+
|
||||
el = d.find_el('header [data-app-id="contacts"]', throws=False)
|
||||
el = d.find_el('header [title="Contacts"]', throws=False)
|
||||
if not el:
|
||||
# nc < 29
|
||||
el = d.find_el('header [data-app-id="contacts"]', throws=False)
|
||||
if not el:
|
||||
# nc < 25
|
||||
el = d.find_el('header [data-id="contacts"]')
|
||||
|
||||
1
tests/lxd/create-backup/README.md
Normal file
1
tests/lxd/create-backup/README.md
Normal file
@@ -0,0 +1 @@
|
||||
TODO
|
||||
1
tests/lxd/majorupgrade/README.md
Normal file
1
tests/lxd/majorupgrade/README.md
Normal file
@@ -0,0 +1 @@
|
||||
TODO - convert to lxd
|
||||
@@ -17,7 +17,7 @@ D=$(dirname "$BASH_SOURCE")
|
||||
provision_start "" "/mailinabox" || exit 1
|
||||
|
||||
# Setup system
|
||||
if [ "$1" = "ciab" ]; then
|
||||
if [ "$TESTS" = "ciab" -o "$1" = "ciab" ]; then
|
||||
# use a remote cloudinabox (does not have to be running)
|
||||
provision_shell <<<"
|
||||
cd /mailinabox
|
||||
@@ -44,7 +44,7 @@ exit \$rc
|
||||
"
|
||||
provision_done $?
|
||||
|
||||
else
|
||||
elif [ -z "$1" ]; then
|
||||
# vanilla (default - no miab integration)
|
||||
provision_shell <<<"
|
||||
cd /mailinabox
|
||||
@@ -62,4 +62,7 @@ exit \$rc
|
||||
"
|
||||
provision_done $?
|
||||
|
||||
else
|
||||
echo "Invalid argument: $1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
##### details.
|
||||
#####
|
||||
|
||||
|
||||
from browser.automation import (
|
||||
TestDriver,
|
||||
TimeoutException,
|
||||
@@ -50,16 +51,20 @@ try:
|
||||
# open Contacts
|
||||
#
|
||||
d.start("Open contacts app")
|
||||
contacts = nc.open_contacts()
|
||||
try:
|
||||
contacts = nc.open_contacts()
|
||||
except NoSuchElementException:
|
||||
nc.close_first_run_wizard()
|
||||
contacts = nc.open_contacts()
|
||||
nc.wait_for_app_load()
|
||||
|
||||
#
|
||||
# handle selected operation
|
||||
# handle selected operation
|
||||
#
|
||||
if op=='exists':
|
||||
d.start("Check that contact %s exists", contact['email'])
|
||||
contacts.click_contact(contact) # raises NoSuchElementException if not found
|
||||
|
||||
|
||||
elif op=='delete':
|
||||
d.start("Delete contact %s", contact['email'])
|
||||
contacts.click_contact(contact)
|
||||
@@ -68,7 +73,7 @@ try:
|
||||
|
||||
elif op=='nop':
|
||||
pass
|
||||
|
||||
|
||||
else:
|
||||
raise ValueError('Invalid operation: %s' % op)
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ test_web_config() {
|
||||
record "output=$REST_OUTPUT"
|
||||
if [ $code -eq 0 ]; then
|
||||
test_failure "carddav url works, but expecting 401/NotAuthenticated from server"
|
||||
elif [ $code -eq 1 -o $REST_HTTP_CODE -ne 401 ] || ! grep "NotAuthenticated" <<<"$REST_OUTPUT" >/dev/null; then
|
||||
elif [ $REST_HTTP_CODE -ne 401 ]; then
|
||||
test_failure "carddav url doesn't work: $REST_ERROR"
|
||||
fi
|
||||
fi
|
||||
@@ -189,7 +189,7 @@ test_web_config() {
|
||||
record "output=$REST_OUTPUT"
|
||||
if [ $code -eq 0 ]; then
|
||||
test_failure "caldav url works, but expecting 401/NotAuthenticated from server"
|
||||
elif [ $code -eq 1 -o $REST_HTTP_CODE -ne 401 ] || ! grep "NotAuthenticated" <<<"$REST_OUTPUT" >/dev/null; then
|
||||
elif [ $REST_HTTP_CODE -ne 401 ]; then
|
||||
test_failure "caldav url doesn't work: $REST_ERROR"
|
||||
fi
|
||||
fi
|
||||
|
||||
3
tests/vagrant/.gitignore
vendored
3
tests/vagrant/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
.vagrant
|
||||
out
|
||||
*-console.log
|
||||
104
tests/vagrant/Vagrantfile
vendored
104
tests/vagrant/Vagrantfile
vendored
@@ -1,104 +0,0 @@
|
||||
#####
|
||||
##### This file is part of Mail-in-a-Box-LDAP which is released under the
|
||||
##### terms of the GNU Affero General Public License as published by the
|
||||
##### Free Software Foundation, either version 3 of the License, or (at
|
||||
##### your option) any later version. See file LICENSE or go to
|
||||
##### https://github.com/downtownallday/mailinabox-ldap for full license
|
||||
##### details.
|
||||
#####
|
||||
|
||||
load './funcs.rb'
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
|
||||
config.vm.synced_folder "../..", "/mailinabox", id: "mailinabox", automount: false
|
||||
use_preloaded_box config, "ubuntu/jammy64"
|
||||
|
||||
# fresh install with encryption-at-rest
|
||||
|
||||
if ENV['tests']=='all'
|
||||
config.vm.define "remote-nextcloud-docker-ehdd" do |m1|
|
||||
m1.vm.provision :shell, :inline => <<-SH
|
||||
cd /mailinabox
|
||||
export PRIMARY_HOSTNAME=qa1.abc.com
|
||||
export FEATURE_MUNIN=false
|
||||
export EHDD_KEYFILE=$HOME/keyfile
|
||||
echo -n "boo" >$EHDD_KEYFILE
|
||||
tests/system-setup/remote-nextcloud-docker.sh || exit 1
|
||||
tests/runner.sh -no-smtp-remote remote-nextcloud ehdd default || exit 2
|
||||
SH
|
||||
end
|
||||
end
|
||||
|
||||
# remote-nextcloud-docker w/basic data
|
||||
|
||||
config.vm.define "remote-nextcloud-docker" do |m1|
|
||||
m1.vm.provision :shell, :inline => <<-SH
|
||||
cd /mailinabox
|
||||
export PRIMARY_HOSTNAME=qa2.abc.com
|
||||
export FEATURE_MUNIN=false
|
||||
tests/system-setup/remote-nextcloud-docker.sh upgrade --populate=basic || exit 1
|
||||
tests/runner.sh -no-smtp-remote remote-nextcloud upgrade-basic default || exit 2
|
||||
SH
|
||||
end
|
||||
|
||||
|
||||
# upgrade-from-upstream
|
||||
|
||||
config.vm.define "upgrade-from-upstream" do |m1|
|
||||
m1.vm.provision :shell, :inline => <<-SH
|
||||
cd /mailinabox
|
||||
export PRIMARY_HOSTNAME=qa3.abc.com
|
||||
# TODO: change UPSTREAM_TAG to 'main' once upstream is installable
|
||||
export UPSTREAM_TAG=v67
|
||||
tests/system-setup/upgrade-from-upstream.sh --populate=basic --populate=totpuser || exit 1
|
||||
tests/runner.sh -no-smtp-remote upgrade-basic upgrade-totpuser default || exit 2
|
||||
SH
|
||||
end
|
||||
|
||||
# upgrade
|
||||
|
||||
# this test is only needed when testing migrations from miabldap
|
||||
# to a newer miabldap with a migration step
|
||||
#
|
||||
# upgrade will handle testing upgrades of
|
||||
# miabldap with or without a new migration step
|
||||
config.vm.define "upgrade" do |m1|
|
||||
m1.vm.provision :shell, :inline => <<-SH
|
||||
cd /mailinabox
|
||||
# TODO: remove DEB_PYTHON_INSTALL_LAYOUT once MIABLDAP_RELEASE_TAG >= v66 (see https://github.com/downtownallday/mailinabox-ldap/commit/371f5bc1b236de40a1ed5d9118140ee13fddf5dc)
|
||||
export DEB_PYTHON_INSTALL_LAYOUT='deb'
|
||||
export PRIMARY_HOSTNAME=upgrade.abc.com
|
||||
tests/system-setup/upgrade.sh --populate=basic --populate=totpuser || exit 1
|
||||
tests/runner.sh -no-smtp-remote upgrade-basic upgrade-totpuser default || exit 2
|
||||
SH
|
||||
end
|
||||
|
||||
# unsetvars: because miab sets bash '-e' to fail any setup script
|
||||
# when a script command returns a non-zero exit code, and more
|
||||
# importantly '-u' which fails scripts when any unset variable is
|
||||
# accessed, this definition sets a minimal number of environment
|
||||
# variables prior to running start.sh. Doing so will test that no
|
||||
# failures occur during setup in the most common use case because
|
||||
# other vagrant definitions in this file load
|
||||
# tests/system-setup/setup-default.sh, which pre-assign a value to
|
||||
# most variables.
|
||||
|
||||
if ENV['tests']=='all' or ENV['tests']=='pre-commit'
|
||||
config.vm.define "unsetvars" do |m1|
|
||||
m1.vm.hostname = "mailinabox.lan"
|
||||
m1.vm.network "private_network", ip: "192.168.56.4"
|
||||
m1.vm.provision :shell, :inline => <<-SH
|
||||
export NONINTERACTIVE=1
|
||||
export PUBLIC_IP=auto
|
||||
export PUBLIC_IPV6=auto
|
||||
export PRIMARY_HOSTNAME=auto
|
||||
export SKIP_NETWORK_CHECKS=1
|
||||
cd /mailinabox
|
||||
setup/start.sh
|
||||
SH
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
@@ -1,25 +0,0 @@
|
||||
#####
|
||||
##### This file is part of Mail-in-a-Box-LDAP which is released under the
|
||||
##### terms of the GNU Affero General Public License as published by the
|
||||
##### Free Software Foundation, either version 3 of the License, or (at
|
||||
##### your option) any later version. See file LICENSE or go to
|
||||
##### https://github.com/downtownallday/mailinabox-ldap for full license
|
||||
##### details.
|
||||
#####
|
||||
|
||||
def use_preloaded_box(obj, name, preloaded_dir=".")
|
||||
obj.vm.box = String.new(name)
|
||||
_name=name.sub! '/','-' # ubuntu/bionic64 => ubuntu-bionic64
|
||||
if File.file?("#{preloaded_dir}/preloaded/preloaded-#{_name}.box")
|
||||
# box name needs to be unique on the system
|
||||
obj.vm.box = "preloaded-miabldap-#{_name}"
|
||||
obj.vm.box_url = "file://" + Dir.pwd + "/#{preloaded_dir}/preloaded/preloaded-#{_name}.box"
|
||||
if Vagrant.has_plugin?('vagrant-vbguest')
|
||||
# do not update additions when booting this machine
|
||||
obj.vbguest.auto_update = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Grab the name of the default interface
|
||||
$default_network_interface = `ip route | awk '/^default/ {printf "%s", $5; exit 0}'`
|
||||
@@ -1,86 +0,0 @@
|
||||
#!/bin/bash
|
||||
#####
|
||||
##### This file is part of Mail-in-a-Box-LDAP which is released under the
|
||||
##### terms of the GNU Affero General Public License as published by the
|
||||
##### Free Software Foundation, either version 3 of the License, or (at
|
||||
##### your option) any later version. See file LICENSE or go to
|
||||
##### https://github.com/downtownallday/mailinabox-ldap for full license
|
||||
##### details.
|
||||
#####
|
||||
|
||||
|
||||
# Parallel provisioning for virtualbox because "The Vagrant VirtualBox
|
||||
# provider does not support parallel execution at this time"
|
||||
# (https://www.vagrantup.com/docs/providers/virtualbox/usage.html)
|
||||
#
|
||||
# Credit to:
|
||||
# https://dzone.com/articles/parallel-provisioning-speeding
|
||||
#
|
||||
|
||||
. "$(dirname "$0")/../lib/color-output.sh"
|
||||
. "$(dirname "$0")/../lib/misc.sh"
|
||||
|
||||
if [ -z "$tests" ]; then
|
||||
export tests="pre-commit"
|
||||
fi
|
||||
|
||||
|
||||
OUTPUT_DIR=out
|
||||
#rm -rf "$OUTPUT_DIR"
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# set total parallel vms to (#cores minus 1)
|
||||
MAX_PROCS=$(cat /proc/cpuinfo | grep processor | wc -l)
|
||||
let MAX_PROCS-=1
|
||||
|
||||
|
||||
parallel_provision() {
|
||||
while read box; do
|
||||
outfile="$OUTPUT_DIR/$box.out.txt"
|
||||
rm -f "$outfile"
|
||||
echo "Provisioning '$box'. Output will be in: $outfile" 1>&2
|
||||
echo $box
|
||||
done | xargs -P $MAX_PROCS -I"BOXNAME" \
|
||||
sh -c 'vagrant provision BOXNAME >'"$OUTPUT_DIR/"'BOXNAME.out.txt 2>&1 && echo "EXITCODE: 0" >> '"$OUTPUT_DIR/"'BOXNAME.out.txt || echo "EXITCODE: $?" >>'"$OUTPUT_DIR/"'BOXNAME.out.txt'
|
||||
}
|
||||
|
||||
## -- main -- ##
|
||||
|
||||
start_time="$(date +%s)"
|
||||
|
||||
# start boxes sequentially to avoid vbox explosions
|
||||
vagrant up --no-provision
|
||||
|
||||
# but run provision tasks in parallel
|
||||
boxes="$(vagrant status | awk '/running \(/ {print $1}')"
|
||||
echo "$boxes" | parallel_provision
|
||||
|
||||
|
||||
# output overall result - Vagrantfile script must output "EXITCODE: <num>"
|
||||
H1 "Results"
|
||||
|
||||
rc=0
|
||||
for box in $boxes; do
|
||||
file="$OUTPUT_DIR"/$box.out.txt
|
||||
exitcode="$(tail "$file" | grep EXITCODE: | awk '{print $NF}')"
|
||||
echo -n "$box: "
|
||||
if [ -z "$exitcode" ]; then
|
||||
danger "NO EXITCODE!"
|
||||
[ $rc -eq 0 ] && rc=2
|
||||
elif [ "$exitcode" == "0" ]; then
|
||||
success "SUCCESS"
|
||||
else
|
||||
danger "FAILURE ($exitcode)"
|
||||
rc=1
|
||||
fi
|
||||
done
|
||||
|
||||
# output elapsed time
|
||||
end_time="$(date +%s)"
|
||||
echo ""
|
||||
echo "Elapsed time: $(elapsed_pretty $start_time $end_time)"
|
||||
|
||||
# exit
|
||||
echo ""
|
||||
echo "Guest VMs are running! Destroy them with 'vagrant destroy -f'"
|
||||
exit $rc
|
||||
2
tests/vagrant/preloaded/.gitignore
vendored
2
tests/vagrant/preloaded/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
*.box
|
||||
src/
|
||||
58
tests/vagrant/preloaded/Vagrantfile
vendored
58
tests/vagrant/preloaded/Vagrantfile
vendored
@@ -1,58 +0,0 @@
|
||||
#####
|
||||
##### This file is part of Mail-in-a-Box-LDAP which is released under the
|
||||
##### terms of the GNU Affero General Public License as published by the
|
||||
##### Free Software Foundation, either version 3 of the License, or (at
|
||||
##### your option) any later version. See file LICENSE or go to
|
||||
##### https://github.com/downtownallday/mailinabox-ldap for full license
|
||||
##### details.
|
||||
#####
|
||||
|
||||
def checkout_tag_and_mount(obj, tag)
|
||||
if "#{tag}" == ""
|
||||
obj.vm.synced_folder "../../..", "/mailinabox", id: "mailinabox", automount: false
|
||||
else
|
||||
_srcdir="src/maibldap-#{tag}"
|
||||
if not Dir.exist?(_srcdir)
|
||||
puts "Cloning tag #{tag} to #{_srcdir}"
|
||||
if tag.size==40 and tag.match?(/\A[0-9a-fA-F]+\Z/)
|
||||
system("git clone #{ENV['MIABLDAP_GIT']} #{_srcdir}")
|
||||
system("cd #{_srcdir}; git reset --hard #{tag}")
|
||||
else
|
||||
system("git clone -b #{tag} --depth 1 #{ENV['MIABLDAP_GIT']} #{_srcdir}")
|
||||
end
|
||||
end
|
||||
obj.vm.synced_folder _srcdir, "/mailinabox", id: "mailinabox", automount: false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
|
||||
checkout_tag_and_mount config, ENV['RELEASE_TAG']
|
||||
|
||||
config.vm.define "preloaded-ubuntu-bionic64" do |m1|
|
||||
m1.vm.box = "ubuntu/bionic64"
|
||||
m1.vm.provision :shell, :inline => <<-SH
|
||||
cd /mailinabox
|
||||
tests/vagrant/preloaded/prepvm.sh --no-dry-run
|
||||
rc=$?
|
||||
echo "$rc" > "/vagrant/prepcode.txt"
|
||||
[ $rc -gt 0 ] && exit 1
|
||||
exit 0
|
||||
SH
|
||||
end
|
||||
|
||||
config.vm.define "preloaded-ubuntu-jammy64" do |m1|
|
||||
m1.vm.box = "ubuntu/jammy64"
|
||||
m1.vm.boot_timeout = 30
|
||||
m1.vm.provision :shell, :inline => <<-SH
|
||||
cd /mailinabox
|
||||
tests/vagrant/preloaded/prepvm.sh --no-dry-run
|
||||
rc=$?
|
||||
echo "$rc" > "/vagrant/prepcode.txt"
|
||||
[ $rc -gt 0 ] && exit 1
|
||||
exit 0
|
||||
SH
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,134 +0,0 @@
|
||||
#!/bin/bash
|
||||
#####
|
||||
##### This file is part of Mail-in-a-Box-LDAP which is released under the
|
||||
##### terms of the GNU Affero General Public License as published by the
|
||||
##### Free Software Foundation, either version 3 of the License, or (at
|
||||
##### your option) any later version. See file LICENSE or go to
|
||||
##### https://github.com/downtownallday/mailinabox-ldap for full license
|
||||
##### details.
|
||||
#####
|
||||
|
||||
|
||||
# load defaults for MIABLDAP_GIT and MIABLDAP_FINAL_RELEASE_TAG_BIONIC64 (make available to Vagrantfile)
|
||||
pushd "../../.." >/dev/null
|
||||
source tests/lib/color-output.sh
|
||||
source tests/system-setup/setup-defaults.sh || exit 1
|
||||
popd >/dev/null
|
||||
|
||||
H1 "Destroy any running boxes"
|
||||
vagrant destroy -f
|
||||
rm -f prepcode.txt
|
||||
|
||||
H1 "Ensure plugins are installed"
|
||||
for plugin in "vagrant-vbguest" "vagrant-reload"
|
||||
do
|
||||
if ! vagrant plugin list | grep -F "$plugin" >/dev/null; then
|
||||
vagrant plugin install "$plugin" || exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
H1 "Upgrade base boxes"
|
||||
vagrant box update
|
||||
|
||||
|
||||
boxes=(
|
||||
"preloaded-ubuntu-jammy64"
|
||||
"preloaded-ubuntu-bionic64"
|
||||
)
|
||||
# preload packages from source of the following git tags. empty string
|
||||
# means use the current source tree
|
||||
tags=(
|
||||
""
|
||||
"$MIABLDAP_FINAL_RELEASE_TAG_BIONIC64"
|
||||
)
|
||||
try_reboot=(
|
||||
true
|
||||
false
|
||||
)
|
||||
idx=0
|
||||
|
||||
for box in "${boxes[@]}"
|
||||
do
|
||||
if [ -z "$1" ]; then
|
||||
# no cli arguments - only process first box
|
||||
[ $idx -ge 1 ] && break
|
||||
else
|
||||
# cli argument specifies "all" or a named box
|
||||
if [ "$1" != "all" -a "$1" != "$box" ]; then
|
||||
let idx+=1
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
H1 "Provision: $box"
|
||||
export RELEASE_TAG="${tags[$idx]}"
|
||||
vagrant up $box | tee /tmp/$box.out
|
||||
upcode=$?
|
||||
|
||||
if [ $upcode -eq 0 -a ! -e "./prepcode.txt" ] && ${try_reboot[$idx]} && grep -F 'Authentication failure' /tmp/$box.out >/dev/null; then
|
||||
# note: upcode is 0 only if config.vm.boot_timeout is set.
|
||||
# If this works it may be an indication that ruby's internal
|
||||
# ssh does not support the algorithm required by the server,
|
||||
# or the public key does not match (vagrant and vm out of
|
||||
# sync)
|
||||
echo ""
|
||||
echo "VAGRANT AUTHENTICATION FAILURE - TRYING LOOSER ALLOWED SSHD ALGS"
|
||||
if vagrant ssh $box -c "sudo bash -c 'echo PubkeyAcceptedAlgorithms +ssh-rsa > /etc/ssh/sshd_config.d/miabldap.conf; sudo systemctl restart sshd'"; then
|
||||
vagrant halt $box
|
||||
vagrant up $box
|
||||
upcode=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $upcode -ne 0 -a ! -e "./prepcode.txt" ] && ${try_reboot[$idx]}
|
||||
then
|
||||
# a reboot may be necessary if guest addtions was newly
|
||||
# compiled by vagrant plugin "vagrant-vbguest"
|
||||
echo ""
|
||||
echo "VAGRANT UP RETURNED $upcode -- RETRYING AFTER REBOOT"
|
||||
vagrant halt $box
|
||||
vagrant up $box
|
||||
upcode=$?
|
||||
fi
|
||||
|
||||
rm -f /tmp/$box.out
|
||||
|
||||
let idx+=1
|
||||
prepcode=$(cat "./prepcode.txt")
|
||||
rm -f prepcode.txt
|
||||
echo ""
|
||||
echo "VAGRANT UP RETURNED $upcode"
|
||||
echo "PREPVM RETURNED $prepcode"
|
||||
|
||||
if [ "$prepcode" != "0" -o $upcode -ne 0 ]; then
|
||||
echo "FAILED!!!!!!!!"
|
||||
vagrant destroy -f $box
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if vagrant ssh $box -- cat /var/run/reboot-required >/dev/null 2>&1; then
|
||||
echo "REBOOT REQUIRED"
|
||||
vagrant reload $box
|
||||
else
|
||||
echo "REBOOT NOT REQUIRED"
|
||||
fi
|
||||
|
||||
vagrant halt $box
|
||||
vagrant package $box
|
||||
rm -f $box.box
|
||||
mv package.box $box.box
|
||||
|
||||
vagrant destroy -f $box
|
||||
cached_name="$(sed 's/preloaded-/preloaded-miabldap-/' <<<"$box")"
|
||||
echo "Removing cached box $cached_name"
|
||||
if [ -e "../funcs.rb" ]; then
|
||||
pushd .. > /dev/null
|
||||
vagrant box remove $cached_name
|
||||
code=$?
|
||||
popd > /dev/null
|
||||
else
|
||||
vagrant box remove $cached_name
|
||||
code=$?
|
||||
fi
|
||||
echo "Remove cache box result: $code - ignoring"
|
||||
done
|
||||
@@ -1,230 +0,0 @@
|
||||
#!/bin/bash
|
||||
#####
|
||||
##### This file is part of Mail-in-a-Box-LDAP which is released under the
|
||||
##### terms of the GNU Affero General Public License as published by the
|
||||
##### Free Software Foundation, either version 3 of the License, or (at
|
||||
##### your option) any later version. See file LICENSE or go to
|
||||
##### https://github.com/downtownallday/mailinabox-ldap for full license
|
||||
##### details.
|
||||
#####
|
||||
|
||||
|
||||
# Run this on a VM to pre-install all the packages, then
|
||||
# take a snapshot - it will greatly speed up subsequent
|
||||
# test installs
|
||||
|
||||
#
|
||||
# What won't be installed:
|
||||
#
|
||||
# Nextcloud and Roundcube are downloaded with wget by the setup
|
||||
# scripts, so they are not included
|
||||
#
|
||||
# slapd - we want to test installation with setup/ldap.sh
|
||||
#
|
||||
|
||||
if [ ! -d "setup" ]; then
|
||||
echo "Run from the miab root directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source tests/lib/misc.sh
|
||||
source tests/lib/system.sh
|
||||
source tests/lib/color-output.sh
|
||||
|
||||
dry_run=true
|
||||
start=$(date +%s)
|
||||
|
||||
if [ "$1" == "--no-dry-run" ]; then
|
||||
dry_run=false
|
||||
fi
|
||||
|
||||
if $dry_run; then
|
||||
echo "WARNING: dry run is TRUE, no changes will be made"
|
||||
fi
|
||||
|
||||
|
||||
# prevent apt from running needrestart(1)
|
||||
export NEEDRESTART_SUSPEND=true
|
||||
|
||||
# prevent interaction during package install
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# what major version of ubuntu are we installing on?
|
||||
OS_MAJOR=$(. /etc/os-release; echo $VERSION_ID | awk -F. '{print $1}')
|
||||
|
||||
|
||||
remove_line_continuation() {
|
||||
local file="$1"
|
||||
awk '
|
||||
BEGIN { C=0 }
|
||||
C==1 && /[^\\]$/ { C=0; print $0; next }
|
||||
C==1 { printf("%s",substr($0,0,length($0)-1)); next }
|
||||
/\\$/ { C=1; printf("%s",substr($0,0,length($0)-1)); next }
|
||||
{ print $0 }' \
|
||||
"$file"
|
||||
}
|
||||
|
||||
install_packages() {
|
||||
local return_code=0
|
||||
while read line; do
|
||||
pkgs=""
|
||||
case "$line" in
|
||||
apt_install* )
|
||||
pkgs="$(cut -c12- <<<"$line")"
|
||||
;;
|
||||
"apt-get install"* )
|
||||
pkgs="$(cut -c16- <<<"$line")"
|
||||
;;
|
||||
"apt install"* )
|
||||
pkgs="$(cut -c12- <<<"$line")"
|
||||
;;
|
||||
esac
|
||||
|
||||
# don't install slapd
|
||||
pkgs="$(sed 's/slapd//g' <<< "$pkgs")"
|
||||
|
||||
# manually set PHP_VER if necessary
|
||||
if grep "PHP_VER" <<<"$pkgs" >/dev/null; then
|
||||
pkgs="$(sed "s/\"\?\${*PHP_VER}*\"\?/$PHP_VER/g" <<< "$pkgs")"
|
||||
fi
|
||||
|
||||
if [ ! -z "$pkgs" ]; then
|
||||
H2 "install: $pkgs"
|
||||
if ! $dry_run; then
|
||||
exec_no_output apt-get install -y $pkgs
|
||||
let return_code+=$?
|
||||
fi
|
||||
fi
|
||||
done
|
||||
return $return_code
|
||||
}
|
||||
|
||||
install_ppas() {
|
||||
H1 "Add apt repositories"
|
||||
grep 'hide_output add-apt-repository' setup/system.sh |
|
||||
while read line; do
|
||||
line=$(sed 's/^hide_output //' <<< "$line")
|
||||
H2 "$line"
|
||||
if ! $dry_run; then
|
||||
exec_no_output $line
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
add_swap() {
|
||||
H1 "Add a swap file to the system"
|
||||
if ! $dry_run; then
|
||||
dd if=/dev/zero of=/swapfile bs=1024 count=$[1024*1024] status=none
|
||||
chmod 600 /swapfile
|
||||
mkswap /swapfile
|
||||
swapon /swapfile
|
||||
echo "/swapfile none swap sw 0 0" >> /etc/fstab
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# install PPAs from sources
|
||||
install_ppas
|
||||
|
||||
# add swap file
|
||||
add_swap
|
||||
|
||||
# obtain PHP_VER variable from sources
|
||||
PHP_VER=$(source setup/functions.sh; echo $PHP_VER)
|
||||
|
||||
|
||||
if ! $dry_run; then
|
||||
H1 "Upgrade system"
|
||||
H2 "apt update"
|
||||
exec_no_output apt-get update -y || exit 1
|
||||
H2 "apt upgrade"
|
||||
exec_no_output apt-get upgrade -y --with-new-pkgs || exit 1
|
||||
H2 "apt autoremove"
|
||||
exec_no_output apt-get autoremove -y
|
||||
fi
|
||||
|
||||
# without using the same installation order as setup/start.sh, we end
|
||||
# up with the system's php getting installed in addition to the
|
||||
# non-system php that may also installed by setup (don't know why,
|
||||
# probably one of the packages has a dependency). create an ordered
|
||||
# list of files to process so we get a similar system setup.
|
||||
|
||||
setup_files=( $(ls setup/*.sh) )
|
||||
desired_order=(
|
||||
setup/functions.sh
|
||||
setup/preflight.sh
|
||||
setup/questions.sh
|
||||
setup/network-checks.sh
|
||||
setup/system.sh
|
||||
setup/ssl.sh
|
||||
setup/dns.sh
|
||||
setup/ldap.sh
|
||||
setup/mail-postfix.sh
|
||||
setup/mail-dovecot.sh
|
||||
setup/mail-users.sh
|
||||
setup/dkim.sh
|
||||
setup/spamassassin.sh
|
||||
setup/web.sh
|
||||
setup/webmail.sh
|
||||
setup/nextcloud.sh
|
||||
setup/zpush.sh
|
||||
setup/management.sh
|
||||
setup/management-capture.sh
|
||||
setup/munin.sh
|
||||
setup/firstuser.sh
|
||||
)
|
||||
ordered_files=()
|
||||
for file in "${desired_order[@]}" "${setup_files[@]}"; do
|
||||
if [ -e "$file" ] && ! array_contains "$file" "${ordered_files[@]}"; then
|
||||
ordered_files+=( "$file" )
|
||||
fi
|
||||
done
|
||||
|
||||
failed=0
|
||||
|
||||
for file in ${ordered_files[@]}; do
|
||||
H1 "$file"
|
||||
remove_line_continuation "$file" | install_packages
|
||||
[ $? -ne 0 ] && let failed+=1
|
||||
done
|
||||
|
||||
if ! $dry_run; then
|
||||
# bonus
|
||||
H1 "install extras"
|
||||
|
||||
H2 "openssh, emacs, ntpdate, net-tools, jq"
|
||||
exec_no_output apt-get install -y openssh-server emacs-nox ntpdate net-tools jq || let failed+=1
|
||||
|
||||
# these are added by system-setup scripts and needed for test runner
|
||||
H2 "python3-dnspython"
|
||||
exec_no_output apt-get install -y python3-dnspython || let failed+=1
|
||||
H2 "pyotp(pip)"
|
||||
exec_no_output python3 -m pip install pyotp --quiet || let failed+=1
|
||||
|
||||
# ...and for browser-based tests
|
||||
#H2 "x11" # needed for chromium w/head (not --headless)
|
||||
#exec_no_output apt-get install -y xorg openbox xvfb gtk2-engines-pixbuf dbus-x11 xfonts-base xfonts-100dpi xfonts-75dpi xfonts-cyrillic xfonts-scalable x11-apps imagemagick || let failed+=1
|
||||
H2 "chromium"
|
||||
#exec_no_output apt-get install -y chromium-browser || let failed+=1
|
||||
exec_no_output snap install chromium || let failed+=1
|
||||
H2 "selenium(pip)"
|
||||
exec_no_output python3 -m pip install selenium --quiet || let failed+=1
|
||||
|
||||
# remove apache, which is what setup will do
|
||||
H2 "remove apache2"
|
||||
exec_no_output apt-get -y purge apache2 apache2-\*
|
||||
|
||||
fi
|
||||
|
||||
end=$(date +%s)
|
||||
echo ""
|
||||
echo ""
|
||||
if [ $failed -gt 0 ]; then
|
||||
echo "$failed failures! ($(elapsed_pretty $start $end))"
|
||||
echo ""
|
||||
exit 1
|
||||
else
|
||||
echo "Successfully prepped in $(elapsed_pretty $start $end). Take a snapshot...."
|
||||
echo ""
|
||||
exit 0
|
||||
fi
|
||||
2
tests/vagrant/vanilla/.gitignore
vendored
2
tests/vagrant/vanilla/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
.vagrant
|
||||
*-console.log
|
||||
87
tests/vagrant/vanilla/Vagrantfile
vendored
87
tests/vagrant/vanilla/Vagrantfile
vendored
@@ -1,87 +0,0 @@
|
||||
#####
|
||||
##### This file is part of Mail-in-a-Box-LDAP which is released under the
|
||||
##### terms of the GNU Affero General Public License as published by the
|
||||
##### Free Software Foundation, either version 3 of the License, or (at
|
||||
##### your option) any later version. See file LICENSE or go to
|
||||
##### https://github.com/downtownallday/mailinabox-ldap for full license
|
||||
##### details.
|
||||
#####
|
||||
|
||||
load '../funcs.rb'
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
|
||||
config.vm.synced_folder "../../..", "/mailinabox", id: "mailinabox", automount: false
|
||||
config.vm.network "public_network", bridge: "#$default_network_interface"
|
||||
use_preloaded_box config, "ubuntu/jammy64", ".."
|
||||
|
||||
if ENV['tests']=='ciab'
|
||||
|
||||
# vanilla connected to ciab (ciab does not need to be up)
|
||||
|
||||
config.vm.define "vanilla" do |m1|
|
||||
m1.vm.provision :shell, :inline => <<-SH
|
||||
cat >/tmp/provision.sh <<EOF
|
||||
#!/bin/bash
|
||||
if [ \\$EUID -ne 0 ]; then
|
||||
echo "Must be root"
|
||||
exit 1
|
||||
fi
|
||||
cd /mailinabox
|
||||
export PRIMARY_HOSTNAME=vanilla.local
|
||||
export NC_PROTO=https
|
||||
export NC_HOST=vanilla-ciab.local
|
||||
export NC_PORT=443
|
||||
export NC_PREFIX=/
|
||||
export SKIP_SYSTEM_UPDATE=0
|
||||
tests/system-setup/vanilla.sh --qa-ca --enable-mod=remote-nextcloud
|
||||
if ! ufw status | grep remote_nextcloud >/dev/null; then
|
||||
# firewall rules aren't added when ciab is down
|
||||
echo "For testing, allow ldaps from anywhere"
|
||||
ufw allow ldaps
|
||||
fi
|
||||
echo "Add smart host alias - so \\$NC_HOST can send mail to/via this host"
|
||||
(
|
||||
source tests/lib/all.sh
|
||||
rest_urlencoded POST /admin/mail/aliases/add qa@abc.com Test_1234 "address=@\\$NC_HOST" "description=smart-host" "permitted_senders=qa@abc.com" 2>/dev/null
|
||||
echo "\\$REST_HTTP_CODE: \\$REST_OUTPUT"
|
||||
)
|
||||
EOF
|
||||
chmod +x /tmp/provision.sh
|
||||
/tmp/provision.sh
|
||||
SH
|
||||
end # vanilla connected to ciab
|
||||
|
||||
else
|
||||
|
||||
|
||||
# vanilla (default) install
|
||||
|
||||
config.vm.define "vanilla" do |m1|
|
||||
m1.vm.provision :shell, :inline => <<-SH
|
||||
cat >/tmp/provision.sh <<EOF
|
||||
#!/bin/bash
|
||||
if [ \\$EUID -ne 0 ]; then
|
||||
echo "Must be root"
|
||||
exit 1
|
||||
fi
|
||||
start=\\$(date +%s)
|
||||
cd /mailinabox
|
||||
export PRIMARY_HOSTNAME=vanilla.local
|
||||
#export FEATURE_MUNIN=false
|
||||
#export FEATURE_NEXTCLOUD=false
|
||||
export SKIP_SYSTEM_UPDATE=0
|
||||
tests/system-setup/vanilla.sh
|
||||
# --enable-mod=roundcube-master \
|
||||
# --enable-mod=roundcube-debug \
|
||||
# --enable-mod=rcmcarddav-composer
|
||||
end=\\$(date +%s)
|
||||
echo "Provisioning took \\$(source tests/lib/misc.sh; elapsed_pretty \\$start \\$end)"
|
||||
EOF
|
||||
chmod +x /tmp/provision.sh
|
||||
/tmp/provision.sh
|
||||
SH
|
||||
end # vanilla (default)
|
||||
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user