示例#1
0
    def start(self):
        runtime_config = self._runtime_config_getter()

        # api_host set to 'localhost' won't be accessible from a docker container
        # because container will have it's own 'localhost'.
        # TODO: this works only when /etc/hosts is configured properly.
        api_host = socket.gethostbyname(socket.gethostname()) if (
            runtime_config.api_host == '0.0.0.0') else runtime_config.api_host

        # Must be HTTP_PORT from apphosting/ext/vmruntime/vmservice.py
        # TODO: update apphosting/ext/vmruntime/vmservice.py to use
        # env var set here.
        PORT = 8080

        self._container = containers.Container(
            self._docker_client,
            containers.ContainerOptions(
                image_opts=containers.ImageOptions(
                    dockerfile_dir=self._module_configuration.application_root,
                    tag='vm.%(RUNTIME)s.%(APP_ID)s.%(MODULE)s.%(VERSION)s' % {
                        'APP_ID': self._module_configuration.application,
                        'MODULE': self._module_configuration.module_name,
                        'RUNTIME':
                        self._module_configuration.effective_runtime,
                        'VERSION': self._module_configuration.major_version
                    },
                    nocache=False),
                port=PORT,
                environment={
                    'API_HOST': api_host,
                    'API_PORT': runtime_config.api_port,
                    'GAE_LONG_APP_ID':
                    self._module_configuration.application_external_name,
                    'GAE_PARTITION': self._module_configuration.partition,
                    'GAE_MODULE_NAME': self._module_configuration.module_name,
                    'GAE_MODULE_VERSION':
                    self._module_configuration.major_version,
                    'GAE_MINOR_VERSION':
                    self._module_configuration.minor_version,
                    'GAE_MODULE_INSTANCE': runtime_config.instance_id
                },
                volumes={
                    '/var/log/app_engine/app': {
                        'bind': '/var/log/app_engine/app'
                    }
                },
                volumes_from=None))

        self._container.Start()

        self._proxy = http_proxy.HttpProxy(
            host=self._container.host,
            port=self._container.port,
            instance_died_unexpectedly=self._instance_died_unexpectedly,
            instance_logs_getter=self._get_instance_logs,
            error_handler_file=application_configuration.get_app_error_file(
                self._module_configuration))
  def start(self):
    """Starts the runtime process and waits until it is ready to serve."""
    runtime_config = self._runtime_config_getter()
    # TODO: Use a different process group to isolate the child process
    # from signals sent to the parent. Only available in subprocess in
    # Python 2.7.
    assert self._start_process_flavor in self._VALID_START_PROCESS_FLAVORS
    if self._start_process_flavor == START_PROCESS:
      serialized_config = base64.b64encode(runtime_config.SerializeToString())
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        self._process = safe_subprocess.start_process(
            self._args,
            serialized_config,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            env=self._env,
            cwd=self._module_configuration.application_root)
      line = self._process.stdout.readline()
    elif self._start_process_flavor == START_PROCESS_FILE:
      serialized_config = runtime_config.SerializeToString()
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        self._process = safe_subprocess.start_process_file(
            args=self._args,
            input_string=serialized_config,
            env=self._env,
            cwd=self._module_configuration.application_root,
            stderr=subprocess.PIPE)
      line = self._read_start_process_file()
      _remove_retry_sharing_violation(self._process.child_out.name)

    # _stderr_tee may be pre-set by unit tests.
    if self._stderr_tee is None:
      self._stderr_tee = tee.Tee(self._process.stderr, sys.stderr)
      self._stderr_tee.start()

    port = None
    error = None
    try:
      port = int(line)
    except ValueError:
      error = 'bad runtime process port [%r]' % line
      logging.error(error)
    finally:
      self._proxy = http_proxy.HttpProxy(
          host='localhost', port=port,
          instance_died_unexpectedly=self._instance_died_unexpectedly,
          instance_logs_getter=self._get_instance_logs,
          error_handler_file=application_configuration.get_app_error_file(
              self._module_configuration),
          prior_error=error)
      self._proxy.wait_for_connection()
