def setUp(self):
        from unittest import SkipTest
        raise SkipTest("Process dispatcher currently not supported")

        self._start_container()
        self.container.start_rel_from_url('res/deploy/basic.yml')

        self.pd_cli = ProcessDispatcherServiceClient()

        self.process_definition = IonObject(OT.ProcessDefinition, name='test_process')
        self.process_definition.executable = {'module': 'ion.services.test.test_process_state_gate',
                                              'class': 'TestProcess'}
        self.process_definition_id = self.pd_cli.create_process_definition(self.process_definition)
        self.event_queue = queue.Queue()

        self.process_schedule = IonObject(OT.ProcessSchedule)
        self.process_schedule.queueing_mode = ProcessQueueingMode.ALWAYS

        self.pid = self.pd_cli.create_process(self.process_definition_id)

        self.event_queue = queue.Queue()

        self.event_sub = EventSubscriber(event_type="ProcessLifecycleEvent",
                                         callback=self._event_callback,
                                         origin=self.pid,
                                         origin_type="DispatchedProcess")
    def setUp(self):
        raise SkipTest("Process dispatcher currently not supported")
        self._start_container()
        self.container.start_rel_from_url('res/deploy/basic.yml')

        self.rr_cli = ResourceRegistryServiceClient()
        self.pd_cli = ProcessDispatcherServiceClient()


        self.process_definition = ProcessDefinition(name='test_process')
        self.process_definition.executable = {'module': 'ion.services.test.test_process_dispatcher',
                                              'class': 'TestProcess'}
        self.process_definition_id = self.pd_cli.create_process_definition(self.process_definition)

        self.waiter = ProcessStateWaiter()
class ProcessStateGateIntTest(IonIntegrationTestCase):

    def setUp(self):
        from unittest import SkipTest
        raise SkipTest("Process dispatcher currently not supported")

        self._start_container()
        self.container.start_rel_from_url('res/deploy/basic.yml')

        self.pd_cli = ProcessDispatcherServiceClient()

        self.process_definition = IonObject(OT.ProcessDefinition, name='test_process')
        self.process_definition.executable = {'module': 'ion.services.test.test_process_state_gate',
                                              'class': 'TestProcess'}
        self.process_definition_id = self.pd_cli.create_process_definition(self.process_definition)
        self.event_queue = queue.Queue()

        self.process_schedule = IonObject(OT.ProcessSchedule)
        self.process_schedule.queueing_mode = ProcessQueueingMode.ALWAYS

        self.pid = self.pd_cli.create_process(self.process_definition_id)

        self.event_queue = queue.Queue()

        self.event_sub = EventSubscriber(event_type="ProcessLifecycleEvent",
                                         callback=self._event_callback,
                                         origin=self.pid,
                                         origin_type="DispatchedProcess")

    def tearDown(self):
        #stop subscriber if its running
        if self.event_sub and self.event_sub._cbthread:
            self.event_sub.stop()
        self._stop_container()


    def _event_callback(self, event, *args, **kwargs):
        self.event_queue.put(event)


    def latest_event(self, timeout=10):
        # get latest event from our local event subscriber
        try:
            event = self.event_queue.get(timeout=timeout)
        except Empty:
            event = None
        return event


    def await_state(self, state, timeout=10):

        print "Emptying event queue"
        while True:
            event = self.latest_event(0)
            if event:
                print "State %s from event %s" % (event.state, event)
            else:
                break
        self.event_sub.start()

        #wait for process state
        print "Setting up %s gate" % ProcessStateEnum._str_map[state]
        gate = ProcessStateGate(self.pd_cli.read_process,
                                self.pid,
                                state)
        print "Waiting"
        ret = gate.await(timeout)

        print "Await got %s" % ret
        event = self.latest_event(timeout=1)

        # check false positives/negatives
        if ret and gate._get_first_chance() is None and event is None:
            self.fail("ProcessStateGate got an event that EventSubscriber didnt....")

        self.event_sub.stop()

        if (not ret) or gate._get_last_chance():
            if event and event.state == state:
                self.fail("EventSubscriber got state event %s for process %s, ProcessStateGate missed it" %
                          (ProcessStateEnum._str_map[event.state], self.pid))


        return ret

    def process_start(self):
        print "Scheduling process...",
        self.pd_cli.schedule_process(self.process_definition_id,
                                     self.process_schedule,
                                     configuration={},
                                     process_id=self.pid)
        print "Done scheduling process."


    def process_stop(self):
        print "STOPPING process...",
        self.pd_cli.cancel_process(self.pid)
        print "Done stopping process"


    def test_process_state_gate(self):

        self.assertFalse(self.await_state(ProcessStateEnum.RUNNING, 1),
                         "The process was reported as spawned, but we didn't yet")

        print "GOING TO ACTUALLY START PROCESS NOW"
        spawn_later(1, self.process_start)

        self.assertTrue(self.await_state(ProcessStateEnum.RUNNING),
                        "The process did not spawn")

        self.assertFalse(self.await_state(ProcessStateEnum.TERMINATED, 1),
                        "The process claims to have terminated, but we didn't kill it")


        print "communicating with the process to make sure it is really running"
        test_client = TestClient()
        for i in range(5):
            self.assertEqual(i + 1, test_client.count(timeout=10))

        spawn_later(1, self.process_stop)

        self.assertTrue(self.await_state(ProcessStateEnum.TERMINATED),
                        "The process failed to be reported as terminated when it was terminated")

        self.assertFalse(self.await_state(ProcessStateEnum.RUNNING, 1),
                         "The process was reported as spawned, but we killed it")
