cmd-runner 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. #!/usr/bin/env python
  2. # Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"). You
  5. # may not use this file except in compliance with the License. A copy of
  6. # the License is located at
  7. #
  8. # http://aws.amazon.com/apache2.0/
  9. #
  10. # or in the "license" file accompanying this file. This file is
  11. # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
  12. # ANY KIND, either express or implied. See the License for the specific
  13. # language governing permissions and limitations under the License.
  14. """Driver for client scripts.
  15. How it Works
  16. ============
  17. This script is part of the infrastructure used for resource leak tests.
  18. At a high level, we're trying to verify that specific types of operations
  19. don't leak memory. For example:
  20. * Creating 100 clients in a loop doesn't leak memory within reason
  21. * Making 100 API calls doesn't leak memory
  22. * Streaming uploads/downloads to/from S3 have O(1) memory usage.
  23. In order to do this, these tests are written to utilize two separate
  24. processes: a driver and a runner. The driver sends commands to the
  25. runner which performs the actual commands. While the runner is
  26. running commands the driver can record various attributes about
  27. the runner process. So far this is just memory usage.
  28. There's a BaseClientDriverTest test that implements the driver part
  29. of the tests. These should read like normal functional/integration tests.
  30. This script (cmd-runner) implements the runner. It listens for commands
  31. from the driver and runs the commands.
  32. The driver and runner communicate via a basic text protocol. Commands
  33. are line separated with arguments separated by spaces. Commands
  34. are sent to the runner via stdin.
  35. On success, the runner response through stdout with "OK".
  36. Here's an example::
  37. +--------+ +-------+
  38. | Driver | |Runner |
  39. +--------+ +-------+
  40. create_client s3
  41. -------------------------------------------->
  42. OK
  43. <-------------------------------------------
  44. make_aws_request 100 ec2 describe_instances
  45. -------------------------------------------->
  46. OK
  47. <-------------------------------------------
  48. stream_s3_upload bucket key /tmp/foo.txt
  49. -------------------------------------------->
  50. OK
  51. <-------------------------------------------
  52. exit
  53. -------------------------------------------->
  54. OK
  55. <-------------------------------------------
  56. """
  57. import kscore.session
  58. import sys
  59. class CommandRunner(object):
  60. DEFAULT_REGION = 'cn-beijing-6'
  61. def __init__(self):
  62. self._session = kscore.session.get_session()
  63. self._clients = []
  64. self._waiters = []
  65. self._paginators = []
  66. def run(self):
  67. while True:
  68. line = sys.stdin.readline()
  69. parts = line.strip().split()
  70. if not parts:
  71. break
  72. elif parts[0] == 'exit':
  73. sys.stdout.write('OK\n')
  74. sys.stdout.flush()
  75. break
  76. else:
  77. getattr(self, '_do_%s' % parts[0])(parts[1:])
  78. sys.stdout.write('OK\n')
  79. sys.stdout.flush()
  80. def _do_create_client(self, args):
  81. # The args provided by the user map directly to the args
  82. # passed to create_client. At the least you need
  83. # to provide the service name.
  84. self._clients.append(self._session.create_client(*args))
  85. def _do_create_multiple_clients(self, args):
  86. # Args:
  87. # * num_clients - Number of clients to create in a loop
  88. # * client_args - Variable number of args to pass to create_client
  89. num_clients = args[0]
  90. client_args = args[1:]
  91. for _ in range(int(num_clients)):
  92. self._clients.append(self._session.create_client(*client_args))
  93. def _do_free_clients(self, args):
  94. # Frees the memory associated with all clients.
  95. self._clients = []
  96. def _do_create_waiter(self, args):
  97. # Args:
  98. # [0] client name used in its instantiation
  99. # [1] waiter name used in its instantiation
  100. client = self._create_client(args[0])
  101. self._waiters.append(client.get_waiter(args[1]))
  102. def _do_create_multiple_waiters(self, args):
  103. # Args:
  104. # [0] num_waiters - Number of clients to create in a loop
  105. # [1] client name used in its instantiation
  106. # [2] waiter name used in its instantiation
  107. num_waiters = args[0]
  108. client = self._create_client(args[1])
  109. for _ in range(int(num_waiters)):
  110. self._waiters.append(client.get_waiter(args[2]))
  111. def _do_free_waiters(self, args):
  112. # Frees the memory associated with all of the waiters.
  113. self._waiters = []
  114. def _do_create_paginator(self, args):
  115. # Args:
  116. # [0] client name used in its instantiation
  117. # [1] paginator name used in its instantiation
  118. client = self._create_client(args[0])
  119. self._paginators.append(client.get_paginator(args[1]))
  120. def _do_create_multiple_paginators(self, args):
  121. # Args:
  122. # [0] num_paginators - Number of paginators to create in a loop
  123. # [1] client name used in its instantiation
  124. # [2] paginator name used in its instantiation
  125. num_paginators = args[0]
  126. client = self._create_client(args[1])
  127. for _ in range(int(num_paginators)):
  128. self._paginators.append(client.get_paginator(args[2]))
  129. def _do_free_paginators(self, args):
  130. # Frees the memory associated with all of the waiters.
  131. self._paginators = []
  132. def _do_make_aws_request(self, args):
  133. # Create a client and make a number of AWS requests.
  134. # Args:
  135. # * num_requests - The number of requests to create in a loop
  136. # * service_name - The name of the AWS service
  137. # * operation_name - The name of the service, snake_cased
  138. # * oepration_args - Variable args, kwargs to pass to the API call,
  139. # in the format Kwarg1:Val1 Kwarg2:Val2
  140. num_requests = int(args[0])
  141. service_name = args[1]
  142. operation_name = args[2]
  143. kwargs = dict([v.split(':', 1) for v in args[3:]])
  144. client = self._create_client(service_name)
  145. method = getattr(client, operation_name)
  146. for _ in range(num_requests):
  147. method(**kwargs)
  148. def _create_client(self, service_name):
  149. # Create a client using the provided service name.
  150. # It will also inject a region name of self.DEFAULT_REGION.
  151. return self._session.create_client(service_name,
  152. region_name=self.DEFAULT_REGION)
  153. def run():
  154. CommandRunner().run()
  155. run()