示例#3
0
  def start(self):
    runtime_config = self._runtime_config_getter()

    # api_host set to 'localhost' won't be accessible from a docker container
    # because container will have it's own 'localhost'.
    # TODO: this works only when /etc/hosts is configured properly.
    api_host = socket.gethostbyname(socket.gethostname()) if (
        runtime_config.api_host == '0.0.0.0') else runtime_config.api_host

    image_id, _ = self._docker_client.build(
        path=self._module_configuration.application_root,
        tag='vme.python.%(APP_ID)s.%(MODULE)s.%(VERSION)s' % {
            'APP_ID': self._module_configuration.application,
            'MODULE': self._module_configuration.module_name,
            'VERSION': self._module_configuration.version_id},
        quiet=False, fileobj=None, nocache=False, rm=False, stream=False)

    # Must be HTTP_PORT from apphosting/ext/vmruntime/vmservice.py
    # TODO: update apphosting/ext/vmruntime/vmservice.py to use
    # env var set here.
    PORT = 8080

    self._container_id = self._docker_client.create_container(
        image=image_id, hostname=None, user=None, detach=True, stdin_open=False,
        tty=False, mem_limit=0,
        ports=[PORT],
        # TODO: set environment variable for MetadataServer
        environment={'API_HOST': api_host,
                     'API_PORT': runtime_config.api_port},
        dns=None,
        network_disabled=False, name=None)

    logging.info('Container %s created' % self._container_id)

    self._docker_client.start(
        self._container_id,
        # Assigns random available docker port
        port_bindings={PORT: None})

    logging.info('Container %s started' % self._container_id)

    container_info = self._docker_client.inspect_container(self._container_id)
    port = int(
        container_info['NetworkSettings']['Ports']['8080/tcp'][0]['HostPort'])

    self._proxy = http_proxy.HttpProxy(
        host='localhost', port=port,
        instance_died_unexpectedly=self._instance_died_unexpectedly,
        instance_logs_getter=self._get_instance_logs,
        error_handler_file=application_configuration.get_app_error_file(
            self._module_configuration))
  def start(self):
    runtime_config = self._runtime_config_getter()

    # api_host set to 'localhost' won't be accessible from a docker container
    # because container will have it's own 'localhost'.
    # TODO: this works only when /etc/hosts is configured properly.
    api_host = socket.gethostbyname(socket.gethostname()) if (
        runtime_config.api_host == '0.0.0.0') else runtime_config.api_host

    # Must be HTTP_PORT from apphosting/ext/vmruntime/vmservice.py
    # TODO: update apphosting/ext/vmruntime/vmservice.py to use
    # env var set here.
    PORT = 8080

    self._container = containers.Container(
        self._docker_client,
        containers.ContainerOptions(
            image_opts=containers.ImageOptions(
                dockerfile_dir=self._module_configuration.application_root,
                tag='vm.%(RUNTIME)s.%(APP_ID)s.%(MODULE)s.%(VERSION)s' % {
                    'APP_ID': self._module_configuration.application,
                    'MODULE': self._module_configuration.module_name,
                    'RUNTIME': self._module_configuration.effective_runtime,
                    'VERSION': self._module_configuration.major_version},
                nocache=False),
            port=PORT,
            environment={
                'API_HOST': api_host,
                'API_PORT': runtime_config.api_port,
                'GAE_LONG_APP_ID':
                    self._module_configuration.application_external_name,
                'GAE_PARTITION': self._module_configuration.partition,
                'GAE_MODULE_NAME': self._module_configuration.module_name,
                'GAE_MODULE_VERSION': self._module_configuration.major_version,
                'GAE_MINOR_VERSION': self._module_configuration.minor_version,
                'GAE_MODULE_INSTANCE': runtime_config.instance_id},
            volumes={
                '/var/log/app_engine/app': {'bind': '/var/log/app_engine/app'}
            },
            volumes_from=None))

    self._container.Start()

    self._proxy = http_proxy.HttpProxy(
        host=self._container.host, port=self._container.port,
        instance_died_unexpectedly=self._instance_died_unexpectedly,
        instance_logs_getter=self._get_instance_logs,
        error_handler_file=application_configuration.get_app_error_file(
            self._module_configuration))
