123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708 |
- # -*- coding: utf-8 -*-
- from __future__ import absolute_import, print_function, unicode_literals
- import os
- import threading
- import time
- import pytest
- import salt.utils.json
- import salt.utils.stringutils
- from salt.ext import six
- from salt.netapi.rest_tornado import saltnado
- from salt.utils.versions import StrictVersion
- from salt.utils.zeromq import ZMQDefaultLoop as ZMQIOLoop
- from salt.utils.zeromq import zmq
- from tests.support.helpers import flaky, slowTest
- from tests.support.unit import skipIf
- from tests.unit.netapi.test_rest_tornado import SaltnadoTestCase
- HAS_ZMQ_IOLOOP = bool(zmq)
- class _SaltnadoIntegrationTestCase(SaltnadoTestCase): # pylint: disable=abstract-method
- @property
- def opts(self):
- return self.get_config("client_config", from_scratch=True)
- @property
- def mod_opts(self):
- return self.get_config("minion", from_scratch=True)
- @skipIf(HAS_ZMQ_IOLOOP is False, "PyZMQ version must be >= 14.0.1 to run these tests.")
- @skipIf(
- StrictVersion(zmq.__version__) < StrictVersion("14.0.1"),
- "PyZMQ must be >= 14.0.1 to run these tests.",
- )
- @pytest.mark.usefixtures("salt_sub_minion")
- class TestSaltAPIHandler(_SaltnadoIntegrationTestCase):
- def setUp(self):
- super(TestSaltAPIHandler, self).setUp()
- os.environ["ASYNC_TEST_TIMEOUT"] = "300"
- def get_app(self):
- urls = [("/", saltnado.SaltAPIHandler)]
- application = self.build_tornado_app(urls)
- application.event_listener = saltnado.EventListener({}, self.opts)
- self.application = application
- return application
- def test_root(self):
- """
- Test the root path which returns the list of clients we support
- """
- response = self.fetch("/", connect_timeout=30, request_timeout=30,)
- self.assertEqual(response.code, 200)
- response_obj = salt.utils.json.loads(response.body)
- self.assertEqual(
- sorted(response_obj["clients"]),
- ["local", "local_async", "runner", "runner_async"],
- )
- self.assertEqual(response_obj["return"], "Welcome")
- @slowTest
- def test_post_no_auth(self):
- """
- Test post with no auth token, should 401
- """
- # get a token for this test
- low = [{"client": "local", "tgt": "*", "fun": "test.ping"}]
- response = self.fetch(
- "/",
- method="POST",
- body=salt.utils.json.dumps(low),
- headers={"Content-Type": self.content_type_map["json"]},
- follow_redirects=False,
- connect_timeout=30,
- request_timeout=30,
- )
- self.assertEqual(response.code, 302)
- self.assertEqual(response.headers["Location"], "/login")
- # Local client tests
- @skipIf(True, "to be re-enabled when #23623 is merged")
- def test_simple_local_post(self):
- """
- Test a basic API of /
- """
- low = [{"client": "local", "tgt": "*", "fun": "test.ping"}]
- response = self.fetch(
- "/",
- method="POST",
- body=salt.utils.json.dumps(low),
- headers={
- "Content-Type": self.content_type_map["json"],
- saltnado.AUTH_TOKEN_HEADER: self.token["token"],
- },
- connect_timeout=30,
- request_timeout=30,
- )
- response_obj = salt.utils.json.loads(response.body)
- self.assertEqual(len(response_obj["return"]), 1)
- # If --proxy is set, it will cause an extra minion_id to be in the
- # response. Since there's not a great way to know if the test
- # runner's proxy minion is running, and we're not testing proxy
- # minions here anyway, just remove it from the response.
- response_obj["return"][0].pop("proxytest", None)
- self.assertEqual(
- response_obj["return"][0], {"minion": True, "sub_minion": True}
- )
- def test_simple_local_post_no_tgt(self):
- """
- POST job with invalid tgt
- """
- low = [{"client": "local", "tgt": "minion_we_dont_have", "fun": "test.ping"}]
- response = self.fetch(
- "/",
- method="POST",
- body=salt.utils.json.dumps(low),
- headers={
- "Content-Type": self.content_type_map["json"],
- saltnado.AUTH_TOKEN_HEADER: self.token["token"],
- },
- connect_timeout=30,
- request_timeout=30,
- )
- response_obj = salt.utils.json.loads(response.body)
- self.assertEqual(
- response_obj["return"],
- [
- "No minions matched the target. No command was sent, no jid was assigned."
- ],
- )
- # local client request body test
- @skipIf(True, "Undetermined race condition in test. Temporarily disabled.")
- def test_simple_local_post_only_dictionary_request(self):
- """
- Test a basic API of /
- """
- low = {
- "client": "local",
- "tgt": "*",
- "fun": "test.ping",
- }
- response = self.fetch(
- "/",
- method="POST",
- body=salt.utils.json.dumps(low),
- headers={
- "Content-Type": self.content_type_map["json"],
- saltnado.AUTH_TOKEN_HEADER: self.token["token"],
- },
- connect_timeout=30,
- request_timeout=30,
- )
- response_obj = salt.utils.json.loads(response.body)
- self.assertEqual(len(response_obj["return"]), 1)
- # If --proxy is set, it will cause an extra minion_id to be in the
- # response. Since there's not a great way to know if the test
- # runner's proxy minion is running, and we're not testing proxy
- # minions here anyway, just remove it from the response.
- response_obj["return"][0].pop("proxytest", None)
- self.assertEqual(
- response_obj["return"][0], {"minion": True, "sub_minion": True}
- )
- def test_simple_local_post_invalid_request(self):
- """
- Test a basic API of /
- """
- low = ["invalid request"]
- response = self.fetch(
- "/",
- method="POST",
- body=salt.utils.json.dumps(low),
- headers={
- "Content-Type": self.content_type_map["json"],
- saltnado.AUTH_TOKEN_HEADER: self.token["token"],
- },
- connect_timeout=30,
- request_timeout=30,
- )
- self.assertEqual(response.code, 400)
- # local_async tests
- def test_simple_local_async_post(self):
- low = [{"client": "local_async", "tgt": "*", "fun": "test.ping"}]
- response = self.fetch(
- "/",
- method="POST",
- body=salt.utils.json.dumps(low),
- headers={
- "Content-Type": self.content_type_map["json"],
- saltnado.AUTH_TOKEN_HEADER: self.token["token"],
- },
- )
- response_obj = salt.utils.json.loads(response.body)
- ret = response_obj["return"]
- ret[0]["minions"] = sorted(ret[0]["minions"])
- try:
- # If --proxy is set, it will cause an extra minion_id to be in the
- # response. Since there's not a great way to know if the test
- # runner's proxy minion is running, and we're not testing proxy
- # minions here anyway, just remove it from the response.
- ret[0]["minions"].remove("proxytest")
- except ValueError:
- pass
- # TODO: verify pub function? Maybe look at how we test the publisher
- self.assertEqual(len(ret), 1)
- self.assertIn("jid", ret[0])
- self.assertEqual(ret[0]["minions"], sorted(["minion", "sub_minion"]))
- def test_multi_local_async_post(self):
- low = [
- {"client": "local_async", "tgt": "*", "fun": "test.ping"},
- {"client": "local_async", "tgt": "*", "fun": "test.ping"},
- ]
- response = self.fetch(
- "/",
- method="POST",
- body=salt.utils.json.dumps(low),
- headers={
- "Content-Type": self.content_type_map["json"],
- saltnado.AUTH_TOKEN_HEADER: self.token["token"],
- },
- )
- response_obj = salt.utils.json.loads(response.body)
- ret = response_obj["return"]
- ret[0]["minions"] = sorted(ret[0]["minions"])
- ret[1]["minions"] = sorted(ret[1]["minions"])
- try:
- # If --proxy is set, it will cause an extra minion_id to be in the
- # response. Since there's not a great way to know if the test
- # runner's proxy minion is running, and we're not testing proxy
- # minions here anyway, just remove it from the response.
- ret[0]["minions"].remove("proxytest")
- ret[1]["minions"].remove("proxytest")
- except ValueError:
- pass
- self.assertEqual(len(ret), 2)
- self.assertIn("jid", ret[0])
- self.assertIn("jid", ret[1])
- self.assertEqual(ret[0]["minions"], sorted(["minion", "sub_minion"]))
- self.assertEqual(ret[1]["minions"], sorted(["minion", "sub_minion"]))
- @slowTest
- def test_multi_local_async_post_multitoken(self):
- low = [
- {"client": "local_async", "tgt": "*", "fun": "test.ping"},
- {
- "client": "local_async",
- "tgt": "*",
- "fun": "test.ping",
- "token": self.token[
- "token"
- ], # send a different (but still valid token)
- },
- {
- "client": "local_async",
- "tgt": "*",
- "fun": "test.ping",
- "token": "BAD_TOKEN", # send a bad token
- },
- ]
- response = self.fetch(
- "/",
- method="POST",
- body=salt.utils.json.dumps(low),
- headers={
- "Content-Type": self.content_type_map["json"],
- saltnado.AUTH_TOKEN_HEADER: self.token["token"],
- },
- )
- response_obj = salt.utils.json.loads(response.body)
- ret = response_obj["return"]
- ret[0]["minions"] = sorted(ret[0]["minions"])
- ret[1]["minions"] = sorted(ret[1]["minions"])
- try:
- # If --proxy is set, it will cause an extra minion_id to be in the
- # response. Since there's not a great way to know if the test
- # runner's proxy minion is running, and we're not testing proxy
- # minions here anyway, just remove it from the response.
- ret[0]["minions"].remove("proxytest")
- ret[1]["minions"].remove("proxytest")
- except ValueError:
- pass
- self.assertEqual(len(ret), 3) # make sure we got 3 responses
- self.assertIn("jid", ret[0]) # the first 2 are regular returns
- self.assertIn("jid", ret[1])
- self.assertIn("Failed to authenticate", ret[2]) # bad auth
- self.assertEqual(ret[0]["minions"], sorted(["minion", "sub_minion"]))
- self.assertEqual(ret[1]["minions"], sorted(["minion", "sub_minion"]))
- @slowTest
- def test_simple_local_async_post_no_tgt(self):
- low = [
- {"client": "local_async", "tgt": "minion_we_dont_have", "fun": "test.ping"}
- ]
- response = self.fetch(
- "/",
- method="POST",
- body=salt.utils.json.dumps(low),
- headers={
- "Content-Type": self.content_type_map["json"],
- saltnado.AUTH_TOKEN_HEADER: self.token["token"],
- },
- )
- response_obj = salt.utils.json.loads(response.body)
- self.assertEqual(response_obj["return"], [{}])
- @skipIf(True, "Undetermined race condition in test. Temporarily disabled.")
- def test_simple_local_post_only_dictionary_request_with_order_masters(self):
- """
- Test a basic API of /
- """
- low = {
- "client": "local",
- "tgt": "*",
- "fun": "test.ping",
- }
- self.application.opts["order_masters"] = True
- self.application.opts["syndic_wait"] = 5
- response = self.fetch(
- "/",
- method="POST",
- body=salt.utils.json.dumps(low),
- headers={
- "Content-Type": self.content_type_map["json"],
- saltnado.AUTH_TOKEN_HEADER: self.token["token"],
- },
- connect_timeout=30,
- request_timeout=30,
- )
- response_obj = salt.utils.json.loads(response.body)
- self.application.opts["order_masters"] = []
- self.application.opts["syndic_wait"] = 5
- # If --proxy is set, it will cause an extra minion_id to be in the
- # response. Since there's not a great way to know if the test runner's
- # proxy minion is running, and we're not testing proxy minions here
- # anyway, just remove it from the response.
- response_obj[0]["return"].pop("proxytest", None)
- self.assertEqual(response_obj["return"], [{"minion": True, "sub_minion": True}])
- # runner tests
- @slowTest
- def test_simple_local_runner_post(self):
- low = [{"client": "runner", "fun": "manage.up"}]
- response = self.fetch(
- "/",
- method="POST",
- body=salt.utils.json.dumps(low),
- headers={
- "Content-Type": self.content_type_map["json"],
- saltnado.AUTH_TOKEN_HEADER: self.token["token"],
- },
- connect_timeout=30,
- request_timeout=300,
- )
- response_obj = salt.utils.json.loads(response.body)
- self.assertEqual(len(response_obj["return"]), 1)
- try:
- # If --proxy is set, it will cause an extra minion_id to be in the
- # response. Since there's not a great way to know if the test
- # runner's proxy minion is running, and we're not testing proxy
- # minions here anyway, just remove it from the response.
- response_obj["return"][0].remove("proxytest")
- except ValueError:
- pass
- self.assertEqual(
- sorted(response_obj["return"][0]), sorted(["minion", "sub_minion"])
- )
- # runner_async tests
- def test_simple_local_runner_async_post(self):
- low = [{"client": "runner_async", "fun": "manage.up"}]
- response = self.fetch(
- "/",
- method="POST",
- body=salt.utils.json.dumps(low),
- headers={
- "Content-Type": self.content_type_map["json"],
- saltnado.AUTH_TOKEN_HEADER: self.token["token"],
- },
- connect_timeout=10,
- request_timeout=10,
- )
- response_obj = salt.utils.json.loads(response.body)
- self.assertIn("return", response_obj)
- self.assertEqual(1, len(response_obj["return"]))
- self.assertIn("jid", response_obj["return"][0])
- self.assertIn("tag", response_obj["return"][0])
- @flaky
- @skipIf(HAS_ZMQ_IOLOOP is False, "PyZMQ version must be >= 14.0.1 to run these tests.")
- class TestMinionSaltAPIHandler(_SaltnadoIntegrationTestCase):
- def get_app(self):
- urls = [
- (r"/minions/(.*)", saltnado.MinionSaltAPIHandler),
- (r"/minions", saltnado.MinionSaltAPIHandler),
- ]
- application = self.build_tornado_app(urls)
- application.event_listener = saltnado.EventListener({}, self.opts)
- return application
- @skipIf(True, "issue #34753")
- def test_get_no_mid(self):
- response = self.fetch(
- "/minions",
- method="GET",
- headers={saltnado.AUTH_TOKEN_HEADER: self.token["token"]},
- follow_redirects=False,
- )
- response_obj = salt.utils.json.loads(response.body)
- self.assertEqual(len(response_obj["return"]), 1)
- # one per minion
- self.assertEqual(len(response_obj["return"][0]), 2)
- # check a single grain
- for minion_id, grains in six.iteritems(response_obj["return"][0]):
- self.assertEqual(minion_id, grains["id"])
- @skipIf(True, "to be re-enabled when #23623 is merged")
- @slowTest
- def test_get(self):
- response = self.fetch(
- "/minions/minion",
- method="GET",
- headers={saltnado.AUTH_TOKEN_HEADER: self.token["token"]},
- follow_redirects=False,
- )
- response_obj = salt.utils.json.loads(response.body)
- self.assertEqual(len(response_obj["return"]), 1)
- self.assertEqual(len(response_obj["return"][0]), 1)
- # check a single grain
- self.assertEqual(response_obj["return"][0]["minion"]["id"], "minion")
- def test_post(self):
- low = [{"tgt": "*minion", "fun": "test.ping"}]
- response = self.fetch(
- "/minions",
- method="POST",
- body=salt.utils.json.dumps(low),
- headers={
- "Content-Type": self.content_type_map["json"],
- saltnado.AUTH_TOKEN_HEADER: self.token["token"],
- },
- )
- response_obj = salt.utils.json.loads(response.body)
- ret = response_obj["return"]
- ret[0]["minions"] = sorted(ret[0]["minions"])
- # TODO: verify pub function? Maybe look at how we test the publisher
- self.assertEqual(len(ret), 1)
- self.assertIn("jid", ret[0])
- self.assertEqual(ret[0]["minions"], sorted(["minion", "sub_minion"]))
- @slowTest
- def test_post_with_client(self):
- # get a token for this test
- low = [{"client": "local_async", "tgt": "*minion", "fun": "test.ping"}]
- response = self.fetch(
- "/minions",
- method="POST",
- body=salt.utils.json.dumps(low),
- headers={
- "Content-Type": self.content_type_map["json"],
- saltnado.AUTH_TOKEN_HEADER: self.token["token"],
- },
- )
- response_obj = salt.utils.json.loads(response.body)
- ret = response_obj["return"]
- ret[0]["minions"] = sorted(ret[0]["minions"])
- # TODO: verify pub function? Maybe look at how we test the publisher
- self.assertEqual(len(ret), 1)
- self.assertIn("jid", ret[0])
- self.assertEqual(ret[0]["minions"], sorted(["minion", "sub_minion"]))
- @slowTest
- def test_post_with_incorrect_client(self):
- """
- The /minions endpoint is asynchronous only, so if you try something else
- make sure you get an error
- """
- # get a token for this test
- low = [{"client": "local", "tgt": "*", "fun": "test.ping"}]
- response = self.fetch(
- "/minions",
- method="POST",
- body=salt.utils.json.dumps(low),
- headers={
- "Content-Type": self.content_type_map["json"],
- saltnado.AUTH_TOKEN_HEADER: self.token["token"],
- },
- )
- self.assertEqual(response.code, 400)
- @skipIf(HAS_ZMQ_IOLOOP is False, "PyZMQ version must be >= 14.0.1 to run these tests.")
- class TestJobsSaltAPIHandler(_SaltnadoIntegrationTestCase):
- def get_app(self):
- urls = [
- (r"/jobs/(.*)", saltnado.JobsSaltAPIHandler),
- (r"/jobs", saltnado.JobsSaltAPIHandler),
- ]
- application = self.build_tornado_app(urls)
- application.event_listener = saltnado.EventListener({}, self.opts)
- return application
- @skipIf(True, "to be re-enabled when #23623 is merged")
- @slowTest
- def test_get(self):
- # test with no JID
- self.http_client.fetch(
- self.get_url("/jobs"),
- self.stop,
- method="GET",
- headers={saltnado.AUTH_TOKEN_HEADER: self.token["token"]},
- follow_redirects=False,
- )
- response = self.wait(timeout=30)
- response_obj = salt.utils.json.loads(response.body)["return"][0]
- try:
- for jid, ret in six.iteritems(response_obj):
- self.assertIn("Function", ret)
- self.assertIn("Target", ret)
- self.assertIn("Target-type", ret)
- self.assertIn("User", ret)
- self.assertIn("StartTime", ret)
- self.assertIn("Arguments", ret)
- except AttributeError as attribute_error:
- print(salt.utils.json.loads(response.body))
- raise
- # test with a specific JID passed in
- jid = next(six.iterkeys(response_obj))
- self.http_client.fetch(
- self.get_url("/jobs/{0}".format(jid)),
- self.stop,
- method="GET",
- headers={saltnado.AUTH_TOKEN_HEADER: self.token["token"]},
- follow_redirects=False,
- )
- response = self.wait(timeout=30)
- response_obj = salt.utils.json.loads(response.body)["return"][0]
- self.assertIn("Function", response_obj)
- self.assertIn("Target", response_obj)
- self.assertIn("Target-type", response_obj)
- self.assertIn("User", response_obj)
- self.assertIn("StartTime", response_obj)
- self.assertIn("Arguments", response_obj)
- self.assertIn("Result", response_obj)
- # TODO: run all the same tests from the root handler, but for now since they are
- # the same code, we'll just sanity check
- @skipIf(HAS_ZMQ_IOLOOP is False, "PyZMQ version must be >= 14.0.1 to run these tests.")
- class TestRunSaltAPIHandler(_SaltnadoIntegrationTestCase):
- def get_app(self):
- urls = [
- ("/run", saltnado.RunSaltAPIHandler),
- ]
- application = self.build_tornado_app(urls)
- application.event_listener = saltnado.EventListener({}, self.opts)
- return application
- @skipIf(True, "to be re-enabled when #23623 is merged")
- @slowTest
- def test_get(self):
- low = [{"client": "local", "tgt": "*", "fun": "test.ping"}]
- response = self.fetch(
- "/run",
- method="POST",
- body=salt.utils.json.dumps(low),
- headers={
- "Content-Type": self.content_type_map["json"],
- saltnado.AUTH_TOKEN_HEADER: self.token["token"],
- },
- )
- response_obj = salt.utils.json.loads(response.body)
- self.assertEqual(response_obj["return"], [{"minion": True, "sub_minion": True}])
- @skipIf(HAS_ZMQ_IOLOOP is False, "PyZMQ version must be >= 14.0.1 to run these tests.")
- class TestEventsSaltAPIHandler(_SaltnadoIntegrationTestCase):
- def get_app(self):
- urls = [
- (r"/events", saltnado.EventsSaltAPIHandler),
- ]
- application = self.build_tornado_app(urls)
- application.event_listener = saltnado.EventListener({}, self.opts)
- # store a reference, for magic later!
- self.application = application
- self.events_to_fire = 0
- return application
- @slowTest
- def test_get(self):
- self.events_to_fire = 5
- response = self.fetch(
- "/events",
- headers={saltnado.AUTH_TOKEN_HEADER: self.token["token"]},
- streaming_callback=self.on_event,
- )
- def _stop(self):
- self.stop()
- def on_event(self, event):
- if six.PY3:
- event = event.decode("utf-8")
- if self.events_to_fire > 0:
- self.application.event_listener.event.fire_event(
- {"foo": "bar", "baz": "qux"}, "salt/netapi/test"
- )
- self.events_to_fire -= 1
- # once we've fired all the events, lets call it a day
- else:
- # wait so that we can ensure that the next future is ready to go
- # to make sure we don't explode if the next one is ready
- ZMQIOLoop.current().add_timeout(time.time() + 0.5, self._stop)
- event = event.strip()
- # if we got a retry, just continue
- if event != "retry: 400":
- tag, data = event.splitlines()
- self.assertTrue(tag.startswith("tag: "))
- self.assertTrue(data.startswith("data: "))
- @skipIf(HAS_ZMQ_IOLOOP is False, "PyZMQ version must be >= 14.0.1 to run these tests.")
- class TestWebhookSaltAPIHandler(_SaltnadoIntegrationTestCase):
- def get_app(self):
- urls = [
- (r"/hook(/.*)?", saltnado.WebhookSaltAPIHandler),
- ]
- application = self.build_tornado_app(urls)
- self.application = application
- application.event_listener = saltnado.EventListener({}, self.opts)
- return application
- @skipIf(True, "Skipping until we can devote more resources to debugging this test.")
- def test_post(self):
- self._future_resolved = threading.Event()
- try:
- def verify_event(future):
- """
- Notify the threading event that the future is resolved
- """
- self._future_resolved.set()
- self._finished = (
- False # TODO: remove after some cleanup of the event listener
- )
- # get an event future
- future = self.application.event_listener.get_event(
- self, tag="salt/netapi/hook", callback=verify_event
- )
- # fire the event
- response = self.fetch(
- "/hook",
- method="POST",
- body="foo=bar",
- headers={saltnado.AUTH_TOKEN_HEADER: self.token["token"]},
- )
- response_obj = salt.utils.json.loads(response.body)
- self.assertTrue(response_obj["success"])
- resolve_future_timeout = 60
- self._future_resolved.wait(resolve_future_timeout)
- try:
- event = future.result()
- except Exception as exc: # pylint: disable=broad-except
- self.fail(
- "Failed to resolve future under {} secs: {}".format(
- resolve_future_timeout, exc
- )
- )
- self.assertEqual(event["tag"], "salt/netapi/hook")
- self.assertIn("headers", event["data"])
- self.assertEqual(
- event["data"]["post"], {"foo": salt.utils.stringutils.to_bytes("bar")}
- )
- finally:
- self._future_resolved.clear()
- del self._future_resolved
|