class ProcessDispatcherServiceIntTest(IonIntegrationTestCase):

    def setUp(self):
        raise SkipTest("Process dispatcher currently not supported")
        self._start_container()
        self.container.start_rel_from_url('res/deploy/basic.yml')

        self.rr_cli = ResourceRegistryServiceClient()
        self.pd_cli = ProcessDispatcherServiceClient()


        self.process_definition = ProcessDefinition(name='test_process')
        self.process_definition.executable = {'module': 'ion.services.test.test_process_dispatcher',
                                              'class': 'TestProcess'}
        self.process_definition_id = self.pd_cli.create_process_definition(self.process_definition)

        self.waiter = ProcessStateWaiter()

    def tearDown(self):
        self.waiter.stop()

    def test_create_schedule_cancel(self):
        process_schedule = ProcessSchedule()
        process_schedule.queueing_mode = ProcessQueueingMode.ALWAYS

        proc_name = 'myreallygoodname'
        pid = self.pd_cli.create_process(self.process_definition_id)
        self.waiter.start(pid)

        pid2 = self.pd_cli.schedule_process(self.process_definition_id,
            process_schedule, configuration={}, process_id=pid, name=proc_name)
        self.assertEqual(pid, pid2)

        # verifies L4-CI-CEI-RQ141 and L4-CI-CEI-RQ142
        self.waiter.await_state_event(pid, ProcessStateEnum.RUNNING)

        proc = self.pd_cli.read_process(pid)
        self.assertEqual(proc.process_id, pid)
        self.assertEqual(proc.process_configuration, {})
        self.assertEqual(proc.process_state, ProcessStateEnum.RUNNING)

        # make sure process is readable directly from RR (mirrored)
        # verifies L4-CI-CEI-RQ63
        # verifies L4-CI-CEI-RQ64
        proc = self.rr_cli.read(pid)
        self.assertEqual(proc.process_id, pid)

        # now try communicating with the process to make sure it is really running
        test_client = TestClient()
        for i in range(5):
            self.assertEqual(i + 1, test_client.count(timeout=10))

        # verifies L4-CI-CEI-RQ147

        # check the process name was set in container
        got_proc_name = test_client.get_process_name(pid=pid2)
        self.assertEqual(proc_name, got_proc_name)

        # kill the process and start it again
        self.pd_cli.cancel_process(pid)

        self.waiter.await_state_event(pid, ProcessStateEnum.TERMINATED)

        pid2 = self.pd_cli.schedule_process(self.process_definition_id,
            process_schedule, configuration={}, process_id=pid)
        self.assertEqual(pid, pid2)

        self.waiter.await_state_event(pid, ProcessStateEnum.RUNNING)

        for i in range(5):
            self.assertEqual(i + 1, test_client.count(timeout=10))

        # kill the process for good
        self.pd_cli.cancel_process(pid)
        self.waiter.await_state_event(pid, ProcessStateEnum.TERMINATED)

    def test_schedule_with_config(self):

        process_schedule = ProcessSchedule()
        process_schedule.queueing_mode = ProcessQueueingMode.ALWAYS

        pid = self.pd_cli.create_process(self.process_definition_id)
        self.waiter.start(pid)

        # verifies L4-CI-CEI-RQ66

        # feed in a string that the process will return -- verifies that
        # configuration actually makes it to the instantiated process
        test_response = uuid.uuid4().hex
        configuration = {"test_response": test_response}

        pid2 = self.pd_cli.schedule_process(self.process_definition_id,
            process_schedule, configuration=configuration, process_id=pid)
        self.assertEqual(pid, pid2)

        self.waiter.await_state_event(pid, ProcessStateEnum.RUNNING)

        test_client = TestClient()

        # verifies L4-CI-CEI-RQ139
        # assure that configuration block (which can contain inputs, outputs,
        # and arbitrary config) 1) makes it to the process and 2) is returned
        # in process queries

        self.assertEqual(test_client.query(), test_response)

        proc = self.pd_cli.read_process(pid)
        self.assertEqual(proc.process_id, pid)
        self.assertEqual(proc.process_configuration, configuration)

        # kill the process for good
        self.pd_cli.cancel_process(pid)
        self.waiter.await_state_event(pid, ProcessStateEnum.TERMINATED)

    def test_schedule_bad_config(self):

        process_schedule = ProcessSchedule()

        # a non-JSON-serializable IonObject
        o = ProcessTarget()

        with self.assertRaises(BadRequest) as ar:
            self.pd_cli.schedule_process(self.process_definition_id,
                process_schedule, configuration={"bad": o})
        self.assertTrue(ar.exception.message.startswith("bad configuration"))

    def test_cancel_notfound(self):
        with self.assertRaises(NotFound):
            self.pd_cli.cancel_process("not-a-real-process-id")

    def test_create_invalid_definition(self):
        # create process definition missing module and class
        # verifies L4-CI-CEI-RQ137
        executable = dict(url="http://somewhere.com/something.py")
        definition = ProcessDefinition(name="test_process", executable=executable)
        with self.assertRaises(BadRequest):
            self.pd_cli.create_process_definition(definition)