def _launch_nested_container_session(self): """Sends a request to the Mesos Agent to launch a new nested container and attach to its output stream. The output stream is then sent back in the response. """ message = { 'type': "LAUNCH_NESTED_CONTAINER_SESSION", 'launch_nested_container_session': { 'container_id': { 'parent': { 'value': self.parent_id }, 'value': self.container_id }, 'command': { 'value': self.cmd, 'arguments': [self.cmd] + self.args, 'shell': False}}} # If we have to launch in a task group, we need double nesting if self.parent_container_id is not None: message['launch_nested_container_session']['container_id']['parent']['parent'] = { 'value': self.parent_container_id } if self.env is not None: env_vars = [] env_var_regex = re.compile('^([A-Z_][A-Z0-9_]*)=(.*)$') for env_var in self.env.split(':'): matches = env_var_regex.match(env_var) if matches and len(matches.groups()) == 2: env_vars.append({ 'name': matches.group(1), 'type': 'VALUE', 'value': matches.group(2) }) message['launch_nested_container_session']['command']['environment'] = { 'variables': env_vars } if not self.user is None: message['launch_nested_container_session']['command']['user'] =\ self.user if self.tty: message[ 'launch_nested_container_session'][ 'container'] = { 'type': 'MESOS', 'tty_info': {}} req_extra_args = { 'stream': True, 'headers': { 'Content-Type': 'application/json', 'Accept': 'application/recordio', 'Message-Accept': 'application/json'}} response = http.post( self.agent_url, data=json.dumps(message), timeout=None, **req_extra_args) self._process_output_stream(response)
def _launch_nested_container_session(self): """Sends a request to the Mesos Agent to launch a new nested container and attach to its output stream. The output stream is then sent back in the response. """ message = { 'type': "LAUNCH_NESTED_CONTAINER_SESSION", 'launch_nested_container_session': { 'container_id': { 'parent': { 'value': self.parent_id }, 'value': self.container_id }, 'command': { 'value': self.cmd, 'arguments': [self.cmd] + self.args, 'shell': False } } } if not self.user is None: message['launch_nested_container_session']['command']['user'] =\ self.user if self.tty: message['launch_nested_container_session']['container'] = { 'type': 'MESOS', 'tty_info': {} } req_extra_args = { 'stream': True, 'headers': { 'Content-Type': 'application/json', 'Accept': 'application/recordio', 'Message-Accept': 'application/json' } } response = http.post(self.agent_url, data=json.dumps(message), timeout=None, **req_extra_args) self._process_output_stream(response)
def _attach_container_input(self): """Streams all input data (e.g. STDIN) from the client to the agent """ def _initial_input_streamer(): """Generator function yielding the initial ATTACH_CONTAINER_INPUT message for streaming. We have a separate generator for this so that we can attempt the connection once before committing to a persistent connection where we stream the rest of the input. :returns: A RecordIO encoded message """ message = { 'type': 'ATTACH_CONTAINER_INPUT', 'attach_container_input': { 'type': 'CONTAINER_ID', 'container_id': { 'parent': { 'value': self.parent_id }, 'value': self.container_id } } } yield self.encoder.encode(message) def _input_streamer(): """Generator function yielding ATTACH_CONTAINER_INPUT messages for streaming. It yields the _intitial_input_streamer() message, followed by messages from the input_queue on each subsequent call. :returns: A RecordIO encoded message """ yield next(_initial_input_streamer()) while True: record = self.input_queue.get() if not record: break yield record req_extra_args = { 'headers': { 'Content-Type': 'application/recordio', 'Message-Content-Type': 'application/json', 'Accept': 'application/json', 'Connection': 'close', 'Transfer-Encoding': 'chunked' } } # Ensure we don't try to attach our input to a container that isn't # fully up and running by waiting until the # `_process_output_stream` function signals us that it's ready. self.attach_input_event.wait() # Send an intial "Test" message to ensure that we are able to # establish a connection with the agent. If we aren't we will throw # an exception and break out of this thread. However, in cases where # we receive a 500 response from the agent, we actually want to # continue without throwing an exception. A 500 error indicates that # we can't connect to the container because it has already finished # running. In that case we continue running to allow the output queue # to be flushed. try: http.post(self.agent_url, data=_initial_input_streamer(), **req_extra_args) except MesosHTTPException as e: if not e.response.status_code == 500: raise e # If we succeeded with that connection, unblock process_output_stream() # from sending output data to the output thread. self.print_output_event.set() # Begin streaming the input. http.post(self.agent_url, data=_input_streamer(), timeout=None, **req_extra_args)