def test_validate_command_choices_dictionary_list_response( self, validator): mock_client = Mock() mock_client.send_bg_request.return_value.output = '[{"value": "value"}]' validator._client = mock_client request = Request( system="foo", command="command1", parameters={"key1": "value"}, system_version="0.0.1", instance_name="instance_name", ) command = Mock(parameters=[ Parameter( key="key1", choices=Choices(type="command", value="command_name"), optional=False, ) ]) validator.get_and_validate_parameters(request, command) mock_client.send_bg_request.assert_called_with( _command="command_name", _system_name="foo", _system_version="0.0.1", _instance_name="instance_name", )
def test_command_lookup(self, monkeypatch, validator): request = Request(parameters={}) lookup_mock = Mock(return_value=Mock(parameters=[])) monkeypatch.setattr(validator, "get_and_validate_command_for_system", lookup_mock) validator.get_and_validate_parameters(request) lookup_mock.assert_called_once_with(request)
def setUp(self): self.request_mock = Mock() self.ts_epoch = 1451606400000 self.ts_dt = datetime.datetime(2016, 1, 1) self.request_dict = { "children": [], "parent": None, "system": "system_name", "system_version": "0.0.1", "instance_name": "default", "command": "say", "id": "58542eb571afd47ead90d25f", "parameters": {}, "comment": "bye!", "output": "nested output", "output_type": "STRING", "status": "IN_PROGRESS", "command_type": "ACTION", "created_at": self.ts_epoch, "updated_at": self.ts_epoch, "error_class": None, "metadata": {}, "has_parent": True, "requester": None, } self.job_dict = { "name": "job_name", "trigger_type": "date", "trigger": {"run_date": self.ts_epoch, "timezone": "utc"}, "request_template": { "system": "system", "system_version": "1.0.0", "instance_name": "default", "command": "speak", "parameters": {"message": "hey!"}, "comment": "hi!", "metadata": {"request": "stuff"}, }, "misfire_grace_time": 3, "coalesce": True, "next_run_time": self.ts_epoch, "success_count": 0, "error_count": 0, } db_dict = copy.deepcopy(self.job_dict) db_dict["request_template"] = RequestTemplate(**db_dict["request_template"]) db_dict["trigger"]["run_date"] = self.ts_dt db_dict["trigger"] = DateTrigger(**db_dict["trigger"]) db_dict["next_run_time"] = self.ts_dt self.job = Job(**db_dict) db_dict = copy.deepcopy(self.request_dict) db_dict["created_at"] = self.ts_dt db_dict["updated_at"] = self.ts_dt self.request = Request(**db_dict) super(RequestAPITest, self).setUp()
def test_save_update_updated_at(self): request = Request( system="foo", command="bar", status="CREATED", updated_at="this_will_be_updated", ) request.save() self.assertNotEqual(request.updated_at, "this_will_be_updated")
def test_validate_value_in_choices_no_choices(self, validator): req = Request(system="foo", command="command1", parameters={"key1": "value"}) command_parameter = Mock(key="key1", multi=False, type="String", choices=None) command = Mock(parameters=[command_parameter]) validator.get_and_validate_parameters(req, command)
def test_update_and_validate_parameter_extract_parameter_multi_not_list( self, validator): req = Request(system="foo", command="command1", parameters={"key1": "NOT A LIST"}) command_parameter = Mock(key="key1", multi=True) command = Mock(parameters=[command_parameter]) with pytest.raises(ModelValidationError): validator.get_and_validate_parameters(req, command)
def __init__(self, clients, heartbeat_interval=10, timeout_seconds=30): self.logger = logging.getLogger(__name__) self.display_name = "Plugin Status Monitor" self.clients = clients self.heartbeat_interval = heartbeat_interval self.timeout = timedelta(seconds=timeout_seconds) self.status_request = Request(command="_status", command_type="EPHEMERAL") super(PluginStatusMonitor, self).__init__(logger=self.logger, name="PluginStatusMonitor")
def test_update_and_validate_parameter_extract_parameter_nullable_no_default( self, validator): req = Request(system="foo", command="command1", parameters={}) command_parameter = Parameter(key="key1", multi=False, nullable=True, default=None) command = Mock(parameters=[command_parameter]) validated_parameters = validator.get_and_validate_parameters( req, command) assert validated_parameters["key1"] is None
def test_validate_regex_nullable(self, validator): req = Request(system="foo", command="command1", parameters={"key1": None}) command_parameter = Parameter(key="key1", multi=False, type="String", regex=r"^Hi.*", nullable=True) command = Command("test", parameters=[command_parameter]) validator.get_and_validate_parameters(req, command)
def test_update_and_validate_parameter_extract_parameter_optional_no_default( self, validator): req = Request(system="foo", command="command1", parameters={}) command_parameter = Parameter(key="key1", multi=False, optional=True, default=None) command = Mock(parameters=[command_parameter]) with pytest.raises(ModelValidationError): validator.get_and_validate_parameters(req, command)
def setUp(self): brew_view.backend = Mock() brew_view.transport = Mock() self.default_request = Request( system="foo", command="bar", parameters={"baz": "bat"}, output="output", status="CREATED", ) self.default_request.validate_backend = Mock() self.app = brew_view.app.test_client()
def test_validate_value_in_choices_multi_valid_choice(self, validator): req = Request(system="foo", command="command1", parameters={"key1": ["v1", "v2"]}) command_parameter = Mock( key="key1", multi=True, type="String", choices=Mock(type="static", value=["v1", "v2"]), ) command = Mock(parameters=[command_parameter]) validator.get_and_validate_parameters(req, command)
def test_validate_value_in_choices_optional_none_allowed(self, validator): req = Request(system="foo", command="command1", parameters={}) command_parameter = Mock( key="key1", multi=False, type="String", optional=True, default=None, choices=Mock(type="static", value=["value1", "value3"]), ) command = Mock(parameters=[command_parameter]) validator.get_and_validate_parameters(req, command)
def test_extract_parameter_non_multi_calls_with_default( self, validate_mock, validator): req = Request(system="foo", command="command1", parameters={}) command_parameter = Mock(key="key1", multi=False, default="default_value") command = Mock(parameters=[command_parameter]) validate_mock.side_effect = lambda w, x, y, z: w validator.get_and_validate_parameters(req, command) validate_mock.assert_called_once_with("default_value", command_parameter, command, req)
def test_validate_parameter_based_on_type_null_not_nullable( self, validator): req = Request(system="foo", command="command1", parameters={"key1": None}) command_parameter = Mock(key="key1", multi=False, type="String", nullable=False) command = Mock(parameters=[command_parameter]) with pytest.raises(ModelValidationError): validator.get_and_validate_parameters(req, command)
def test_validate_maximum_nullable(self, validator): req = Request(system="foo", command="command1", parameters={"key1": None}) command_parameter = Parameter( key="key1", multi=False, type="Integer", optional=False, minimum=3, nullable=True, ) command = Command("test", parameters=[command_parameter]) validator.get_and_validate_parameters(req, command)
def test_validate_value_in_choices_multi_invalid_choice(self, validator): req = Request(system="foo", command="command1", parameters={"key1": ["v1", "v2"]}) command_parameter = Mock( key="key1", multi=True, type="String", optional=False, choices=Mock(type="static", value=["v1", "v3"]), ) command = Mock(parameters=[command_parameter]) with pytest.raises(ModelValidationError): validator.get_and_validate_parameters(req, command)
def test_missing_nested_parameters(self, validator): req = Request(system="foo", command="command1", parameters={"key1": {}}) nested_parameter = Mock(key="foo", multi=False, type="String", optional=False) command_parameter = Mock(key="key1", multi=False, type="Dictionary", parameters=[nested_parameter]) command = Mock(parameters=[command_parameter]) with pytest.raises(ModelValidationError): validator.get_and_validate_parameters(req, command)
def test_update_and_validate_parameter_extract_parameter_multi( self, validate_mock, validator): req = Request(system="foo", command="command1", parameters={"key1": [1, 2]}) command_parameter = Mock(key="key1", multi=True) command = Mock(parameters=[command_parameter]) validate_mock.side_effect = lambda w, x, y, z: w validator.get_and_validate_parameters(req, command) validate_mock.assert_has_calls( [ call(1, command_parameter, command, req), call(2, command_parameter, command, req), ], any_order=True, )
def setUp(self): self.app = brew_view.app.test_client() self.default_request = Request( system="foo", command="bar", parameters={"baz": "bat"}, output="output", status="CREATED", ) self.default_request.validate_backend = Mock() objects_patch = patch("bg_utils.mongo.models.Request.objects") self.addCleanup(objects_patch.stop) self.objects_mock = objects_patch.start() self.objects_mock.return_value = None self.objects_mock.get = Mock(return_value=self.default_request)
def test_validate_command_choices_bad_value_type(self, validator): mock_client = Mock() mock_client.send_bg_request.return_value.output = '["value"]' validator._client = mock_client request = Request( system="foo", command="command1", parameters={"key1": "value"}, system_version="0.0.1", instance_name="instance_name", ) command = Mock(parameters=[ Parameter(key="key1", optional=False, choices=Choices(type="command", value=1)) ]) with pytest.raises(ModelValidationError): validator.get_and_validate_parameters(request, command)
def test_validate_minimum_non_sequence(self, validator): req = Request(system="foo", command="command1", parameters={"key1": 5}) command_parameter = Parameter(key="key1", multi=False, type="Integer", optional=False, minimum=3) command = Command("test", parameters=[command_parameter]) validator.get_and_validate_parameters(req, command) command_parameter = Parameter(key="key1", multi=False, type="Integer", optional=False, minimum=10) command = Command("test", parameters=[command_parameter]) with pytest.raises(ModelValidationError): validator.get_and_validate_parameters(req, command)
def test_validate_choices_static_bad_type(self, validator): command_parameter = Mock( key="key1", multi=False, type="String", optional=False, default=None, choices=Mock(type="static", value="bad str"), minimum=None, maximum=None, regex=None, ) command = Mock(parameters=[command_parameter]) req = Request(system="foo", command="command1", parameters={"key1": "1"}) with pytest.raises(ModelValidationError): validator.get_and_validate_parameters(req, command)
def test_validate_url_choices(self, validator, response): session_mock = Mock() session_mock.get.return_value.text = response validator._session = session_mock req = Request(system="foo", command="command1", parameters={"key1": "value"}) command_parameter = Mock( key="key1", type="String", optional=False, multi=False, choices=Mock(type="url", value="http://localhost"), minimum=None, maximum=None, regex=None, ) command = Mock(parameters=[command_parameter]) validator.get_and_validate_parameters(req, command) session_mock.get.assert_called_with("http://localhost", params={})
def test_validate_regex(self, validator): req = Request(system="foo", command="command1", parameters={"key1": "Hi World!"}) command_parameter = Parameter(key="key1", multi=False, type="String", optional=False, regex=r"^Hi.*") command = Command("test", parameters=[command_parameter]) validator.get_and_validate_parameters(req, command) command_parameter = Parameter(key="key1", multi=False, type="String", optional=False, regex=r"^Hello.*") command = Command("test", parameters=[command_parameter]) with pytest.raises(ModelValidationError): validator.get_and_validate_parameters(req, command)
def test_clean_fail_bad_status(self): request = Request(system="foo", command="bar", status="bad") self.assertRaises(ModelValidationError, request.clean)
def post(self): """ --- summary: Create a new Request parameters: - name: request in: body description: The Request definition schema: $ref: '#/definitions/Request' - name: blocking in: query required: false description: Flag indicating whether to wait for request completion type: boolean default: false - name: timeout in: query required: false description: Maximum time (seconds) to wait for request completion type: integer default: None (Wait forever) consumes: - application/json - application/x-www-form-urlencoded responses: 201: description: A new Request has been created schema: $ref: '#/definitions/Request' headers: Instance-Status: type: string description: | Current status of the Instance that will process the created Request 400: $ref: '#/definitions/400Error' 50x: $ref: '#/definitions/50xError' tags: - Requests """ self.request.event.name = Events.REQUEST_CREATED.name if self.request.mime_type == "application/json": request_model = self.parser.parse_request( self.request.decoded_body, from_string=True) elif self.request.mime_type == "application/x-www-form-urlencoded": args = {"parameters": {}} for key, value in self.request.body_arguments.items(): if key.startswith("parameters."): args["parameters"][key.replace("parameters.", "")] = value[0].decode( self.request.charset) else: args[key] = value[0].decode(self.request.charset) request_model = Request(**args) else: raise ModelValidationError( "Unsupported or missing content-type header") if request_model.parent: request_model.parent = Request.objects.get( id=str(request_model.parent.id)) if request_model.parent.status in Request.COMPLETED_STATUSES: raise ConflictError("Parent request has already completed") request_model.has_parent = True else: request_model.has_parent = False if self.current_user: request_model.requester = self.current_user.username # Ok, ready to save request_model.save() request_id = str(request_model.id) # Set up the wait event BEFORE yielding the processRequest call blocking = self.get_argument("blocking", default="").lower() == "true" if blocking: brew_view.request_map[request_id] = Event() with thrift_context() as client: try: yield client.processRequest(request_id) except bg_utils.bg_thrift.InvalidRequest as ex: request_model.delete() raise ModelValidationError(ex.message) except bg_utils.bg_thrift.PublishException as ex: request_model.delete() raise RequestPublishException(ex.message) except Exception: if request_model.id: request_model.delete() raise # Query for request from body id req = Request.objects.get(id=request_id) # Now attempt to add the instance status as a header. # The Request is already created at this point so it's a best-effort thing self.set_header("Instance-Status", "UNKNOWN") try: # Since request has system info we can query for a system object system = System.objects.get(name=req.system, version=req.system_version) # Loop through all instances in the system until we find the instance that # matches the request instance for instance in system.instances: if instance.name == req.instance_name: self.set_header("Instance-Status", instance.status) # The Request is already created at this point so adding the Instance status # header is a best-effort thing except Exception as ex: self.logger.exception( "Unable to get Instance status for Request %s: %s", request_id, ex) self.request.event_extras = {"request": req} # Metrics request_created(request_model) if blocking: # Publish metrics and event here here so they aren't skewed # See https://github.com/beer-garden/beer-garden/issues/190 self.request.publish_metrics = False http_api_latency_total.labels( method=self.request.method.upper(), route=self.prometheus_endpoint, status=self.get_status(), ).observe(request_latency(self.request.created_time)) self.request.publish_event = False brew_view.event_publishers.publish_event( self.request.event, **self.request.event_extras) try: timeout = self.get_argument("timeout", default=None) delta = timedelta(seconds=int(timeout)) if timeout else None event = brew_view.request_map.get(request_id) yield event.wait(delta) request_model.reload() except TimeoutError: raise TimeoutExceededError("Timeout exceeded for request %s" % request_id) finally: brew_view.request_map.pop(request_id, None) self.set_status(201) self.write( self.parser.serialize_request(request_model, to_string=False))
def test_no_request_command(self, validator): with pytest.raises(ModelValidationError): validator.get_and_validate_command_for_system( Request(system="foo", parameters={}), Mock())
def test_empty(self, validator): req = Request(system="foo", command="command1") command = Command(parameters=[]) assert validator.get_and_validate_parameters(req, command) == {}
def test_clean_fail_bad_output_type(self): request = Request(system="foo", command="bar", output_type="BAD") self.assertRaises(ModelValidationError, request.clean)