示例#5
0
    def start(self, dockerfile_dir=None):
        runtime_config = self._runtime_config_getter()

        if not self._module_configuration.major_version:
            logging.error('Version needs to be specified in your application '
                          'configuration file.')
            raise VersionError()

        if not dockerfile_dir:
            dockerfile_dir = self._module_configuration.application_root

        # api_host set to 'localhost' won't be accessible from a docker container
        # because container will have it's own 'localhost'.
        # 10.0.2.2 is a special network setup by virtualbox to connect from the
        # guest to the host.
        api_host = runtime_config.api_host
        if runtime_config.api_host in ('0.0.0.0', 'localhost'):
            api_host = '10.0.2.2'

        image_name = _DOCKER_IMAGE_NAME_FORMAT.format(
            # Escape domain if it is present.
            display=self._escape_domain(
                self._module_configuration.application_external_name),
            module=self._module_configuration.module_name,
            version=self._module_configuration.major_version)

        port_bindings = self._port_bindings if self._port_bindings else {}
        port_bindings.setdefault(self._default_port, None)
        debug_port = None

        environment = {
            'API_HOST':
            api_host,
            'API_PORT':
            runtime_config.api_port,
            'GAE_LONG_APP_ID':
            self._module_configuration.application_external_name,
            'GAE_PARTITION':
            self._module_configuration.partition,
            'GAE_MODULE_NAME':
            self._module_configuration.module_name,
            'GAE_MODULE_VERSION':
            self._module_configuration.major_version,
            'GAE_MINOR_VERSION':
            self._module_configuration.minor_version,
            'GAE_MODULE_INSTANCE':
            runtime_config.instance_id,
            'GAE_SERVER_PORT':
            runtime_config.server_port,
            'MODULE_YAML_PATH':
            os.path.basename(self._module_configuration.config_path)
        }
        if self._additional_environment:
            environment.update(self._additional_environment)

        # Handle user defined environment variables
        if self._module_configuration.env_variables:
            ev = (environment.viewkeys()
                  & self._module_configuration.env_variables.viewkeys())
            if ev:
                raise InvalidEnvVariableError(
                    'Environment variables [%s] are reserved for App Engine use'
                    % ', '.join(ev))

            environment.update(self._module_configuration.env_variables)

            # Publish debug port if running in Debug mode.
            if self._module_configuration.env_variables.get('DBG_ENABLE'):
                debug_port = int(
                    self._module_configuration.env_variables.get(
                        'DBG_PORT', self.DEFAULT_DEBUG_PORT))
                environment['DBG_PORT'] = debug_port
                port_bindings[debug_port] = _GetPortToPublish(debug_port)

        # Publish forwarded ports
        # NOTE: fowarded ports are mapped as host_port => container_port,
        # port_bindings are mapped the other way around.
        for h, c in self._module_configuration.forwarded_ports.iteritems():
            if c in port_bindings:
                raise InvalidForwardedPortError(
                    'Port {port} is already used by debugger or runtime specific '
                    'VM Service. Please use a different forwarded_port.'.
                    format(port=c))
            port_bindings[c] = h

        external_logs_path = os.path.join(
            '/var/log/app_engine',
            self._escape_domain(
                self._module_configuration.application_external_name),
            self._module_configuration.module_name,
            self._module_configuration.major_version,
            runtime_config.instance_id)
        internal_logs_path = '/var/log/app_engine'
        container_name = _DOCKER_CONTAINER_NAME_FORMAT.format(
            image_name=image_name, instance_id=runtime_config.instance_id)
        self._container = containers.Container(
            self._docker_client,
            containers.ContainerOptions(
                image_opts=containers.ImageOptions(
                    dockerfile_dir=dockerfile_dir,
                    tag=image_name,
                    nocache=False),
                port=self._default_port,
                port_bindings=port_bindings,
                environment=environment,
                volumes={external_logs_path: {
                    'bind': internal_logs_path
                }},
                name=container_name))

        if self._log_server_container:
            self._logs_container = self._build_logs_container(
                external_logs_path, internal_logs_path,
                runtime_config.instance_id)

        self._container.Start()
        if self._log_server_container:
            try:
                self._logs_container.Start()
            except containers.ImageError:
                logging.warning('Image \'log_server\' is not found.'
                                'Showing logs in admin console is disabled.')

        # Print the debug information before connecting to the container
        # as debugging might break the runtime during initialization, and
        # connecting the debugger is required to start processing requests.
        if debug_port:
            logging.info(
                'To debug module {module} attach to {host}:{port}'.format(
                    module=self._module_configuration.module_name,
                    host=self.ContainerHost(),
                    port=self.PortBinding(debug_port)))

        self._proxy = http_proxy.HttpProxy(
            host=self._container.host,
            port=self._container.port,
            instance_died_unexpectedly=self._instance_died_unexpectedly,
            instance_logs_getter=self.get_instance_logs,
            error_handler_file=application_configuration.get_app_error_file(
                self._module_configuration))

        # If forwarded ports are used we do not really have to serve on 8080.
        # We'll log /_ah/start request fail, but with health-checks disabled
        # we should ignore that and continue working (accepting requests on
        # our forwarded ports outside of dev server control).
        health_check = self._module_configuration.vm_health_check
        health_check_enabled = health_check and health_check.enable_health_check

        if health_check_enabled or not self._module_configuration.forwarded_ports:
            self._proxy.wait_for_connection()
