123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- #!/usr/bin/env python
- # Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- #
- # Licensed under the Apache License, Version 2.0 (the "License"). You
- # may not use this file except in compliance with the License. A copy of
- # the License is located at
- #
- # http://aws.amazon.com/apache2.0/
- #
- # or in the "license" file accompanying this file. This file 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.
- """Driver for client scripts.
- How it Works
- ============
- This script is part of the infrastructure used for resource leak tests.
- At a high level, we're trying to verify that specific types of operations
- don't leak memory. For example:
- * Creating 100 clients in a loop doesn't leak memory within reason
- * Making 100 API calls doesn't leak memory
- * Streaming uploads/downloads to/from S3 have O(1) memory usage.
- In order to do this, these tests are written to utilize two separate
- processes: a driver and a runner. The driver sends commands to the
- runner which performs the actual commands. While the runner is
- running commands the driver can record various attributes about
- the runner process. So far this is just memory usage.
- There's a BaseClientDriverTest test that implements the driver part
- of the tests. These should read like normal functional/integration tests.
- This script (cmd-runner) implements the runner. It listens for commands
- from the driver and runs the commands.
- The driver and runner communicate via a basic text protocol. Commands
- are line separated with arguments separated by spaces. Commands
- are sent to the runner via stdin.
- On success, the runner response through stdout with "OK".
- Here's an example::
- +--------+ +-------+
- | Driver | |Runner |
- +--------+ +-------+
- create_client s3
- -------------------------------------------->
- OK
- <-------------------------------------------
- make_aws_request 100 ec2 describe_instances
- -------------------------------------------->
- OK
- <-------------------------------------------
- stream_s3_upload bucket key /tmp/foo.txt
- -------------------------------------------->
- OK
- <-------------------------------------------
- exit
- -------------------------------------------->
- OK
- <-------------------------------------------
- """
- import kscore.session
- import sys
- class CommandRunner(object):
- DEFAULT_REGION = 'cn-beijing-6'
- def __init__(self):
- self._session = kscore.session.get_session()
- self._clients = []
- self._waiters = []
- self._paginators = []
- def run(self):
- while True:
- line = sys.stdin.readline()
- parts = line.strip().split()
- if not parts:
- break
- elif parts[0] == 'exit':
- sys.stdout.write('OK\n')
- sys.stdout.flush()
- break
- else:
- getattr(self, '_do_%s' % parts[0])(parts[1:])
- sys.stdout.write('OK\n')
- sys.stdout.flush()
- def _do_create_client(self, args):
- # The args provided by the user map directly to the args
- # passed to create_client. At the least you need
- # to provide the service name.
- self._clients.append(self._session.create_client(*args))
- def _do_create_multiple_clients(self, args):
- # Args:
- # * num_clients - Number of clients to create in a loop
- # * client_args - Variable number of args to pass to create_client
- num_clients = args[0]
- client_args = args[1:]
- for _ in range(int(num_clients)):
- self._clients.append(self._session.create_client(*client_args))
- def _do_free_clients(self, args):
- # Frees the memory associated with all clients.
- self._clients = []
- def _do_create_waiter(self, args):
- # Args:
- # [0] client name used in its instantiation
- # [1] waiter name used in its instantiation
- client = self._create_client(args[0])
- self._waiters.append(client.get_waiter(args[1]))
- def _do_create_multiple_waiters(self, args):
- # Args:
- # [0] num_waiters - Number of clients to create in a loop
- # [1] client name used in its instantiation
- # [2] waiter name used in its instantiation
- num_waiters = args[0]
- client = self._create_client(args[1])
- for _ in range(int(num_waiters)):
- self._waiters.append(client.get_waiter(args[2]))
- def _do_free_waiters(self, args):
- # Frees the memory associated with all of the waiters.
- self._waiters = []
- def _do_create_paginator(self, args):
- # Args:
- # [0] client name used in its instantiation
- # [1] paginator name used in its instantiation
- client = self._create_client(args[0])
- self._paginators.append(client.get_paginator(args[1]))
- def _do_create_multiple_paginators(self, args):
- # Args:
- # [0] num_paginators - Number of paginators to create in a loop
- # [1] client name used in its instantiation
- # [2] paginator name used in its instantiation
- num_paginators = args[0]
- client = self._create_client(args[1])
- for _ in range(int(num_paginators)):
- self._paginators.append(client.get_paginator(args[2]))
- def _do_free_paginators(self, args):
- # Frees the memory associated with all of the waiters.
- self._paginators = []
- def _do_make_aws_request(self, args):
- # Create a client and make a number of AWS requests.
- # Args:
- # * num_requests - The number of requests to create in a loop
- # * service_name - The name of the AWS service
- # * operation_name - The name of the service, snake_cased
- # * oepration_args - Variable args, kwargs to pass to the API call,
- # in the format Kwarg1:Val1 Kwarg2:Val2
- num_requests = int(args[0])
- service_name = args[1]
- operation_name = args[2]
- kwargs = dict([v.split(':', 1) for v in args[3:]])
- client = self._create_client(service_name)
- method = getattr(client, operation_name)
- for _ in range(num_requests):
- method(**kwargs)
- def _create_client(self, service_name):
- # Create a client using the provided service name.
- # It will also inject a region name of self.DEFAULT_REGION.
- return self._session.create_client(service_name,
- region_name=self.DEFAULT_REGION)
- def run():
- CommandRunner().run()
- run()
|