Skip to content

Commit

Permalink
CADC-8602 (#175)
Browse files Browse the repository at this point in the history
* Added transfer negotiation tests
  • Loading branch information
andamian committed Nov 18, 2020
1 parent 66152fd commit 0b8b2ae
Show file tree
Hide file tree
Showing 14 changed files with 574 additions and 284 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Expand Up @@ -80,7 +80,7 @@ jobs:
done
coverage combine $(ls -d */.coverage) || exit 1;
- name: Consolidate coverage
env:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
CI_BRANCH: ${GITHUB_REF#"ref/heads"}
Expand Down
28 changes: 28 additions & 0 deletions vos/perftest/Dockerfile
@@ -0,0 +1,28 @@
FROM locustio/locust:latest

USER root

#RUN apk --no-cache add gcc git libffi-dev libxslt-dev make musl-dev openssl-dev
#RUN pip install --pre vos==3.3.a1
RUN pip install -e 'git+https://github.com/opencadc/vostools@newstorage#egg=vostools&subdirectory=vos'

# Make requests trust all certificates
ENV CURL_CA_BUNDLE=

#COPY dev_requirements.txt /tmp/
COPY locustfile.py .
COPY testers.py .
COPY test_config .
RUN mkdir cert
COPY cert/* cert/

COPY entrypoint.sh /

RUN chmod +x /entrypoint.sh

#RUN pip install -r /tmp/dev_requirements.txt

USER locust

ENTRYPOINT ["/entrypoint.sh"]
#CMD ["./docker_start.sh"]
30 changes: 30 additions & 0 deletions vos/perftest/README.md
@@ -0,0 +1,30 @@
# Performance Tests

Testing was done using a product called [Locust](https://locust.io). It runs Python testing code exercising the transfer negotiation and vcp.

Locust allows us to scale up Workers (Called Users to Locust) to issue multiple simultaneous requests. Tests are executed through an intuitive UI, which features the ability to ramp up Workers per second to a set maximum. Tests run until they are manually stopped.

The test_config file contains configuration info used to execute the tests tests.

## Running it

The system runs on port 8089 with Docker and requires two volumes to be created and populated first:

* `cert`: to hold the `cadcproxy.pem` file of the authenticated user that runs the tests.


### Docker Run

The image will need to be built first:

`$ docker build -t myimage .`

Foreground run:
`$ docker run -t --rm -p 8089:8089 myimage`

Background run:
`$ docker run -t -d --name locust -p 8089:8089 myimage`

Then you can visit http://localhost:8089 to use the UI and start a run.

IMPORTANT: Make sure that the testing machines are accessible (they are either public or the appropriate VPN is running)
Empty file added vos/perftest/__init__.py
Empty file.
3 changes: 3 additions & 0 deletions vos/perftest/entrypoint.sh
@@ -0,0 +1,3 @@
#!/bin/sh
locust
#exec "${@}"
175 changes: 175 additions & 0 deletions vos/perftest/locustfile.py
@@ -0,0 +1,175 @@
import os
import sys
import time
import inspect
import logging

from locust import User, events, task, constant_pacing
from uuid import uuid1
import vos
from vos.commands import vcp
from random import randrange
from testers import GlobalTester, config

DEBUG = os.getenv('DEBUG', None)
ONE_KB = 1024
try:
call_freq = int(config['CALL_FREQ'])
except KeyError:
call_freq = 60
logger = logging.getLogger('locust.main')
logger.info('Running with {} calls/s'.format(call_freq))
# TODO Don't know how to do it with a logger
print('Running with {} calls/s'.format(call_freq))


def _touch(path, size=ONE_KB):
with open(path, 'a') as f:
f.truncate(size)
f.close()
return path


class VCPClient(object):

def __init__(self, resource_id, certfile):
self.resource_id = resource_id
self.certfile = certfile

"""
Simple, sample XML RPC client implementation that wraps storage_inventory.Client and
fires locust events on request_success and request_failure, so that all requests
gets tracked in locust's statistics.
"""
def copy(self, source, destination, name, size):
_args = ['vcp']
if DEBUG is not None:
_args.append('-d')

_args.extend(['--certfile={}'.format(self.certfile),
'--resource-id={}'.format(self.resource_id),
source, destination])
sys.argv = _args

start_time = time.time()
try:
vcp()
except Exception as e:
total_time = int((time.time() - start_time) * 1000)
events.request_failure.fire(request_type='vcp', name='vcp:{}__{}'.format(source, destination), response_time=total_time, response_length=0, exception=e)
else:
total_time = int((time.time() - start_time) * 1000)
events.request_success.fire(request_type='vcp', name=name, response_time=total_time, response_length=size)
# In this example, we've hardcoded response_length=0. If we would want the response length to be
# reported correctly in the statistics, we would probably need to hook in at a lower level


def stopwatch(func):
"""
Wrapper to report a function execution time to locust
:param func:
:return:
"""
def wrapper(*args, **kwargs):
# get task's function name
previous_frame = inspect.currentframe().f_back
_, _, task_name, _, _ = inspect.getframeinfo(previous_frame)

start = time.time()
result = None
try:
result = func(*args, **kwargs)
except Exception as e:
total = int((time.time() - start) * 1000)
events.request_failure.fire(request_type="TYPE",
name=func.__name__,
response_time=total,
response_length=0,
exception=e)
else:
total = int((time.time() - start) * 1000)
events.request_success.fire(request_type="TYPE",
name=func.__name__,
response_time=total,
response_length=0)
return result

return wrapper

class Global(User):
wait_time = constant_pacing(60/call_freq)

def __init__(self, *args, **kwargs):
super(Global, self).__init__(*args, **kwargs)
self.global_tester = GlobalTester()

@task
@stopwatch
def anon_get_pub(self):
"""
Negotiate a transfer request for a random public file
:return:
"""
self.global_tester.anon_get_pub()

@task
@stopwatch
def anon_get_priv(self):
"""
Unauthorized failures
:return:
"""
self.global_tester.anon_get_priv()

@task
@stopwatch
def auth_get_pub(self):
"""
Negotiate a transfer request for a random public file
:return:
"""
self.global_tester.auth_get_pub()

@task
@stopwatch
def auth_get_priv(self):
self.global_tester.auth_get_priv()

@task
@stopwatch
def anon_put(self):
"""
Negotiate a transfer request for pushing a file anon - Failure
:return:
"""
self.global_tester.anon_put()

@task
@stopwatch
def auth_put(self):
"""
Negotiate a transfer request for pushing a file
:return:
"""
self.global_tester.auth_put()


# class ApiUser(VCPLocust):
# wait_time = between(0.1, 1)
# source = ''
# size = int(os.getenv('FILE_SIZE_IN_BYTES', ONE_KB))
#
# class task_set(TaskSet):
# def setup(self):
# file_dir = os.getenv('FILE_DIR', '/tmp')
# ApiUser.source = _touch('{}/SOURCE_FILE.txt'.format(file_dir), ApiUser.size)
#
# def teardown(self):
# os.remove(ApiUser.source)
#
# @task
# def upload(self):
# file_id = uuid1()
# dest = 'cadc:CADCRegtest1/{}.txt'.format(file_id)
# self.client.copy(ApiUser.source, dest, 'upload_{}'.format(ApiUser.size), ApiUser.size)

20 changes: 20 additions & 0 deletions vos/perftest/test_config
@@ -0,0 +1,20 @@
[GENERAL]
REG=mach275.cadc.dao.nrc.ca
CERT_FILE=./cert/cadcproxy.pem
# number of calls/user/sec
CALL_FREQ=120
#ANON_TESTS_ONLY=True
#AUTH_TESTS_ONLY=True


[GLOBAL]
RESOURCE_ID=ivo://cadc.nrc.ca/alpha-global/raven
# luskan sync url
LUSKAN_URL=https://proc5-27.cadc.dao.nrc.ca/luskan/sync

ARGUS_URL=https://www.cadc-ccda.hia-iha.nrc-cnrc.gc.ca/argus/sync
# number of public/private uris to randomly access in order to avoid the effect of caching on test results.
NUM_URIS = 1000

[LOCAL]
RESOURCE_ID=ivo://cadc.nrc.ca/alpha-global/raven

0 comments on commit 0b8b2ae

Please sign in to comment.