示例#6
0
  def start(self):
    """Starts the runtime process and waits until it is ready to serve."""
    runtime_config = self._runtime_config_getter()
    # TODO: Use a different process group to isolate the child process
    # from signals sent to the parent. Only available in subprocess in
    # Python 2.7.
    assert self._start_process_flavor in self._VALID_START_PROCESS_FLAVORS
    host = 'localhost'
    if self._start_process_flavor == START_PROCESS:
      serialized_config = base64.b64encode(runtime_config.SerializeToString())
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        self._process = safe_subprocess.start_process(
            self._args,
            serialized_config,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            env=self._env,
            cwd=self._module_configuration.application_root)
      port = self._process.stdout.readline()
      if '\t' in port:  # Split out the host if present.
        host, port = port.split('\t', 1)
    elif self._start_process_flavor == START_PROCESS_FILE:
      serialized_config = runtime_config.SerializeToString()
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        self._process = safe_subprocess.start_process_file(
            args=self._args,
            input_string=serialized_config,
            env=self._env,
            cwd=self._module_configuration.application_root,
            stderr=subprocess.PIPE)
      port = self._read_start_process_file()
      _remove_retry_sharing_violation(self._process.child_out.name)
    elif self._start_process_flavor == START_PROCESS_REVERSE:
      serialized_config = runtime_config.SerializeToString()
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        port = portpicker.PickUnusedPort()
        self._env['PORT'] = str(port)

        # If any of the strings in args contain {port}, replace that substring
        # with the selected port. This allows a user-specified runtime to
        # pass the port along to the subprocess as a command-line argument.
        args = [arg.replace('{port}', str(port)) for arg in self._args]

        self._process = safe_subprocess.start_process_file(
            args=args,
            input_string=serialized_config,
            env=self._env,
            cwd=self._module_configuration.application_root,
            stderr=subprocess.PIPE)
    elif self._start_process_flavor == START_PROCESS_REVERSE_NO_FILE:
      serialized_config = runtime_config.SerializeToString()
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        port = portpicker.PickUnusedPort()
        if self._extra_args_getter:
          self._args.append(self._extra_args_getter(port))

        # If any of the strings in _args contain {port}, {api_host}, {api_port},
        # replace that substring with the selected port. This allows
        # a user-specified runtime to pass the port along to the subprocess
        # as a command-line argument.
        args = [arg.replace('{port}', str(port))
                .replace('{api_port}', str(runtime_config.api_port))
                .replace('{api_host}', runtime_config.api_host)
                for arg in self._args]

        self._process = safe_subprocess.start_process(
            args=args,
            input_string=serialized_config,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            env=self._env,
            cwd=self._module_configuration.application_root)

    # _stderr_tee may be pre-set by unit tests.
    if self._stderr_tee is None:
      self._stderr_tee = tee.Tee(self._process.stderr, sys.stderr)
      self._stderr_tee.start()

    error = None
    try:
      port = int(port)
    except ValueError:
      error = 'bad runtime process port [%r]' % port
      logging.error(error)
    finally:
      self._proxy = http_proxy.HttpProxy(
          host=host, port=port,
          instance_died_unexpectedly=self._instance_died_unexpectedly,
          instance_logs_getter=self._get_instance_logs,
          error_handler_file=application_configuration.get_app_error_file(
              self._module_configuration),
          prior_error=error)
      self._proxy.wait_for_connection()
