mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 18:53:35 +00:00
vendor files
This commit is contained in:
135
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/README.md
generated
vendored
Normal file
135
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/README.md
generated
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
# Kubernetes end to end
|
||||
|
||||
End-to-end (e2e) tests for Kubernetes provide a mechanism to test end-to-end
|
||||
behavior of the system, and is the last signal to ensure end user operations
|
||||
match developer specifications. Although unit and integration tests provide a
|
||||
good signal, in a distributed system like Kubernetes it is not uncommon that a
|
||||
minor change may pass all unit and integration tests, but cause unforeseen
|
||||
changes at the system level.
|
||||
|
||||
The primary objectives of the e2e tests are to ensure a consistent and reliable
|
||||
behavior of the kubernetes code base, and to catch hard-to-test bugs before
|
||||
users do, when unit and integration tests are insufficient.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
To deploy the end-to-end test suite, it is best to deploy the
|
||||
[kubernetes-core bundle](https://github.com/juju-solutions/bundle-kubernetes-core)
|
||||
and then relate the `kubernetes-e2e` charm.
|
||||
|
||||
```shell
|
||||
juju deploy kubernetes-core
|
||||
juju deploy cs:~containers/kubernetes-e2e
|
||||
juju add-relation kubernetes-e2e:kube-control kubernetes-master:kube-control
|
||||
juju add-relation kubernetes-e2e:kubernetes-master kubernetes-master:kube-api-endpoint
|
||||
juju add-relation kubernetes-e2e easyrsa
|
||||
```
|
||||
|
||||
|
||||
Once the relations have settled, and the `kubernetes-e2e` charm reports
|
||||
`Ready to test.` - you may kick off an end to end validation test.
|
||||
|
||||
### Running the e2e test
|
||||
|
||||
The e2e test is encapsulated as an action to ensure consistent runs of the
|
||||
end to end test. The defaults are sensible for most deployments.
|
||||
|
||||
```shell
|
||||
juju run-action kubernetes-e2e/0 test
|
||||
```
|
||||
|
||||
### Tuning the e2e test
|
||||
|
||||
The e2e test is configurable. By default it will focus on or skip the declared
|
||||
conformance tests in a cloud agnostic way. Default behaviors are configurable.
|
||||
This allows the operator to test only a subset of the conformance tests, or to
|
||||
test more behaviors not enabled by default. You can see all tunable options on
|
||||
the charm by inspecting the schema output of the actions:
|
||||
|
||||
```shell
|
||||
$ juju actions kubernetes-e2e --format=yaml --schema
|
||||
test:
|
||||
description: Run end-to-end validation test suite
|
||||
properties:
|
||||
focus:
|
||||
default: \[Conformance\]
|
||||
description: Regex focus for executing the test
|
||||
type: string
|
||||
skip:
|
||||
default: \[Flaky\]
|
||||
description: Regex of tests to skip
|
||||
type: string
|
||||
timeout:
|
||||
default: 30000
|
||||
description: Timeout in nanoseconds
|
||||
type: integer
|
||||
title: test
|
||||
type: object
|
||||
```
|
||||
|
||||
|
||||
As an example, you can run a more limited set of tests for rapid validation of
|
||||
a deployed cluster. The following example will skip the `Flaky`, `Slow`, and
|
||||
`Feature` labeled tests:
|
||||
|
||||
```shell
|
||||
juju run-action kubernetes-e2e/0 test skip='\[(Flaky|Slow|Feature:.*)\]'
|
||||
```
|
||||
|
||||
> Note: the escaping of the regex due to how bash handles brackets.
|
||||
|
||||
To see the different types of tests the Kubernetes end-to-end charm has access
|
||||
to, we encourage you to see the upstream documentation on the different types
|
||||
of tests, and to strongly understand what subsets of the tests you are running.
|
||||
|
||||
[Kinds of tests](https://github.com/kubernetes/community/blob/master/contributors/devel/e2e-tests.md#kinds-of-tests)
|
||||
|
||||
### More information on end-to-end testing
|
||||
|
||||
Along with the above descriptions, end-to-end testing is a much larger subject
|
||||
than this readme can encapsulate. There is far more information in the
|
||||
[end-to-end testing guide](https://github.com/kubernetes/community/blob/master/contributors/devel/e2e-tests.md).
|
||||
|
||||
### Evaluating end-to-end results
|
||||
|
||||
It is not enough to just simply run the test. Result output is stored in two
|
||||
places. The raw output of the e2e run is available in the `juju show-action-output`
|
||||
command, as well as a flat file on disk on the `kubernetes-e2e` unit that
|
||||
executed the test.
|
||||
|
||||
> Note: The results will only be available once the action has
|
||||
completed the test run. End-to-end testing can be quite time intensive. Often
|
||||
times taking **greater than 1 hour**, depending on configuration.
|
||||
|
||||
##### Flat file
|
||||
|
||||
```shell
|
||||
$ juju run-action kubernetes-e2e/0 test
|
||||
Action queued with id: 4ceed33a-d96d-465a-8f31-20d63442e51b
|
||||
|
||||
$ juju scp kubernetes-e2e/0:4ceed33a-d96d-465a-8f31-20d63442e51b.log .
|
||||
```
|
||||
|
||||
##### Action result output
|
||||
|
||||
```shell
|
||||
$ juju run-action kubernetes-e2e/0 test
|
||||
Action queued with id: 4ceed33a-d96d-465a-8f31-20d63442e51b
|
||||
|
||||
$ juju show-action-output 4ceed33a-d96d-465a-8f31-20d63442e51b
|
||||
```
|
||||
|
||||
## Known issues
|
||||
|
||||
The e2e test suite assumes egress network access. It will pull container
|
||||
images from `gcr.io`. You will need to have this registry unblocked in your
|
||||
firewall to successfully run e2e test results. Or you may use the exposed
|
||||
proxy settings [properly configured](https://github.com/juju-solutions/bundle-canonical-kubernetes#proxy-configuration)
|
||||
on the kubernetes-worker units.
|
||||
|
||||
## Help resources:
|
||||
|
||||
- [Bug Tracker](https://github.com/juju-solutions/bundle-canonical-kubernetes/issues)
|
||||
- [Github Repository](https://github.com/kubernetes/kubernetes/)
|
||||
- [Mailing List](mailto:juju@lists.ubuntu.com)
|
19
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/actions.yaml
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/actions.yaml
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
test:
|
||||
description: "Execute an end to end test."
|
||||
params:
|
||||
focus:
|
||||
default: "\\[Conformance\\]"
|
||||
description: Run tests matching the focus regex pattern.
|
||||
type: string
|
||||
parallelism:
|
||||
default: 25
|
||||
description: The number of test nodes to run in parallel.
|
||||
type: integer
|
||||
skip:
|
||||
default: "\\[Flaky\\]|\\[Serial\\]"
|
||||
description: Skip tests matching the skip regex pattern.
|
||||
type: string
|
||||
timeout:
|
||||
default: 30000
|
||||
description: Timeout in nanoseconds
|
||||
type: integer
|
53
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/actions/test
generated
vendored
Executable file
53
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/actions/test
generated
vendored
Executable file
@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
export PATH="$PATH:/snap/bin"
|
||||
|
||||
# Grab the action parameter values
|
||||
FOCUS=$(action-get focus)
|
||||
SKIP=$(action-get skip)
|
||||
PARALLELISM=$(action-get parallelism)
|
||||
|
||||
if [ ! -f /home/ubuntu/.kube/config ]
|
||||
then
|
||||
action-fail "Missing Kubernetes configuration."
|
||||
action-set suggestion="Relate to the certificate authority, and kubernetes-master"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# get the host from the config file
|
||||
SERVER=$(cat /home/ubuntu/.kube/config | grep server | sed 's/ server: //')
|
||||
|
||||
ACTION_HOME=/home/ubuntu
|
||||
ACTION_LOG=$ACTION_HOME/${JUJU_ACTION_UUID}.log
|
||||
ACTION_LOG_TGZ=$ACTION_LOG.tar.gz
|
||||
ACTION_JUNIT=$ACTION_HOME/${JUJU_ACTION_UUID}-junit
|
||||
ACTION_JUNIT_TGZ=$ACTION_JUNIT.tar.gz
|
||||
|
||||
# This initializes an e2e build log with the START TIMESTAMP.
|
||||
echo "JUJU_E2E_START=$(date -u +%s)" | tee $ACTION_LOG
|
||||
echo "JUJU_E2E_VERSION=$(kubectl version | grep Server | cut -d " " -f 5 | cut -d ":" -f 2 | sed s/\"// | sed s/\",//)" | tee -a $ACTION_LOG
|
||||
GINKGO_ARGS="-nodes=$PARALLELISM" kubernetes-test.e2e \
|
||||
-kubeconfig /home/ubuntu/.kube/config \
|
||||
-host $SERVER \
|
||||
-ginkgo.focus $FOCUS \
|
||||
-ginkgo.skip "$SKIP" \
|
||||
-report-dir $ACTION_JUNIT 2>&1 | tee -a $ACTION_LOG
|
||||
|
||||
# This appends the END TIMESTAMP to the e2e build log
|
||||
echo "JUJU_E2E_END=$(date -u +%s)" | tee -a $ACTION_LOG
|
||||
|
||||
# set cwd to /home/ubuntu and tar the artifacts using a minimal directory
|
||||
# path. Extracing "home/ubuntu/1412341234/foobar.log is cumbersome in ci
|
||||
cd $ACTION_HOME/${JUJU_ACTION_UUID}-junit
|
||||
tar -czf $ACTION_JUNIT_TGZ *
|
||||
cd ..
|
||||
tar -czf $ACTION_LOG_TGZ ${JUJU_ACTION_UUID}.log
|
||||
|
||||
action-set log="$ACTION_LOG_TGZ"
|
||||
action-set junit="$ACTION_JUNIT_TGZ"
|
||||
|
||||
if tail ${JUJU_ACTION_UUID}.log | grep -q "Test Suite Failed"; then
|
||||
action-fail "Failure detected in the logs"
|
||||
fi
|
6
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/config.yaml
generated
vendored
Normal file
6
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/config.yaml
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
options:
|
||||
channel:
|
||||
type: string
|
||||
default: "1.8/stable"
|
||||
description: |
|
||||
Snap channel to install Kubernetes snaps from
|
362
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/icon.svg
generated
vendored
Normal file
362
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/icon.svg
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 26 KiB |
12
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/layer.yaml
generated
vendored
Normal file
12
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/layer.yaml
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
repo: https://github.com/juju-solutions/layer-kubernetes-e2e
|
||||
includes:
|
||||
- layer:basic
|
||||
- layer:tls-client
|
||||
- layer:snap
|
||||
- interface:http
|
||||
- interface:kube-control
|
||||
options:
|
||||
tls-client:
|
||||
ca_certificate_path: '/srv/kubernetes/ca.crt'
|
||||
client_certificate_path: '/srv/kubernetes/client.crt'
|
||||
client_key_path: '/srv/kubernetes/client.key'
|
31
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/metadata.yaml
generated
vendored
Normal file
31
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/metadata.yaml
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
name: kubernetes-e2e
|
||||
summary: Run end-2-end validation of a clusters conformance
|
||||
maintainers:
|
||||
- Tim Van Steenburgh <tim.van.steenburgh@canonical.com>
|
||||
- George Kraft <george.kraft@canonical.com>
|
||||
- Rye Terrell <rye.terrell@canonical.com>
|
||||
- Konstantinos Tsakalozos <kos.tsakalozos@canonical.com>
|
||||
- Charles Butler <Chuck@dasroot.net>
|
||||
- Matthew Bruzek <mbruzek@ubuntu.com>
|
||||
description: |
|
||||
Deploy the Kubernetes e2e framework and validate the conformance of a
|
||||
deployed kubernetes cluster
|
||||
tags:
|
||||
- validation
|
||||
- conformance
|
||||
series:
|
||||
- xenial
|
||||
requires:
|
||||
kubernetes-master:
|
||||
interface: http
|
||||
kube-control:
|
||||
interface: kube-control
|
||||
resources:
|
||||
kubectl:
|
||||
type: file
|
||||
filename: kubectl.snap
|
||||
description: kubectl snap
|
||||
kubernetes-test:
|
||||
type: file
|
||||
filename: kubernetes-test.snap
|
||||
description: kubernetes-test snap
|
220
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/reactive/kubernetes_e2e.py
generated
vendored
Normal file
220
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/reactive/kubernetes_e2e.py
generated
vendored
Normal file
@ -0,0 +1,220 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2015 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from charms import layer
|
||||
from charms.layer import snap
|
||||
|
||||
from charms.reactive import hook
|
||||
from charms.reactive import is_state
|
||||
from charms.reactive import set_state
|
||||
from charms.reactive import when
|
||||
from charms.reactive import when_not
|
||||
from charms.reactive.helpers import data_changed
|
||||
|
||||
from charmhelpers.core import hookenv, unitdata
|
||||
|
||||
from shlex import split
|
||||
|
||||
from subprocess import check_call
|
||||
from subprocess import check_output
|
||||
|
||||
db = unitdata.kv()
|
||||
USER = 'system:e2e'
|
||||
|
||||
|
||||
@hook('upgrade-charm')
|
||||
def reset_delivery_states():
|
||||
''' Remove the state set when resources are unpacked. '''
|
||||
install_snaps()
|
||||
|
||||
|
||||
@when('kubernetes-e2e.installed')
|
||||
def report_status():
|
||||
''' Report the status of the charm. '''
|
||||
messaging()
|
||||
|
||||
|
||||
def messaging():
|
||||
''' Probe our relations to determine the propper messaging to the
|
||||
end user '''
|
||||
|
||||
missing_services = []
|
||||
if not is_state('kubernetes-master.available'):
|
||||
missing_services.append('kubernetes-master:http')
|
||||
if not is_state('certificates.available'):
|
||||
missing_services.append('certificates')
|
||||
if not is_state('kubeconfig.ready'):
|
||||
missing_services.append('kubernetes-master:kube-control')
|
||||
|
||||
if missing_services:
|
||||
if len(missing_services) > 1:
|
||||
subject = 'relations'
|
||||
else:
|
||||
subject = 'relation'
|
||||
|
||||
services = ','.join(missing_services)
|
||||
message = 'Missing {0}: {1}'.format(subject, services)
|
||||
hookenv.status_set('blocked', message)
|
||||
return
|
||||
|
||||
hookenv.status_set('active', 'Ready to test.')
|
||||
|
||||
|
||||
@when('config.changed.channel')
|
||||
def channel_changed():
|
||||
install_snaps()
|
||||
|
||||
|
||||
def install_snaps():
|
||||
''' Deliver the e2e and kubectl components from the binary resource stream
|
||||
packages declared in the charm '''
|
||||
channel = hookenv.config('channel')
|
||||
hookenv.status_set('maintenance', 'Installing kubectl snap')
|
||||
snap.install('kubectl', channel=channel, classic=True)
|
||||
hookenv.status_set('maintenance', 'Installing kubernetes-test snap')
|
||||
snap.install('kubernetes-test', channel=channel, classic=True)
|
||||
set_state('kubernetes-e2e.installed')
|
||||
|
||||
|
||||
@when('tls_client.ca.saved', 'tls_client.client.certificate.saved',
|
||||
'tls_client.client.key.saved', 'kubernetes-master.available',
|
||||
'kubernetes-e2e.installed', 'e2e.auth.bootstrapped')
|
||||
@when_not('kubeconfig.ready')
|
||||
def prepare_kubeconfig_certificates(master):
|
||||
''' Prepare the data to feed to create the kubeconfig file. '''
|
||||
|
||||
layer_options = layer.options('tls-client')
|
||||
# Get all the paths to the tls information required for kubeconfig.
|
||||
ca = layer_options.get('ca_certificate_path')
|
||||
creds = db.get('credentials')
|
||||
data_changed('kube-control.creds', creds)
|
||||
|
||||
servers = get_kube_api_servers(master)
|
||||
|
||||
# pedantry
|
||||
kubeconfig_path = '/home/ubuntu/.kube/config'
|
||||
|
||||
# Create kubernetes configuration in the default location for ubuntu.
|
||||
create_kubeconfig('/root/.kube/config', servers[0], ca,
|
||||
token=creds['client_token'], user='root')
|
||||
create_kubeconfig(kubeconfig_path, servers[0], ca,
|
||||
token=creds['client_token'], user='ubuntu')
|
||||
# Set permissions on the ubuntu users kubeconfig to ensure a consistent UX
|
||||
cmd = ['chown', 'ubuntu:ubuntu', kubeconfig_path]
|
||||
check_call(cmd)
|
||||
messaging()
|
||||
set_state('kubeconfig.ready')
|
||||
|
||||
|
||||
@when('kube-control.connected')
|
||||
def request_credentials(kube_control):
|
||||
""" Request authorization creds."""
|
||||
|
||||
# Ask for a user, although we will be using the 'client_token'
|
||||
kube_control.set_auth_request(USER)
|
||||
|
||||
|
||||
@when('kube-control.auth.available')
|
||||
def catch_change_in_creds(kube_control):
|
||||
"""Request a service restart in case credential updates were detected."""
|
||||
creds = kube_control.get_auth_credentials(USER)
|
||||
if creds \
|
||||
and data_changed('kube-control.creds', creds) \
|
||||
and creds['user'] == USER:
|
||||
# We need to cache the credentials here because if the
|
||||
# master changes (master leader dies and replaced by a new one)
|
||||
# the new master will have no recollection of our certs.
|
||||
db.set('credentials', creds)
|
||||
set_state('e2e.auth.bootstrapped')
|
||||
|
||||
|
||||
@when('kubernetes-e2e.installed', 'kubeconfig.ready')
|
||||
def set_app_version():
|
||||
''' Declare the application version to juju '''
|
||||
cmd = ['kubectl', 'version', '--client']
|
||||
from subprocess import CalledProcessError
|
||||
try:
|
||||
version = check_output(cmd).decode('utf-8')
|
||||
except CalledProcessError:
|
||||
message = "Missing kubeconfig causes errors. Skipping version set."
|
||||
hookenv.log(message)
|
||||
return
|
||||
git_version = version.split('GitVersion:"v')[-1]
|
||||
version_from = git_version.split('",')[0]
|
||||
hookenv.application_version_set(version_from.rstrip())
|
||||
|
||||
|
||||
def create_kubeconfig(kubeconfig, server, ca, key=None, certificate=None,
|
||||
user='ubuntu', context='juju-context',
|
||||
cluster='juju-cluster', password=None, token=None):
|
||||
'''Create a configuration for Kubernetes based on path using the supplied
|
||||
arguments for values of the Kubernetes server, CA, key, certificate, user
|
||||
context and cluster.'''
|
||||
if not key and not certificate and not password and not token:
|
||||
raise ValueError('Missing authentication mechanism.')
|
||||
|
||||
# token and password are mutually exclusive. Error early if both are
|
||||
# present. The developer has requested an impossible situation.
|
||||
# see: kubectl config set-credentials --help
|
||||
if token and password:
|
||||
raise ValueError('Token and Password are mutually exclusive.')
|
||||
# Create the config file with the address of the master server.
|
||||
cmd = 'kubectl config --kubeconfig={0} set-cluster {1} ' \
|
||||
'--server={2} --certificate-authority={3} --embed-certs=true'
|
||||
check_call(split(cmd.format(kubeconfig, cluster, server, ca)))
|
||||
# Delete old users
|
||||
cmd = 'kubectl config --kubeconfig={0} unset users'
|
||||
check_call(split(cmd.format(kubeconfig)))
|
||||
# Create the credentials using the client flags.
|
||||
cmd = 'kubectl config --kubeconfig={0} ' \
|
||||
'set-credentials {1} '.format(kubeconfig, user)
|
||||
|
||||
if key and certificate:
|
||||
cmd = '{0} --client-key={1} --client-certificate={2} '\
|
||||
'--embed-certs=true'.format(cmd, key, certificate)
|
||||
if password:
|
||||
cmd = "{0} --username={1} --password={2}".format(cmd, user, password)
|
||||
# This is mutually exclusive from password. They will not work together.
|
||||
if token:
|
||||
cmd = "{0} --token={1}".format(cmd, token)
|
||||
check_call(split(cmd))
|
||||
# Create a default context with the cluster.
|
||||
cmd = 'kubectl config --kubeconfig={0} set-context {1} ' \
|
||||
'--cluster={2} --user={3}'
|
||||
check_call(split(cmd.format(kubeconfig, context, cluster, user)))
|
||||
# Make the config use this new context.
|
||||
cmd = 'kubectl config --kubeconfig={0} use-context {1}'
|
||||
check_call(split(cmd.format(kubeconfig, context)))
|
||||
|
||||
|
||||
def get_kube_api_servers(master):
|
||||
'''Return the kubernetes api server address and port for this
|
||||
relationship.'''
|
||||
hosts = []
|
||||
# Iterate over every service from the relation object.
|
||||
for service in master.services():
|
||||
for unit in service['hosts']:
|
||||
hosts.append('https://{0}:{1}'.format(unit['hostname'],
|
||||
unit['port']))
|
||||
return hosts
|
||||
|
||||
|
||||
def determine_arch():
|
||||
''' dpkg wrapper to surface the architecture we are tied to'''
|
||||
cmd = ['dpkg', '--print-architecture']
|
||||
output = check_output(cmd).decode('utf-8')
|
||||
|
||||
return output.rstrip()
|
12
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/tox.ini
generated
vendored
Normal file
12
vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-e2e/tox.ini
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
[tox]
|
||||
skipsdist=True
|
||||
envlist = py34, py35
|
||||
skip_missing_interpreters = True
|
||||
|
||||
[testenv]
|
||||
commands = py.test -v
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
|
||||
[flake8]
|
||||
exclude=docs
|
Reference in New Issue
Block a user