From e904c8e2d35cb16f744a0de3b20466ad3befa36d Mon Sep 17 00:00:00 2001 From: Eddie Arrage Date: Sat, 28 Jul 2018 01:02:35 +0000 Subject: Implement initial Jmeter master/slave containers - Jmeter can be used for L4-7 functional and performance testing - Jmeter master has gRPC server for management - Generates Jmeter test plans from minimal yaml params file (sample to be added with cloverctl) using template - Optionally span tests across slave containers to allow greater loads to be generated - Specify loop/thread/slave count and URL list, which dictates target and number of connections that will be attempted - clover-controller will interface to gRPC interface on Jmeter master - Start tests on master and retrieve log/result files - Render master and slave k8s manifests files Change-Id: Id144c8f551b7d375ff252c8de0611f895b50387c Signed-off-by: Eddie Arrage --- clover/tools/jmeter/build_master.sh | 16 ++ clover/tools/jmeter/build_slave.sh | 16 ++ clover/tools/jmeter/jmeter-master/Dockerfile | 29 ++ .../tools/jmeter/jmeter-master/grpc/build_proto.sh | 11 + .../tools/jmeter/jmeter-master/grpc/jmeter.proto | 43 +++ .../tools/jmeter/jmeter-master/grpc/jmeter_pb2.py | 291 +++++++++++++++++++++ .../jmeter/jmeter-master/grpc/jmeter_pb2_grpc.py | 80 ++++++ .../jmeter/jmeter-master/grpc/jmeter_server.py | 118 +++++++++ .../jmeter/jmeter-master/process/grpc_process.sh | 11 + .../tools/jmeter/jmeter-master/tests/jmx.template | 135 ++++++++++ clover/tools/jmeter/jmeter-slave/Dockerfile | 27 ++ clover/tools/jmeter/rmi_keystore.jks | Bin 0 -> 2190 bytes clover/tools/jmeter/yaml/manifest.template | 45 ++++ clover/tools/jmeter/yaml/render_master.py | 67 +++++ clover/tools/jmeter/yaml/render_slave.py | 67 +++++ 15 files changed, 956 insertions(+) create mode 100755 clover/tools/jmeter/build_master.sh create mode 100755 clover/tools/jmeter/build_slave.sh create mode 100644 clover/tools/jmeter/jmeter-master/Dockerfile create mode 100755 clover/tools/jmeter/jmeter-master/grpc/build_proto.sh create mode 100644 clover/tools/jmeter/jmeter-master/grpc/jmeter.proto create mode 100644 clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2.py create mode 100644 clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2_grpc.py create mode 100644 clover/tools/jmeter/jmeter-master/grpc/jmeter_server.py create mode 100755 clover/tools/jmeter/jmeter-master/process/grpc_process.sh create mode 100644 clover/tools/jmeter/jmeter-master/tests/jmx.template create mode 100644 clover/tools/jmeter/jmeter-slave/Dockerfile create mode 100644 clover/tools/jmeter/rmi_keystore.jks create mode 100644 clover/tools/jmeter/yaml/manifest.template create mode 100644 clover/tools/jmeter/yaml/render_master.py create mode 100644 clover/tools/jmeter/yaml/render_slave.py diff --git a/clover/tools/jmeter/build_master.sh b/clover/tools/jmeter/build_master.sh new file mode 100755 index 0000000..5c5459a --- /dev/null +++ b/clover/tools/jmeter/build_master.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +IMAGE_PATH=${IMAGE_PATH:-"localhost:5000"} +IMAGE_NAME=${IMAGE_NAME:-"clover-jmeter-master"} + +docker build -f jmeter-master/Dockerfile -t $IMAGE_NAME . +docker tag $IMAGE_NAME $IMAGE_PATH/$IMAGE_NAME +docker push $IMAGE_PATH/$IMAGE_NAME diff --git a/clover/tools/jmeter/build_slave.sh b/clover/tools/jmeter/build_slave.sh new file mode 100755 index 0000000..1651c55 --- /dev/null +++ b/clover/tools/jmeter/build_slave.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +IMAGE_PATH=${IMAGE_PATH:-"localhost:5000"} +IMAGE_NAME=${IMAGE_NAME:-"clover-jmeter-slave"} + +docker build -f jmeter-slave/Dockerfile -t $IMAGE_NAME . +docker tag $IMAGE_NAME $IMAGE_PATH/$IMAGE_NAME +docker push $IMAGE_PATH/$IMAGE_NAME diff --git a/clover/tools/jmeter/jmeter-master/Dockerfile b/clover/tools/jmeter/jmeter-master/Dockerfile new file mode 100644 index 0000000..da0e474 --- /dev/null +++ b/clover/tools/jmeter/jmeter-master/Dockerfile @@ -0,0 +1,29 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +FROM java:8 + +RUN wget http://apache.mirrors.hoobly.com//jmeter/binaries/apache-jmeter-4.0.tgz +RUN tar -xvzf apache-jmeter-4.0.tgz +RUN rm apache-jmeter-4.0.tgz +RUN mv apache-jmeter-4.0 /jmeter +ENV JMETER_HOME /jmeter +ENV PATH $JMETER_HOME/bin:$PATH +COPY rmi_keystore.jks $JMETER_HOME/bin + +RUN apt update && apt install -y python-setuptools python-dev python-pip +RUN python -m pip install --upgrade pip +RUN python -m pip install enum34 futures cython +RUN python -m pip install grpcio protobuf jinja2 + +WORKDIR /jmeter/bin + +COPY jmeter-master/process process +COPY jmeter-master/grpc grpc +COPY jmeter-master/tests tests + +CMD ./process/grpc_process.sh no_init diff --git a/clover/tools/jmeter/jmeter-master/grpc/build_proto.sh b/clover/tools/jmeter/jmeter-master/grpc/build_proto.sh new file mode 100755 index 0000000..52dfd0a --- /dev/null +++ b/clover/tools/jmeter/jmeter-master/grpc/build_proto.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +python -m grpc_tools.protoc -I./ --python_out=. --grpc_python_out=. jmeter.proto diff --git a/clover/tools/jmeter/jmeter-master/grpc/jmeter.proto b/clover/tools/jmeter/jmeter-master/grpc/jmeter.proto new file mode 100644 index 0000000..7213faa --- /dev/null +++ b/clover/tools/jmeter/jmeter-master/grpc/jmeter.proto @@ -0,0 +1,43 @@ +// Copyright (c) Authors of Clover +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Apache License, Version 2.0 +// which accompanies this distribution, and is available at +// http://www.apache.org/licenses/LICENSE-2.0 + +syntax = "proto3"; + +package jmeter; + +// The controller service definition. +service Controller { + + rpc GenTest (ConfigJmeter) returns (JmeterReply) {} + rpc StartTest (TestParams) returns (JmeterReply) {} + rpc GetResults (JResults) returns (JmeterReply) {} +} + +message TestParams { + string num_slaves = 1; + string test_plan = 2; + string slave_ips = 3; +} + +message ConfigJmeter { + string url_list = 1; + string num_threads = 2; + string url_names = 3; + string url_protocols = 4; + string url_methods = 5; + string loops = 6; + string ramp_time = 7; +} + +message JmeterReply { + string message = 1; +} + +message JResults { + string r_format = 1; + string r_file = 2; +} diff --git a/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2.py b/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2.py new file mode 100644 index 0000000..e4a75fd --- /dev/null +++ b/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2.py @@ -0,0 +1,291 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: jmeter.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='jmeter.proto', + package='jmeter', + syntax='proto3', + serialized_pb=_b('\n\x0cjmeter.proto\x12\x06jmeter\"F\n\nTestParams\x12\x12\n\nnum_slaves\x18\x01 \x01(\t\x12\x11\n\ttest_plan\x18\x02 \x01(\t\x12\x11\n\tslave_ips\x18\x03 \x01(\t\"\x96\x01\n\x0c\x43onfigJmeter\x12\x10\n\x08url_list\x18\x01 \x01(\t\x12\x13\n\x0bnum_threads\x18\x02 \x01(\t\x12\x11\n\turl_names\x18\x03 \x01(\t\x12\x15\n\rurl_protocols\x18\x04 \x01(\t\x12\x13\n\x0burl_methods\x18\x05 \x01(\t\x12\r\n\x05loops\x18\x06 \x01(\t\x12\x11\n\tramp_time\x18\x07 \x01(\t\"\x1e\n\x0bJmeterReply\x12\x0f\n\x07message\x18\x01 \x01(\t\",\n\x08JResults\x12\x10\n\x08r_format\x18\x01 \x01(\t\x12\x0e\n\x06r_file\x18\x02 \x01(\t2\xb3\x01\n\nController\x12\x36\n\x07GenTest\x12\x14.jmeter.ConfigJmeter\x1a\x13.jmeter.JmeterReply\"\x00\x12\x36\n\tStartTest\x12\x12.jmeter.TestParams\x1a\x13.jmeter.JmeterReply\"\x00\x12\x35\n\nGetResults\x12\x10.jmeter.JResults\x1a\x13.jmeter.JmeterReply\"\x00\x62\x06proto3') +) + + + + +_TESTPARAMS = _descriptor.Descriptor( + name='TestParams', + full_name='jmeter.TestParams', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='num_slaves', full_name='jmeter.TestParams.num_slaves', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='test_plan', full_name='jmeter.TestParams.test_plan', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='slave_ips', full_name='jmeter.TestParams.slave_ips', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=24, + serialized_end=94, +) + + +_CONFIGJMETER = _descriptor.Descriptor( + name='ConfigJmeter', + full_name='jmeter.ConfigJmeter', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='url_list', full_name='jmeter.ConfigJmeter.url_list', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='num_threads', full_name='jmeter.ConfigJmeter.num_threads', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='url_names', full_name='jmeter.ConfigJmeter.url_names', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='url_protocols', full_name='jmeter.ConfigJmeter.url_protocols', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='url_methods', full_name='jmeter.ConfigJmeter.url_methods', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='loops', full_name='jmeter.ConfigJmeter.loops', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='ramp_time', full_name='jmeter.ConfigJmeter.ramp_time', index=6, + number=7, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=97, + serialized_end=247, +) + + +_JMETERREPLY = _descriptor.Descriptor( + name='JmeterReply', + full_name='jmeter.JmeterReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='message', full_name='jmeter.JmeterReply.message', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=249, + serialized_end=279, +) + + +_JRESULTS = _descriptor.Descriptor( + name='JResults', + full_name='jmeter.JResults', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='r_format', full_name='jmeter.JResults.r_format', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='r_file', full_name='jmeter.JResults.r_file', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=281, + serialized_end=325, +) + +DESCRIPTOR.message_types_by_name['TestParams'] = _TESTPARAMS +DESCRIPTOR.message_types_by_name['ConfigJmeter'] = _CONFIGJMETER +DESCRIPTOR.message_types_by_name['JmeterReply'] = _JMETERREPLY +DESCRIPTOR.message_types_by_name['JResults'] = _JRESULTS +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +TestParams = _reflection.GeneratedProtocolMessageType('TestParams', (_message.Message,), dict( + DESCRIPTOR = _TESTPARAMS, + __module__ = 'jmeter_pb2' + # @@protoc_insertion_point(class_scope:jmeter.TestParams) + )) +_sym_db.RegisterMessage(TestParams) + +ConfigJmeter = _reflection.GeneratedProtocolMessageType('ConfigJmeter', (_message.Message,), dict( + DESCRIPTOR = _CONFIGJMETER, + __module__ = 'jmeter_pb2' + # @@protoc_insertion_point(class_scope:jmeter.ConfigJmeter) + )) +_sym_db.RegisterMessage(ConfigJmeter) + +JmeterReply = _reflection.GeneratedProtocolMessageType('JmeterReply', (_message.Message,), dict( + DESCRIPTOR = _JMETERREPLY, + __module__ = 'jmeter_pb2' + # @@protoc_insertion_point(class_scope:jmeter.JmeterReply) + )) +_sym_db.RegisterMessage(JmeterReply) + +JResults = _reflection.GeneratedProtocolMessageType('JResults', (_message.Message,), dict( + DESCRIPTOR = _JRESULTS, + __module__ = 'jmeter_pb2' + # @@protoc_insertion_point(class_scope:jmeter.JResults) + )) +_sym_db.RegisterMessage(JResults) + + + +_CONTROLLER = _descriptor.ServiceDescriptor( + name='Controller', + full_name='jmeter.Controller', + file=DESCRIPTOR, + index=0, + options=None, + serialized_start=328, + serialized_end=507, + methods=[ + _descriptor.MethodDescriptor( + name='GenTest', + full_name='jmeter.Controller.GenTest', + index=0, + containing_service=None, + input_type=_CONFIGJMETER, + output_type=_JMETERREPLY, + options=None, + ), + _descriptor.MethodDescriptor( + name='StartTest', + full_name='jmeter.Controller.StartTest', + index=1, + containing_service=None, + input_type=_TESTPARAMS, + output_type=_JMETERREPLY, + options=None, + ), + _descriptor.MethodDescriptor( + name='GetResults', + full_name='jmeter.Controller.GetResults', + index=2, + containing_service=None, + input_type=_JRESULTS, + output_type=_JMETERREPLY, + options=None, + ), +]) +_sym_db.RegisterServiceDescriptor(_CONTROLLER) + +DESCRIPTOR.services_by_name['Controller'] = _CONTROLLER + +# @@protoc_insertion_point(module_scope) diff --git a/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2_grpc.py b/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2_grpc.py new file mode 100644 index 0000000..4df110d --- /dev/null +++ b/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2_grpc.py @@ -0,0 +1,80 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +import grpc + +import jmeter_pb2 as jmeter__pb2 + + +class ControllerStub(object): + """The controller service definition. + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.GenTest = channel.unary_unary( + '/jmeter.Controller/GenTest', + request_serializer=jmeter__pb2.ConfigJmeter.SerializeToString, + response_deserializer=jmeter__pb2.JmeterReply.FromString, + ) + self.StartTest = channel.unary_unary( + '/jmeter.Controller/StartTest', + request_serializer=jmeter__pb2.TestParams.SerializeToString, + response_deserializer=jmeter__pb2.JmeterReply.FromString, + ) + self.GetResults = channel.unary_unary( + '/jmeter.Controller/GetResults', + request_serializer=jmeter__pb2.JResults.SerializeToString, + response_deserializer=jmeter__pb2.JmeterReply.FromString, + ) + + +class ControllerServicer(object): + """The controller service definition. + """ + + def GenTest(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def StartTest(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetResults(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_ControllerServicer_to_server(servicer, server): + rpc_method_handlers = { + 'GenTest': grpc.unary_unary_rpc_method_handler( + servicer.GenTest, + request_deserializer=jmeter__pb2.ConfigJmeter.FromString, + response_serializer=jmeter__pb2.JmeterReply.SerializeToString, + ), + 'StartTest': grpc.unary_unary_rpc_method_handler( + servicer.StartTest, + request_deserializer=jmeter__pb2.TestParams.FromString, + response_serializer=jmeter__pb2.JmeterReply.SerializeToString, + ), + 'GetResults': grpc.unary_unary_rpc_method_handler( + servicer.GetResults, + request_deserializer=jmeter__pb2.JResults.FromString, + response_serializer=jmeter__pb2.JmeterReply.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'jmeter.Controller', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) diff --git a/clover/tools/jmeter/jmeter-master/grpc/jmeter_server.py b/clover/tools/jmeter/jmeter-master/grpc/jmeter_server.py new file mode 100644 index 0000000..cef180c --- /dev/null +++ b/clover/tools/jmeter/jmeter-master/grpc/jmeter_server.py @@ -0,0 +1,118 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +from concurrent import futures +from jinja2 import Template +from urlparse import urlparse +import time +import sys +import grpc +import subprocess +import pickle +import logging +import jmeter_pb2 +import jmeter_pb2_grpc + + +_ONE_DAY_IN_SECONDS = 60 * 60 * 24 +GRPC_PORT = '[::]:50054' + + +class Controller(jmeter_pb2_grpc.ControllerServicer): + + def __init__(self, init_jmeter): + logging.basicConfig(filename='jmeter_server.log', + level=logging.DEBUG) + self.jmeter = 0 + if init_jmeter == 'init': + print('init test') + + def GenTest(self, r, context): + try: + out_file = 'tests/test.jmx' + template_file = 'tests/jmx.template' + unames = pickle.loads(r.url_names) + umethods = pickle.loads(r.url_methods) + ulist = [] + for url in pickle.loads(r.url_list): + u = urlparse(url) + d = {} + d['domain'] = u.hostname + if u.port: + d['port'] = u.port + else: + d['port'] = 80 + if u.path == '': + d['path'] = '/' + else: + d['path'] = u.path + ulist.append(d) + + with open(template_file) as f: + tmpl = Template(f.read()) + output = tmpl.render( + num_threads=r.num_threads, + url_names=unames, + url_methods=umethods, + ramp_time=r.ramp_time, + loops=r.loops, + url_list=ulist + ) + with open(out_file, "wb") as fh: + fh.write(output) + msg = 'Generated test plan' + except Exception as e: + logging.debug(e) + msg = "Failed to generate test plan" + return jmeter_pb2.JmeterReply(message=msg) + + def StartTest(self, r, context): + try: + if r.num_slaves == '0': + self.jmeter = subprocess.Popen( + ["jmeter", "-n", "-t", "tests/test.jmx", "-l", + "default.jtl"], shell=False) + else: + slave_arg = "-R" + r.slave_ips + self.jmeter = subprocess.Popen( + ["jmeter", "-n", "-t", "tests/test.jmx", slave_arg, "-l", + "default.jtl"], shell=False) + msg = "Started jmeter on pid: {}".format(self.jmeter.pid) + except Exception as e: + logging.debug(e) + msg = e + return jmeter_pb2.JmeterReply(message=msg) + + def GetResults(self, r, context): + try: + if r.r_file == 'log': + r_file = 'jmeter.log' + else: + r_file = 'default.jtl' + f = open(r_file, 'r') + msg = "Retrieved all results\n" + f.read() + except Exception as e: + logging.debug(e) + msg = "Failed to retrieve results" + return jmeter_pb2.JmeterReply(message=msg) + + +def serve(init_jmeter): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + jmeter_pb2_grpc.add_ControllerServicer_to_server( + Controller(init_jmeter), server) + server.add_insecure_port(GRPC_PORT) + server.start() + try: + while True: + time.sleep(_ONE_DAY_IN_SECONDS) + except KeyboardInterrupt: + server.stop(0) + + +if __name__ == '__main__': + serve(sys.argv[1]) diff --git a/clover/tools/jmeter/jmeter-master/process/grpc_process.sh b/clover/tools/jmeter/jmeter-master/process/grpc_process.sh new file mode 100755 index 0000000..0f14d7c --- /dev/null +++ b/clover/tools/jmeter/jmeter-master/process/grpc_process.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +python grpc/jmeter_server.py test1 diff --git a/clover/tools/jmeter/jmeter-master/tests/jmx.template b/clover/tools/jmeter/jmeter-master/tests/jmx.template new file mode 100644 index 0000000..1a6fa95 --- /dev/null +++ b/clover/tools/jmeter/jmeter-master/tests/jmx.template @@ -0,0 +1,135 @@ + + + + + + false + true + false + + + + + + + + continue + + false + {{ loops }} + + {{ num_threads }} + {{ ramp_time }} + 1385457190000 + 1385457190000 + true + 60 + + true + + + {%- for u in url_list %} + + + + + {{ u['domain'] }} + {{ u['port'] }} + + + http + + {{ u['path'] }} + {{ url_methods[loop.index0] }} + true + false + true + false + false + + + + + {%- endfor %} + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + + + /jmeter/bin/results2 + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + + + /jmeter/bin/results_aggregate + + + + + + + diff --git a/clover/tools/jmeter/jmeter-slave/Dockerfile b/clover/tools/jmeter/jmeter-slave/Dockerfile new file mode 100644 index 0000000..b5ccbcd --- /dev/null +++ b/clover/tools/jmeter/jmeter-slave/Dockerfile @@ -0,0 +1,27 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +FROM java:8 + +RUN wget http://apache.mirrors.hoobly.com//jmeter/binaries/apache-jmeter-4.0.tgz +RUN tar -xvzf apache-jmeter-4.0.tgz +RUN rm apache-jmeter-4.0.tgz +RUN mv apache-jmeter-4.0 /jmeter +ENV JMETER_HOME /jmeter +ENV PATH $JMETER_HOME/bin:$PATH +RUN mkdir /share +COPY rmi_keystore.jks $JMETER_HOME/bin + +WORKDIR $JMETER_HOME +# Ports to be exposed from the container for JMeter Master +RUN mkdir scripts + +EXPOSE 1099 + +WORKDIR /jmeter/bin + +CMD ./jmeter-server diff --git a/clover/tools/jmeter/rmi_keystore.jks b/clover/tools/jmeter/rmi_keystore.jks new file mode 100644 index 0000000..f503361 Binary files /dev/null and b/clover/tools/jmeter/rmi_keystore.jks differ diff --git a/clover/tools/jmeter/yaml/manifest.template b/clover/tools/jmeter/yaml/manifest.template new file mode 100644 index 0000000..01a4595 --- /dev/null +++ b/clover/tools/jmeter/yaml/manifest.template @@ -0,0 +1,45 @@ +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{ deploy_name }} + labels: + app: {{ deploy_name }} +spec: +{%- if replica_count %} + replicas: 3{% endif %} + template: + metadata: + labels: + app: {{ deploy_name }} + spec: + containers: + - name: {{ deploy_name }} + image: {{ image_path }}/{{ image_name }}:{{ image_tag }} + ports: +{%- if grpc_port %} + - containerPort: {{ grpc_port }}{% endif %} + - containerPort: {{ rmi_port }} + - containerPort: {{ http_port }} + - containerPort: {{ ssl_port }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ deploy_name }} + labels: + app: {{ deploy_name }} +spec: + ports: +{%- if grpc_port %} + - port: {{ grpc_port }} + name: grpc{% endif %} + - port: {{ rmi_port }} + name: rmi + - port: {{ http_port }} + name: http + - port: {{ ssl_port }} + name: https + selector: + app: {{ deploy_name }} +--- diff --git a/clover/tools/jmeter/yaml/render_master.py b/clover/tools/jmeter/yaml/render_master.py new file mode 100644 index 0000000..884ee81 --- /dev/null +++ b/clover/tools/jmeter/yaml/render_master.py @@ -0,0 +1,67 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +import argparse + +from jinja2 import Template + + +def render_yaml(args): + template_file = 'manifest.template' + out_file = args['deploy_name'] + '.yaml' + + try: + with open(template_file) as f: + tmpl = Template(f.read()) + output = tmpl.render( + image_path=args['image_path'], + image_name=args['image_name'], + image_tag=args['image_tag'], + deploy_name=args['deploy_name'], + grpc_port=args['grpc_port'], + ssl_port=args['ssl_port'], + rmi_port=args['rmi_port'], + http_port=args['http_port'] + ) + with open(out_file, "wb") as fh: + fh.write(output) + return "Generated manifest for {}".format(args['deploy_name']) + except Exception as e: + print(e) + return "Unable to generate manifest for {}".format( + args['deploy_name']) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + '--image_name', default='clover-jmeter-master', + help='The image name to use') + parser.add_argument( + '--image_path', default='localhost:5000', + help='The path to the image to use') + parser.add_argument( + '--image_tag', default='latest', + help='The image tag to use') + parser.add_argument( + '--deploy_name', default='clover-jmeter-master', + help='The k8s deploy name to use') + parser.add_argument( + '--rmi_port', default='1099', + help='The master-slave remote method invocation port') + parser.add_argument( + '--http_port', default='80', + help='HTTP data-plane traffic') + parser.add_argument( + '--grpc_port', default='50054', + help='The GRPC server port for management') + parser.add_argument( + '--ssl_port', default='443', + help='HTTPS data-plane traffic') + + args = parser.parse_args() + print(render_yaml(vars(args))) diff --git a/clover/tools/jmeter/yaml/render_slave.py b/clover/tools/jmeter/yaml/render_slave.py new file mode 100644 index 0000000..cd9fd91 --- /dev/null +++ b/clover/tools/jmeter/yaml/render_slave.py @@ -0,0 +1,67 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +import argparse + +from jinja2 import Template + + +def render_yaml(args): + template_file = 'manifest.template' + out_file = args['deploy_name'] + '.yaml' + + try: + with open(template_file) as f: + tmpl = Template(f.read()) + output = tmpl.render( + image_path=args['image_path'], + image_name=args['image_name'], + image_tag=args['image_tag'], + deploy_name=args['deploy_name'], + replica_count=args['replica_count'], + ssl_port=args['ssl_port'], + rmi_port=args['rmi_port'], + http_port=args['http_port'] + ) + with open(out_file, "wb") as fh: + fh.write(output) + return "Generated manifest for {}".format(args['deploy_name']) + except Exception as e: + print(e) + return "Unable to generate manifest for {}".format( + args['deploy_name']) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + '--image_name', default='clover-jmeter-slave', + help='The image name to use') + parser.add_argument( + '--image_path', default='localhost:5000', + help='The path to the image to use') + parser.add_argument( + '--image_tag', default='latest', + help='The image tag to use') + parser.add_argument( + '--deploy_name', default='clover-jmeter-slave', + help='The k8s deploy name to use') + parser.add_argument( + '--rmi_port', default='1099', + help='The master-slave remote method invocation port') + parser.add_argument( + '--http_port', default='80', + help='HTTP data-plane traffic') + parser.add_argument( + '--replica_count', default='3', + help='Number of replicas in slave deployment') + parser.add_argument( + '--ssl_port', default='443', + help='HTTPS data-plane traffic') + + args = parser.parse_args() + print(render_yaml(vars(args))) -- cgit 1.2.3-korg