示例#7
0
  def start(self):
    """Starts the runtime process and waits until it is ready to serve."""
    runtime_config = self._runtime_config_getter()
    # TODO: Use a different process group to isolate the child process
    # from signals sent to the parent. Only available in subprocess in
    # Python 2.7.
    assert self._start_process_flavor in self._VALID_START_PROCESS_FLAVORS
    if self._start_process_flavor == START_PROCESS:
      serialized_config = base64.b64encode(runtime_config.SerializeToString())
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        self._process = safe_subprocess.start_process(
            self._args,
            serialized_config,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            env=self._env,
            cwd=self._module_configuration.application_root)
      port = self._process.stdout.readline()
      if '\t' in port:  # Split out the host if present.
        host, port = port.split('\t', 1)
    elif self._start_process_flavor == START_PROCESS_FILE:
      serialized_config = runtime_config.SerializeToString()
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        self._process = safe_subprocess.start_process_file(
            args=self._args,
            input_string=serialized_config,
            env=self._env,
            cwd=self._module_configuration.application_root,
            stderr=subprocess.PIPE)
      port = self._read_start_process_file()
      _remove_retry_sharing_violation(self._process.child_out.name)
    elif self._start_process_flavor == START_PROCESS_REVERSE:
      serialized_config = runtime_config.SerializeToString()
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        port = portpicker.PickUnusedPort()
        self._env['PORT'] = str(port)

        # If any of the strings in args contain {port}, replace that substring
        # with the selected port. This allows a user-specified runtime to
        # pass the port along to the subprocess as a command-line argument.
        args = [arg.replace('{port}', str(port)) for arg in self._args]

        self._process = safe_subprocess.start_process_file(
            args=args,
            input_string=serialized_config,
            env=self._env,
            cwd=self._module_configuration.application_root,
            stderr=subprocess.PIPE)
    elif self._start_process_flavor == START_PROCESS_REVERSE_NO_FILE:
      serialized_config = runtime_config.SerializeToString()
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        port = portpicker.PickUnusedPort()
        if self._extra_args_getter:
          self._args.append(self._extra_args_getter(port))

        # If any of the strings in _args contain {port}, {api_host}, {api_port},
        # replace that substring with the selected port. This allows
        # a user-specified runtime to pass the port along to the subprocess
        # as a command-line argument.
        args = [arg.replace('{port}', str(port))
                .replace('{api_port}', str(runtime_config.api_port))
                .replace('{api_host}', runtime_config.api_host)
                for arg in self._args]

        self._process = safe_subprocess.start_process(
            args=args,
            input_string=serialized_config,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            env=self._env,
            cwd=self._module_configuration.application_root)

    # _stderr_tee may be pre-set by unit tests.
    if self._stderr_tee is None:
      self._stderr_tee = tee.Tee(self._process.stderr, sys.stderr)
      self._stderr_tee.start()

    error = None
    try:
      port = int(port)
    except ValueError:
      error = 'bad runtime process port [%r]' % port
      logging.error(error)
    finally:
      self._proxy = http_proxy.HttpProxy(
          host='localhost', port=port,
          instance_died_unexpectedly=self._instance_died_unexpectedly,
          instance_logs_getter=self._get_instance_logs,
          error_handler_file=application_configuration.get_app_error_file(
              self._module_configuration),
          prior_error=error)
      self._proxy.wait_for_connection()
示例#8
0
  def start(self, dockerfile_dir=None):
    runtime_config = self._runtime_config_getter()

    if not dockerfile_dir:
      dockerfile_dir = self._module_configuration.application_root

    # api_host set to 'localhost' won't be accessible from a docker container
    # because container will have it's own 'localhost'.
    # 10.0.2.2 is a special network setup by virtualbox to connect from the
    # guest to the host.
    api_host = runtime_config.api_host
    if runtime_config.api_host in ('0.0.0.0', 'localhost'):
      api_host = '10.0.2.2'

    image_name = _DOCKER_IMAGE_NAME_FORMAT.format(
        # Escape domain if it is present.
        display=self._module_configuration.application_external_name.replace(
            ':', '.'),
        module=self._module_configuration.module_name,
        version=self._module_configuration.major_version)

    environment = {
        'API_HOST': api_host,
        'API_PORT': runtime_config.api_port,
        'GAE_LONG_APP_ID': self._module_configuration.application_external_name,
        'GAE_PARTITION': self._module_configuration.partition,
        'GAE_MODULE_NAME': self._module_configuration.module_name,
        'GAE_MODULE_VERSION': self._module_configuration.major_version,
        'GAE_MINOR_VERSION': self._module_configuration.minor_version,
        'GAE_MODULE_INSTANCE': runtime_config.instance_id,
        'GAE_SERVER_PORT': runtime_config.server_port,
        'MODULE_YAML_PATH': os.path.basename(
            self._module_configuration.config_path)
    }

    # Handle user defined environment variables
    if self._module_configuration.env_variables:
      ev = (environment.viewkeys() &
            self._module_configuration.env_variables.viewkeys())
      if ev:
        raise InvalidEnvVariableError(
            'Environment variables [%s] are reserved for App Engine use' %
            ', '.join(ev))
      environment.update(self._module_configuration.env_variables)

    port_bindings = self._port_bindings if self._port_bindings else {}
    port_bindings.setdefault(self._default_port, None)
    self._container = containers.Container(
        self._docker_client,
        containers.ContainerOptions(
            image_opts=containers.ImageOptions(
                dockerfile_dir=dockerfile_dir,
                tag=image_name,
                nocache=False),
            port=self._default_port,
            port_bindings=port_bindings,
            environment=environment,
            volumes={
                '/var/log/app_engine/app': {'bind': '/var/log/app_engine/app'}
            }
        ))

    self._container.Start()

    self._proxy = http_proxy.HttpProxy(
        host=self._container.host, port=self._container.port,
        instance_died_unexpectedly=self._instance_died_unexpectedly,
        instance_logs_getter=self._get_instance_logs,
        error_handler_file=application_configuration.get_app_error_file(
            self._module_configuration))
    self._proxy.wait_for_connection()
