Merge pull request #570 from dmcgowan/docker-integration
contrib: Docker integration tests
This commit is contained in:
commit
d0fda14e85
46
contrib/docker-integration/Dockerfile
Normal file
46
contrib/docker-integration/Dockerfile
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
FROM debian:jessie
|
||||||
|
|
||||||
|
MAINTAINER Docker Distribution Team <distribution@docker.com>
|
||||||
|
|
||||||
|
# compile and runtime deps
|
||||||
|
# https://github.com/docker/docker/blob/master/project/PACKAGERS.md#runtime-dependencies
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
# For DIND
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
iptables \
|
||||||
|
procps \
|
||||||
|
e2fsprogs \
|
||||||
|
xz-utils \
|
||||||
|
# For build
|
||||||
|
build-essential \
|
||||||
|
file \
|
||||||
|
git \
|
||||||
|
net-tools \
|
||||||
|
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install Docker
|
||||||
|
ENV VERSION 1.7.0-rc1
|
||||||
|
RUN curl -L -o /usr/local/bin/docker https://test.docker.com/builds/Linux/x86_64/docker-${VERSION} \
|
||||||
|
&& chmod +x /usr/local/bin/docker
|
||||||
|
|
||||||
|
# Install DIND
|
||||||
|
RUN curl -L -o /dind https://raw.githubusercontent.com/docker/docker/master/hack/dind \
|
||||||
|
&& chmod +x /dind
|
||||||
|
|
||||||
|
# Install bats
|
||||||
|
RUN cd /usr/local/src/ \
|
||||||
|
&& git clone https://github.com/sstephenson/bats.git \
|
||||||
|
&& cd bats \
|
||||||
|
&& ./install.sh /usr/local
|
||||||
|
|
||||||
|
# Install docker-compose
|
||||||
|
RUN curl -L https://github.com/docker/compose/releases/download/1.2.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose \
|
||||||
|
&& chmod +x /usr/local/bin/docker-compose
|
||||||
|
|
||||||
|
RUN mkdir -p /go/src/github.com/docker/distribution
|
||||||
|
WORKDIR /go/src/github.com/docker/distribution/contrib/docker-integration
|
||||||
|
|
||||||
|
VOLUME /var/lib/docker
|
||||||
|
|
||||||
|
ENTRYPOINT ["/dind"]
|
@ -1,24 +0,0 @@
|
|||||||
.PHONY: build test
|
|
||||||
|
|
||||||
build:
|
|
||||||
docker-compose build
|
|
||||||
|
|
||||||
start: build
|
|
||||||
docker-compose up -d
|
|
||||||
|
|
||||||
stop:
|
|
||||||
docker-compose stop
|
|
||||||
|
|
||||||
clean:
|
|
||||||
docker-compose kill
|
|
||||||
docker-compose rm -f
|
|
||||||
|
|
||||||
install:
|
|
||||||
sh ./install_certs.sh localhost
|
|
||||||
sh ./install_certs.sh localregistry
|
|
||||||
|
|
||||||
test:
|
|
||||||
@echo "!!!!Ensure /etc/hosts entry is updated for localregistry and make install has been run"
|
|
||||||
sh ./test_docker.sh localregistry
|
|
||||||
|
|
||||||
all: build
|
|
@ -1,32 +1,88 @@
|
|||||||
# Docker Registry Multi-Configuration Testing
|
# Docker Registry Integration Testing
|
||||||
|
|
||||||
This compose configuration is intended to setup a testing environment for Docker
|
These integration tests cover interactions between the Docker daemon and the
|
||||||
|
registry server. All tests are run using the docker cli.
|
||||||
|
|
||||||
|
The compose configuration is intended to setup a testing environment for Docker
|
||||||
using multiple registry configurations. These configurations include different
|
using multiple registry configurations. These configurations include different
|
||||||
combinations of a v1 and v2 registry as well as TLS configurations.
|
combinations of a v1 and v2 registry as well as TLS configurations.
|
||||||
|
|
||||||
### Limitations
|
## Running inside of Docker
|
||||||
|
### Get integration container
|
||||||
|
The container image to run the integation tests will need to be pulled or built
|
||||||
|
locally.
|
||||||
|
|
||||||
Currently this setup is configured to use localhost as the hostname which
|
*Building locally*
|
||||||
limits the ease of testing within Docker since localhost is always treated
|
```
|
||||||
as an insecure registry. To treat localhost as secure the Docker code must
|
$ docker build -t distribution/docker-integration .
|
||||||
be modified. Without localhost as secure, the test cases will not distinguish
|
```
|
||||||
between a TLS configuration with a CA and self-signed.
|
|
||||||
|
### Run script
|
||||||
|
|
||||||
|
Invoke the tests within Docker through the `run.sh` script.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./run.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Run with aufs driver and tmp volume
|
||||||
|
**NOTE: Using a volume will prevent multiple runs from needing to
|
||||||
|
re-pull images**
|
||||||
|
```
|
||||||
|
$ DOCKER_GRAPHDRIVER=aufs DOCKER_VOLUME=/tmp/volume ./run.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example developer flow
|
||||||
|
|
||||||
|
These tests are useful for developing both as a registry and docker
|
||||||
|
core developer. The following setup may be used to do integration
|
||||||
|
testing between development versions
|
||||||
|
|
||||||
|
Insert into your `.zshrc` or `.bashrc`
|
||||||
|
|
||||||
|
```
|
||||||
|
# /usr/lib/docker for Docker-in-Docker
|
||||||
|
# Set this directory to make each invocation run much faster, without
|
||||||
|
# the need to repull images.
|
||||||
|
export DOCKER_VOLUME=$HOME/.docker-test-volume
|
||||||
|
|
||||||
|
# Use overlay for all Docker testing, try aufs if overlay not supported
|
||||||
|
export DOCKER_GRAPHDRIVER=overlay
|
||||||
|
|
||||||
|
# Name this according to personal preference
|
||||||
|
function rdtest() {
|
||||||
|
if [ "$1" != "" ]; then
|
||||||
|
DOCKER_BINARY=$GOPATH/src/github.com/docker/docker/bundles/$1/binary/docker
|
||||||
|
if [ ! -f $DOCKER_BINARY ]; then
|
||||||
|
current_version=`cat $GOPATH/src/github.com/docker/docker/VERSION`
|
||||||
|
echo "$DOCKER_BINARY does not exist"
|
||||||
|
echo "Current checked out docker version: $current_version"
|
||||||
|
echo "Checkout desired version and run 'make binary' from $GOPATH/src/github.com/docker/docker"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
$GOPATH/src/github.com/docker/distribution/contrib/docker-integration/run.sh
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Run with Docker release version
|
||||||
|
```
|
||||||
|
$ rdtest
|
||||||
|
```
|
||||||
|
|
||||||
|
Run using local development version of docker
|
||||||
|
```
|
||||||
|
$ cd $GOPATH/src/github.com/docker/docker
|
||||||
|
$ make binary
|
||||||
|
$ rdtest `cat VERSION`
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running manually outside of Docker
|
||||||
|
|
||||||
### Install Docker Compose
|
### Install Docker Compose
|
||||||
|
|
||||||
1. Open a new terminal on the host with your `distribution` source.
|
[Docker Compose Installation Guide](http://docs.docker.com/compose/install/)
|
||||||
|
|
||||||
2. Get the `docker-compose` binary.
|
|
||||||
|
|
||||||
$ sudo wget https://github.com/docker/compose/releases/download/1.1.0/docker-compose-`uname -s`-`uname -m` -O /usr/local/bin/docker-compose
|
|
||||||
|
|
||||||
This command installs the binary in the `/usr/local/bin` directory.
|
|
||||||
|
|
||||||
3. Add executable permissions to the binary.
|
|
||||||
|
|
||||||
$ sudo chmod +x /usr/local/bin/docker-compose
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Start compose setup
|
### Start compose setup
|
||||||
```
|
```
|
||||||
@ -34,7 +90,8 @@ docker-compose up
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Install Certificates
|
### Install Certificates
|
||||||
The certificates must be installed in /etc/docker/cert.d in order to use TLS client auth and use the CA certificate.
|
The certificates must be installed in /etc/docker/cert.d in order to use TLS
|
||||||
|
client auth and use the CA certificate.
|
||||||
```
|
```
|
||||||
sudo sh ./install_certs.sh
|
sudo sh ./install_certs.sh
|
||||||
```
|
```
|
||||||
@ -52,6 +109,16 @@ docker push localhost:5441/hello-world
|
|||||||
# Perform login using user `testuser` and password `passpassword`
|
# Perform login using user `testuser` and password `passpassword`
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Set /etc/hosts entry
|
||||||
|
Find the non-localhost ip address of local machine
|
||||||
|
|
||||||
|
### Run bats
|
||||||
|
Run the bats tests after updating /etc/hosts, installing the certificates, and
|
||||||
|
running the `docker-compose` script.
|
||||||
|
```
|
||||||
|
bats -p .
|
||||||
|
```
|
||||||
|
|
||||||
## Configurations
|
## Configurations
|
||||||
|
|
||||||
Port | V2 | V1 | TLS | Authentication
|
Port | V2 | V1 | TLS | Authentication
|
||||||
@ -59,6 +126,7 @@ Port | V2 | V1 | TLS | Authentication
|
|||||||
5000 | yes | yes | no | none
|
5000 | yes | yes | no | none
|
||||||
5001 | no | yes | no | none
|
5001 | no | yes | no | none
|
||||||
5002 | yes | no | no | none
|
5002 | yes | no | no | none
|
||||||
|
5011 | no | yes | yes | none
|
||||||
5440 | yes | yes | yes | none
|
5440 | yes | yes | yes | none
|
||||||
5441 | yes | yes | yes | basic (testuser/passpassword)
|
5441 | yes | yes | yes | basic (testuser/passpassword)
|
||||||
5442 | yes | yes | yes | TLS client
|
5442 | yes | yes | yes | TLS client
|
||||||
|
36
contrib/docker-integration/run.sh
Executable file
36
contrib/docker-integration/run.sh
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd "$(dirname "$(readlink -f "$BASH_SOURCE")")"
|
||||||
|
|
||||||
|
# Root directory of Distribution
|
||||||
|
DISTRIBUTION_ROOT=$(cd ../..; pwd -P)
|
||||||
|
|
||||||
|
volumeMount=""
|
||||||
|
if [ "$DOCKER_VOLUME" != "" ]; then
|
||||||
|
volumeMount="-v ${DOCKER_VOLUME}:/var/lib/docker"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dockerMount=""
|
||||||
|
if [ "$DOCKER_BINARY" != "" ]; then
|
||||||
|
dockerMount="-v ${DOCKER_BINARY}:/usr/local/bin/docker"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Image containing the integration tests environment.
|
||||||
|
INTEGRATION_IMAGE=${INTEGRATION_IMAGE:-distribution/docker-integration}
|
||||||
|
|
||||||
|
# Make sure we upgrade the integration environment.
|
||||||
|
docker pull $INTEGRATION_IMAGE
|
||||||
|
|
||||||
|
# Start the integration tests in a Docker container.
|
||||||
|
ID=$(docker run -d -t --privileged $volumeMount $dockerMount \
|
||||||
|
-v ${DISTRIBUTION_ROOT}:/go/src/github.com/docker/distribution \
|
||||||
|
-e "STORAGE_DRIVER=$DOCKER_GRAPHDRIVER" \
|
||||||
|
-e "EXEC_DRIVER=$EXEC_DRIVER" \
|
||||||
|
${INTEGRATION_IMAGE} \
|
||||||
|
./test_runner.sh "$@")
|
||||||
|
|
||||||
|
# Clean it up when we exit.
|
||||||
|
trap "docker rm -f -v $ID > /dev/null" EXIT
|
||||||
|
|
||||||
|
docker logs -f $ID
|
@ -1,98 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
hostname=$1
|
|
||||||
if [ "$hostname" = "" ]; then
|
|
||||||
hostname="localhost"
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker pull hello-world
|
|
||||||
|
|
||||||
# TLS Configuration chart
|
|
||||||
# Username/Password: testuser/passpassword
|
|
||||||
# | ca | client | basic | notes
|
|
||||||
# 5440 | yes | no | no | Tests CA certificate
|
|
||||||
# 5441 | yes | no | yes | Tests basic auth over TLS
|
|
||||||
# 5442 | yes | yes | no | Tests client auth with client CA
|
|
||||||
# 5443 | yes | yes | no | Tests client auth without client CA
|
|
||||||
# 5444 | yes | yes | yes | Tests using basic auth + tls auth
|
|
||||||
# 5445 | no | no | no | Tests insecure using TLS
|
|
||||||
# 5446 | no | no | yes | Tests sending credentials to server with insecure TLS
|
|
||||||
# 5447 | no | yes | no | Tests client auth to insecure
|
|
||||||
# 5448 | yes | no | no | Bad SSL version
|
|
||||||
docker tag -f hello-world $hostname:5440/hello-world
|
|
||||||
docker push $hostname:5440/hello-world
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "Fail to push"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker login -u testuser -p passpassword -e distribution@docker.com $hostname:5441
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "Failed to login"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
docker tag -f hello-world $hostname:5441/hello-world
|
|
||||||
docker push $hostname:5441/hello-world
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "Fail to push"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker tag -f hello-world $hostname:5442/hello-world
|
|
||||||
docker push $hostname:5442/hello-world
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "Fail to push"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker tag -f hello-world $hostname:5443/hello-world
|
|
||||||
docker push $hostname:5443/hello-world
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "Expected failure"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker login -u testuser -p passpassword -e distribution@docker.com $hostname:5444
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "Failed to login"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
docker tag -f hello-world $hostname:5444/hello-world
|
|
||||||
docker push $hostname:5444/hello-world
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "Fail to push"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker tag -f hello-world $hostname:5445/hello-world
|
|
||||||
docker push $hostname:5445/hello-world
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "Expected failure with insecure registry"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker login -u testuser -p passpassword -e distribution@docker.com $hostname:5446
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "Failed to login"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
docker tag -f hello-world $hostname:5446/hello-world
|
|
||||||
docker push $hostname:5446/hello-world
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "Expected failure with insecure registry"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker tag -f hello-world $hostname:5447/hello-world
|
|
||||||
docker push $hostname:5447/hello-world
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "Expected failure with insecure registry"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker tag -f hello-world $hostname:5448/hello-world
|
|
||||||
docker push $hostname:5448/hello-world
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "Expected failure contacting with sslv3"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
50
contrib/docker-integration/test_runner.sh
Executable file
50
contrib/docker-integration/test_runner.sh
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd "$(dirname "$(readlink -f "$BASH_SOURCE")")"
|
||||||
|
|
||||||
|
# Load the helpers.
|
||||||
|
#. helpers.bash
|
||||||
|
|
||||||
|
TESTS=${@:-.}
|
||||||
|
|
||||||
|
# Drivers to use for Docker engines the tests are going to create.
|
||||||
|
STORAGE_DRIVER=${STORAGE_DRIVER:-overlay}
|
||||||
|
EXEC_DRIVER=${EXEC_DRIVER:-native}
|
||||||
|
|
||||||
|
|
||||||
|
function execute() {
|
||||||
|
>&2 echo "++ $@"
|
||||||
|
eval "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set IP address in /etc/hosts for localregistry
|
||||||
|
IP=$(ifconfig eth0|grep "inet addr:"| cut -d: -f2 | awk '{ print $1}')
|
||||||
|
execute echo "$IP localregistry" >> /etc/hosts
|
||||||
|
|
||||||
|
# Setup certificates
|
||||||
|
execute sh install_certs.sh localregistry
|
||||||
|
|
||||||
|
# Start the docker engine.
|
||||||
|
execute docker --daemon --log-level=panic \
|
||||||
|
--storage-driver="$STORAGE_DRIVER" --exec-driver="$EXEC_DRIVER" &
|
||||||
|
DOCKER_PID=$!
|
||||||
|
|
||||||
|
# Wait for it to become reachable.
|
||||||
|
tries=10
|
||||||
|
until docker version &> /dev/null; do
|
||||||
|
(( tries-- ))
|
||||||
|
if [ $tries -le 0 ]; then
|
||||||
|
echo >&2 "error: daemon failed to start"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
execute time docker-compose build
|
||||||
|
|
||||||
|
execute docker-compose up -d
|
||||||
|
|
||||||
|
# Run the tests.
|
||||||
|
execute time bats -p $TESTS
|
||||||
|
|
96
contrib/docker-integration/tls.bats
Normal file
96
contrib/docker-integration/tls.bats
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# Registry host name, should be set to non-localhost address and match
|
||||||
|
# DNS name in nginx/ssl certificates and what is installed in /etc/docker/cert.d
|
||||||
|
hostname="localregistry"
|
||||||
|
|
||||||
|
image="hello-world:latest"
|
||||||
|
|
||||||
|
# Login information, should match values in nginx/test.passwd
|
||||||
|
user="testuser"
|
||||||
|
password="passpassword"
|
||||||
|
email="distribution@docker.com"
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
docker pull $image
|
||||||
|
}
|
||||||
|
|
||||||
|
# has_digest enforces the last output line is "Digest: sha256:..."
|
||||||
|
# the input is the name of the array containing the output lines
|
||||||
|
function has_digest() {
|
||||||
|
name=$1[@]
|
||||||
|
lines=("${!name}")
|
||||||
|
length=${#lines[@]}
|
||||||
|
digest_idx=$((length-1))
|
||||||
|
value=${lines[$digest_idx]}
|
||||||
|
result=$(echo "$value"|cut -d':' -f1,2)
|
||||||
|
[ "$result" = "Digest: sha256" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
function login() {
|
||||||
|
run docker login -u $user -p $password -e $email $1
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
# First line is WARNING about credential save
|
||||||
|
[ "${lines[1]}" = "Login Succeeded" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Test valid certificates" {
|
||||||
|
docker tag -f $image $hostname:5440/$image
|
||||||
|
run docker push $hostname:5440/$image
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
has_digest lines
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Test basic auth" {
|
||||||
|
login $hostname:5441
|
||||||
|
docker tag -f $image $hostname:5441/$image
|
||||||
|
run docker push $hostname:5441/$image
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
has_digest lines
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Test TLS client auth" {
|
||||||
|
docker tag -f $image $hostname:5442/$image
|
||||||
|
run docker push $hostname:5442/$image
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
has_digest lines
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Test TLS client with invalid certificate authority fails" {
|
||||||
|
docker tag -f $image $hostname:5443/$image
|
||||||
|
run docker push $hostname:5443/$image
|
||||||
|
[ "$status" -ne 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Test basic auth with TLS client auth" {
|
||||||
|
login $hostname:5444
|
||||||
|
docker tag -f $image $hostname:5444/$image
|
||||||
|
run docker push $hostname:5444/$image
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
has_digest lines
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Test unknown certificate authority fails" {
|
||||||
|
docker tag -f $image $hostname:5445/$image
|
||||||
|
run docker push $hostname:5445/$image
|
||||||
|
[ "$status" -ne 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Test basic auth with unknown certificate authority fails" {
|
||||||
|
run login $hostname:5446
|
||||||
|
[ "$status" -ne 0 ]
|
||||||
|
docker tag -f $image $hostname:5446/$image
|
||||||
|
run docker push $hostname:5446/$image
|
||||||
|
[ "$status" -ne 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Test TLS client auth to server with unknown certificate authority fails" {
|
||||||
|
docker tag -f $image $hostname:5447/$image
|
||||||
|
run docker push $hostname:5447/$image
|
||||||
|
[ "$status" -ne 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Test failure to connect to server fails to fallback to SSLv3" {
|
||||||
|
docker tag -f $image $hostname:5448/$image
|
||||||
|
run docker push $hostname:5448/$image
|
||||||
|
[ "$status" -ne 0 ]
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user