示例#9
0
  def start(self, dockerfile_dir=None, request_id_header_name=None):
    runtime_config = self._runtime_config_getter()

    if not self._module_configuration.major_version:
      logging.error('Version needs to be specified in your application '
                    'configuration file.')
      raise VersionError()

    self._log_manager.add(
        self._escape_domain(
            self._module_configuration.application_external_name),
        self._module_configuration.module_name,
        self._module_configuration.major_version, runtime_config.instance_id)

    if not dockerfile_dir:
      dockerfile_dir = self._module_configuration.application_root

    # api_host set to 'localhost' won't be accessible from a docker container
    # because container will have it's own 'localhost'.
    # 10.0.2.2 is a special network setup by virtualbox to connect from the
    # guest to the host.
    api_host = runtime_config.api_host
    if runtime_config.api_host in ('0.0.0.0', 'localhost'):
      api_host = '10.0.2.2'

    image_name = _DOCKER_IMAGE_NAME_FORMAT.format(
        # Escape domain if it is present.
        display=self._escape_domain(
            self._module_configuration.application_external_name),
        module=self._module_configuration.module_name,
        version=self._module_configuration.major_version)

    port_bindings = self._port_bindings if self._port_bindings else {}
    port_bindings.setdefault(self._default_port, None)
    debug_port = None

    environment = {
        'API_HOST': api_host,
        'API_PORT': runtime_config.api_port,
        'GAE_LONG_APP_ID': self._module_configuration.application_external_name,
        'GAE_PARTITION': self._module_configuration.partition,
        'GAE_MODULE_NAME': self._module_configuration.module_name,
        'GAE_MODULE_VERSION': self._module_configuration.major_version,
        'GAE_MINOR_VERSION': self._module_configuration.minor_version,
        'GAE_MODULE_INSTANCE': runtime_config.instance_id,
        'GAE_SERVER_PORT': runtime_config.server_port,
        'MODULE_YAML_PATH': os.path.basename(
            self._module_configuration.config_path),
        'SERVER_SOFTWARE': http_runtime_constants.SERVER_SOFTWARE
    }
    if self._additional_environment:
      environment.update(self._additional_environment)

    # Handle user defined environment variables
    if self._module_configuration.env_variables:
      ev = (environment.viewkeys() &
            self._module_configuration.env_variables.viewkeys())
      if ev:
        raise InvalidEnvVariableError(
            'Environment variables [%s] are reserved for App Engine use' %
            ', '.join(ev))

      environment.update(self._module_configuration.env_variables)

      # Publish debug port if running in Debug mode.
      if self._module_configuration.env_variables.get('DBG_ENABLE'):
        debug_port = int(self._module_configuration.env_variables.get(
            'DBG_PORT', self.DEFAULT_DEBUG_PORT))
        environment['DBG_PORT'] = debug_port
        port_bindings[debug_port] = _GetPortToPublish(debug_port)

    # Publish forwarded ports
    # NOTE: fowarded ports are mapped as host_port => container_port,
    # port_bindings are mapped the other way around.
    for h, c in self._module_configuration.forwarded_ports.iteritems():
      if c in port_bindings:
        raise InvalidForwardedPortError(
            'Port {port} is already used by debugger or runtime specific '
            'VM Service. Please use a different forwarded_port.'.format(port=c))
      port_bindings[c] = h

    external_logs_path = os.path.join(
        '/var/log/app_engine',
        self._escape_domain(
            self._module_configuration.application_external_name),
        self._module_configuration.module_name,
        self._module_configuration.major_version,
        runtime_config.instance_id)
    internal_logs_path = '/var/log/app_engine'
    container_name = _ContainerName(
        image_name=image_name,
        instance_id=runtime_config.instance_id)
    self._container = containers.Container(
        self._docker_client,
        containers.ContainerOptions(
            image_opts=containers.ImageOptions(
                dockerfile_dir=dockerfile_dir,
                tag=image_name,
                nocache=False),
            port=self._default_port,
            port_bindings=port_bindings,
            environment=environment,
            volumes={
                external_logs_path: {'bind': internal_logs_path}
            },
            name=container_name
        ))

    self._container.Start()
    # As we add stuff, asynchronously check later for a cleanup.
    containers.StartDelayedCleanup(
        self._docker_client, _APP_ENGINE_PREFIX, _CLEANUP_DELAY_SEC,
        _CONTAINERS_TO_KEEP)

    # Print the debug information before connecting to the container
    # as debugging might break the runtime during initialization, and
    # connecting the debugger is required to start processing requests.
    if debug_port:
      logging.info('To debug module {module} attach to {host}:{port}'.format(
          module=self._module_configuration.module_name,
          host=self.ContainerHost(),
          port=self.PortBinding(debug_port)))

    self._proxy = http_proxy.HttpProxy(
        host=self._container.host, port=self._container.port,
        instance_died_unexpectedly=self._instance_died_unexpectedly,
        instance_logs_getter=self.get_instance_logs,
        error_handler_file=application_configuration.get_app_error_file(
            self._module_configuration),
        request_id_header_name=request_id_header_name)

    # If forwarded ports are used we do not really have to serve on 8080.
    # We'll log /_ah/start request fail, but with health-checks disabled
    # we should ignore that and continue working (accepting requests on
    # our forwarded ports outside of dev server control).
    health_check = self._module_configuration.health_check
    health_check_enabled = health_check and health_check.enable_health_check

    if health_check_enabled or not self._module_configuration.forwarded_ports:
      self._proxy.wait_for_connection()
  def start(self, dockerfile_dir=None):
    runtime_config = self._runtime_config_getter()

    if not self._module_configuration.major_version:
      logging.error('Version needs to be specified in your application '
                    'configuration file.')
      raise VersionError()

    if not dockerfile_dir:
      dockerfile_dir = self._module_configuration.application_root

    # api_host set to 'localhost' won't be accessible from a docker container
    # because container will have it's own 'localhost'.
    # 10.0.2.2 is a special network setup by virtualbox to connect from the
    # guest to the host.
    api_host = runtime_config.api_host
    if runtime_config.api_host in ('0.0.0.0', 'localhost'):
      api_host = '10.0.2.2'

    image_name = _DOCKER_IMAGE_NAME_FORMAT.format(
        # Escape domain if it is present.
        display=self._escape_domain(
            self._module_configuration.application_external_name),
        module=self._module_configuration.module_name,
        version=self._module_configuration.major_version)

    port_bindings = self._port_bindings if self._port_bindings else {}
    port_bindings.setdefault(self._default_port, None)
    debug_port = None

    environment = {
        'API_HOST': api_host,
        'API_PORT': runtime_config.api_port,
        'GAE_LONG_APP_ID': self._module_configuration.application_external_name,
        'GAE_PARTITION': self._module_configuration.partition,
        'GAE_MODULE_NAME': self._module_configuration.module_name,
        'GAE_MODULE_VERSION': self._module_configuration.major_version,
        'GAE_MINOR_VERSION': self._module_configuration.minor_version,
        'GAE_MODULE_INSTANCE': runtime_config.instance_id,
        'GAE_SERVER_PORT': runtime_config.server_port,
        'MODULE_YAML_PATH': os.path.basename(
            self._module_configuration.config_path)
    }
    if self._additional_environment:
      environment.update(self._additional_environment)

    # Handle user defined environment variables
    if self._module_configuration.env_variables:
      ev = (environment.viewkeys() &
            self._module_configuration.env_variables.viewkeys())
      if ev:
        raise InvalidEnvVariableError(
            'Environment variables [%s] are reserved for App Engine use' %
            ', '.join(ev))

      environment.update(self._module_configuration.env_variables)

      # Publish debug port if running in Debug mode.
      if self._module_configuration.env_variables.get('DBG_ENABLE'):
        debug_port = int(self._module_configuration.env_variables.get(
            'DBG_PORT', self.DEFAULT_DEBUG_PORT))
        environment['DBG_PORT'] = debug_port
        port_bindings[debug_port] = _GetPortToPublish(debug_port)

    external_logs_path = os.path.join(
        '/var/log/app_engine',
        self._escape_domain(
            self._module_configuration.application_external_name),
        self._module_configuration.module_name,
        self._module_configuration.major_version,
        runtime_config.instance_id)
    container_name = _DOCKER_CONTAINER_NAME_FORMAT.format(
        image_name=image_name,
        minor_version=self._module_configuration.minor_version)
    self._container = containers.Container(
        self._docker_client,
        containers.ContainerOptions(
            image_opts=containers.ImageOptions(
                dockerfile_dir=dockerfile_dir,
                tag=image_name,
                nocache=False),
            port=self._default_port,
            port_bindings=port_bindings,
            environment=environment,
            volumes={
                external_logs_path: {'bind': '/var/log/app_engine'}
            },
            name=container_name
        ))

    self._container.Start()

    # Print the debug information before connecting to the container
    # as debugging might break the runtime during initialization, and
    # connecting the debugger is required to start processing requests.
    if debug_port:
      logging.info('To debug module {module} attach to {host}:{port}'.format(
          module=self._module_configuration.module_name,
          host=self.ContainerHost(),
          port=self.PortBinding(debug_port)))

    self._proxy = http_proxy.HttpProxy(
        host=self._container.host, port=self._container.port,
        instance_died_unexpectedly=self._instance_died_unexpectedly,
        instance_logs_getter=self._get_instance_logs,
        error_handler_file=application_configuration.get_app_error_file(
            self._module_configuration))
    self._proxy.wait_for_connection()
示例#11
0
    def start(self, dockerfile_dir=None):
        runtime_config = self._runtime_config_getter()

        if not self._module_configuration.major_version:
            logging.error('Version needs to be specified in your application '
                          'configuration file.')
            raise VersionError()

        if not dockerfile_dir:
            dockerfile_dir = self._module_configuration.application_root

        # api_host set to 'localhost' won't be accessible from a docker container
        # because container will have it's own 'localhost'.
        # 10.0.2.2 is a special network setup by virtualbox to connect from the
        # guest to the host.
        api_host = runtime_config.api_host
        if runtime_config.api_host in ('0.0.0.0', 'localhost'):
            api_host = '10.0.2.2'

        image_name = _DOCKER_IMAGE_NAME_FORMAT.format(
            # Escape domain if it is present.
            display=self._escape_domain(
                self._module_configuration.application_external_name),
            module=self._module_configuration.module_name,
            version=self._module_configuration.major_version)

        port_bindings = self._port_bindings if self._port_bindings else {}
        port_bindings.setdefault(self._default_port, None)
        debug_port = None

        environment = {
            'API_HOST':
            api_host,
            'API_PORT':
            runtime_config.api_port,
            'GAE_LONG_APP_ID':
            self._module_configuration.application_external_name,
            'GAE_PARTITION':
            self._module_configuration.partition,
            'GAE_MODULE_NAME':
            self._module_configuration.module_name,
            'GAE_MODULE_VERSION':
            self._module_configuration.major_version,
            'GAE_MINOR_VERSION':
            self._module_configuration.minor_version,
            'GAE_MODULE_INSTANCE':
            runtime_config.instance_id,
            'GAE_SERVER_PORT':
            runtime_config.server_port,
            'MODULE_YAML_PATH':
            os.path.basename(self._module_configuration.config_path)
        }
        if self._additional_environment:
            environment.update(self._additional_environment)

        # Handle user defined environment variables
        if self._module_configuration.env_variables:
            ev = (environment.viewkeys()
                  & self._module_configuration.env_variables.viewkeys())
            if ev:
                raise InvalidEnvVariableError(
                    'Environment variables [%s] are reserved for App Engine use'
                    % ', '.join(ev))

            environment.update(self._module_configuration.env_variables)

            # Publish debug port if running in Debug mode.
            if self._module_configuration.env_variables.get('DBG_ENABLE'):
                debug_port = int(
                    self._module_configuration.env_variables.get(
                        'DBG_PORT', self.DEFAULT_DEBUG_PORT))
                environment['DBG_PORT'] = debug_port
                port_bindings[debug_port] = _GetPortToPublish(debug_port)

        external_logs_path = os.path.join(
            '/var/log/app_engine',
            self._escape_domain(
                self._module_configuration.application_external_name),
            self._module_configuration.module_name,
            self._module_configuration.major_version,
            runtime_config.instance_id)
        container_name = _DOCKER_CONTAINER_NAME_FORMAT.format(
            image_name=image_name,
            minor_version=self._module_configuration.minor_version)
        self._container = containers.Container(
            self._docker_client,
            containers.ContainerOptions(
                image_opts=containers.ImageOptions(
                    dockerfile_dir=dockerfile_dir,
                    tag=image_name,
                    nocache=False),
                port=self._default_port,
                port_bindings=port_bindings,
                environment=environment,
                volumes={external_logs_path: {
                    'bind': '/var/log/app_engine'
                }},
                name=container_name))

        self._container.Start()

        # Print the debug information before connecting to the container
        # as debugging might break the runtime during initialization, and
        # connecting the debugger is required to start processing requests.
        if debug_port:
            logging.info(
                'To debug module {module} attach to {host}:{port}'.format(
                    module=self._module_configuration.module_name,
                    host=self.ContainerHost(),
                    port=self.PortBinding(debug_port)))

        self._proxy = http_proxy.HttpProxy(
            host=self._container.host,
            port=self._container.port,
            instance_died_unexpectedly=self._instance_died_unexpectedly,
            instance_logs_getter=self._get_instance_logs,
            error_handler_file=application_configuration.get_app_error_file(
                self._module_configuration))
        self._proxy.wait_for_connection()