def update_dispatch(cls, source_location, keyname, project_id):
    """ Updates an application's dispatch routing rules from the configuration
      file.

    Args:
      options: A Namespace that has fields for each parameter that can be
        passed in via the command-line interface.
    """
    if cls.TAR_GZ_REGEX.search(source_location):
      fetch_function = utils.config_from_tar_gz
      version = Version.from_tar_gz(source_location)
    elif cls.ZIP_REGEX.search(source_location):
      fetch_function = utils.config_from_zip
      version = Version.from_zip(source_location)
    elif os.path.isdir(source_location):
      fetch_function = utils.config_from_dir
      version = Version.from_directory(source_location)
    elif source_location.endswith('.yaml'):
      fetch_function = utils.config_from_dir
      version = Version.from_yaml_file(source_location)
      source_location = os.path.dirname(source_location)
    else:
      raise BadConfigurationException(
        '{} must be a directory, tar.gz, or zip'.format(source_location))

    if project_id:
      version.project_id = project_id

    dispatch_rules = utils.dispatch_from_yaml(source_location, fetch_function)
    if dispatch_rules is None:
        return
    AppScaleLogger.log('Updating dispatch for {}'.format(version.project_id))

    load_balancer_ip = LocalState.get_host_with_role(keyname, 'load_balancer')
    secret_key = LocalState.get_secret_key(keyname)
    admin_client = AdminClient(load_balancer_ip, secret_key)
    operation_id = admin_client.update_dispatch(version.project_id, dispatch_rules)

    # Check on the operation.
    AppScaleLogger.log("Please wait for your dispatch to be updated.")

    deadline = time.time() + cls.MAX_OPERATION_TIME
    while True:
      if time.time() > deadline:
        raise AppScaleException('The operation took too long.')
      operation = admin_client.get_operation(version.project_id, operation_id)
      if not operation['done']:
        time.sleep(1)
        continue

      if 'error' in operation:
        raise AppScaleException(operation['error']['message'])
      dispatch_rules = operation['response']['dispatchRules']
      break

    AppScaleLogger.verbose(
        "The following dispatchRules have been applied to your application's "
        "configuration : {}".format(dispatch_rules))
    AppScaleLogger.success('Dispatch has been updated for {}'.format(
        version.project_id))
  def update_queues(cls, source_location, keyname, project_id):
    """ Updates a project's queues from the configuration file.

    Args:
      source_location: A string specifying the location of the source code.
      keyname: A string specifying the key name.
      project_id: A string specifying the project ID.
    """
    if cls.TAR_GZ_REGEX.search(source_location):
      fetch_function = utils.config_from_tar_gz
      version = Version.from_tar_gz(source_location)
    elif cls.ZIP_REGEX.search(source_location):
      fetch_function = utils.config_from_zip
      version = Version.from_zip(source_location)
    elif os.path.isdir(source_location):
      fetch_function = utils.config_from_dir
      version = Version.from_directory(source_location)
    elif source_location.endswith('.yaml'):
      fetch_function = utils.config_from_dir
      version = Version.from_yaml_file(source_location)
      source_location = os.path.dirname(source_location)
    else:
      raise BadConfigurationException(
        '{} must be a directory, tar.gz, or zip'.format(source_location))

    if project_id:
      version.project_id = project_id

    queue_config = fetch_function('queue.yaml', source_location)
    if queue_config is None:
      queue_config = fetch_function('queue.xml', source_location)
      # If the source does not have a queue configuration file, do nothing.
      if queue_config is None:
        return

      queues = utils.queues_from_xml(queue_config)
    else:
      queues = yaml.safe_load(queue_config)

    AppScaleLogger.log('Updating queues')

    for queue in queues.get('queue', []):
      if 'bucket_size' in queue or 'max_concurrent_requests' in queue:
        AppScaleLogger.warn('Queue configuration uses unsupported rate options'
                            ' (bucket size or max concurrent requests)')
        break

    load_balancer_ip = LocalState.get_host_with_role(keyname, 'load_balancer')
    secret_key = LocalState.get_secret_key(keyname)
    admin_client = AdminClient(load_balancer_ip, secret_key)
    admin_client.update_queues(version.project_id, queues)
  def test_upload_app(self):
    app_id = 'guestbook'
    source_path = '{}.tar.gz'.format(app_id)
    extracted_dir = '/tmp/{}'.format(app_id)
    head_node = '192.168.33.10'
    secret = 'secret-key'
    operation_id = 'operation-1'
    port = 8080
    version_url = 'http://{}:{}'.format(head_node, port)

    argv = ['--keyname', self.keyname, '--file', source_path, '--test']
    options = ParseArgs(argv, self.function).args

    version = Version('python27', 'app.yaml')
    version.project_id = app_id

    flexmock(LocalState).should_receive('extract_tgz_app_to_dir').\
      and_return('/tmp/{}'.format(app_id))
    flexmock(Version).should_receive('from_tar_gz').and_return(version)
    flexmock(AppEngineHelper).should_receive('validate_app_id')
    flexmock(LocalState).should_receive('get_host_with_role').\
      and_return(head_node)
    flexmock(LocalState).should_receive('get_secret_key').and_return(secret)
    flexmock(RemoteHelper).should_receive('copy_app_to_host').\
      with_args(extracted_dir, app_id, self.keyname, False, {}, None).\
      and_return(source_path)
    flexmock(AdminClient).should_receive('create_version').\
      and_return(operation_id)
    flexmock(AdminClient).should_receive('get_operation').\
      and_return({'done': True, 'response': {'versionUrl': version_url}})
    flexmock(shutil).should_receive('rmtree').with_args(extracted_dir)
    flexmock(AppEngineHelper).should_receive('warn_if_version_defined')

    given_host, given_port = AppScaleTools.upload_app(options)
    self.assertEquals(given_host, head_node)
    self.assertEquals(given_port, port)

    # If provided user is not app admin, deployment should fail.
    flexmock(AdminClient).should_receive('create_version').\
      and_raise(AdminError)
    self.assertRaises(AdminError, AppScaleTools.upload_app, options)

    # An application with the PHP runtime should be deployed successfully.
    version = Version('php', 'app.yaml')
    version.project_id = app_id
    flexmock(Version).should_receive('from_tar_gz').and_return(version)
    flexmock(AdminClient).should_receive('create_version').\
      and_return(operation_id)
    given_host, given_port = AppScaleTools.upload_app(options)
    self.assertEquals(given_host, head_node)
    self.assertEquals(given_port, port)
示例#4
0
  def test_from_directory(self):
    # Ensure an exception is raised if there are no configuration candidates.
    shortest_path_func = 'appscale.tools.admin_api.version.shortest_directory_path'
    with patch(shortest_path_func, side_effect=lambda fn, path: None):
      with self.assertRaises(AppEngineConfigException):
        Version.from_directory('/example/guestbook')

    with patch(shortest_path_func,
               side_effect=lambda fn, path: '/example/guestbook/app.yaml'):
      open_path = 'appscale.tools.admin_api.version.open'
      with patch(open_path, mock_open(read_data=SIMPLE_APP_YAML)):
        version = Version.from_yaml_file('/example/app.yaml')

      self.assertEqual(version.runtime, 'python27')
示例#5
0
  def test_from_directory(self):
    # Ensure an exception is raised if there are no configuration candidates.
    shortest_path_func = 'appscale.tools.admin_api.version.shortest_directory_path'
    with patch(shortest_path_func, side_effect=lambda fn, path: None):
      with self.assertRaises(AppEngineConfigException):
        Version.from_directory('/example/guestbook')

    with patch(shortest_path_func,
               side_effect=lambda fn, path: '/example/guestbook/app.yaml'):
      open_path = 'appscale.tools.admin_api.version.open'
      with patch(open_path, mock_open(read_data=SIMPLE_APP_YAML)):
        version = Version.from_yaml_file('/example/app.yaml')

      self.assertEqual(version.runtime, 'python27')
示例#6
0
  def test_from_xml_file(self):
    tree = MagicMock()
    tree.getroot.return_value = ElementTree.fromstring(SIMPLE_AE_WEB_XML)
    with patch.object(ElementTree, 'parse', return_value=tree):
      version = Version.from_xml_file('/example/appengine-web.xml')

    self.assertEqual(version.runtime, 'java')
示例#7
0
  def test_from_xml_file(self):
    tree = MagicMock()
    tree.getroot.return_value = ElementTree.fromstring(SIMPLE_AE_WEB_XML)
    with patch.object(ElementTree, 'parse', return_value=tree):
      version = Version.from_xml_file('/example/appengine-web.xml')

    self.assertEqual(version.runtime, 'java')
  def update_cron(cls, source_location, keyname, project_id):
    """ Updates a project's cron jobs from the configuration file.

    Args:
      source_location: A string specifying the location of the source code.
      keyname: A string specifying the key name.
      project_id: A string specifying the project ID.
    """
    if cls.TAR_GZ_REGEX.search(source_location):
      fetch_function = utils.config_from_tar_gz
      version = Version.from_tar_gz(source_location)
    elif cls.ZIP_REGEX.search(source_location):
      fetch_function = utils.config_from_zip
      version = Version.from_zip(source_location)
    elif os.path.isdir(source_location):
      fetch_function = utils.config_from_dir
      version = Version.from_directory(source_location)
    elif source_location.endswith('.yaml'):
      fetch_function = utils.config_from_dir
      version = Version.from_yaml_file(source_location)
      source_location = os.path.dirname(source_location)
    else:
      raise BadConfigurationException(
        '{} must be a directory, tar.gz, or zip'.format(source_location))

    if project_id:
      version.project_id = project_id

    cron_config = fetch_function('cron.yaml', source_location)
    if cron_config is None:
      cron_config = fetch_function('cron.xml', source_location)
      # If the source does not have a cron configuration file, do nothing.
      if cron_config is None:
        return

      cron_jobs = utils.cron_from_xml(cron_config)
    else:
      cron_jobs = yaml.safe_load(cron_config)

    AppScaleLogger.log('Updating cron jobs')
    load_balancer_ip = LocalState.get_host_with_role(keyname, 'load_balancer')
    secret_key = LocalState.get_secret_key(keyname)
    admin_client = AdminClient(load_balancer_ip, secret_key)
    admin_client.update_cron(version.project_id, cron_jobs)
  def start_service(cls, options):
    """Instructs AppScale to start the named service.

    This is applicable for services using manual scaling.

    Args:
      options: A Namespace that has fields for each parameter that can be
        passed in via the command-line interface.
    Raises:
      AppScaleException: If the named service isn't running in this
        AppScale cloud, or if start is not valid for the service.
    """
    load_balancer_ip = LocalState.get_host_with_role(
      options.keyname, 'load_balancer')
    secret = LocalState.get_secret_key(options.keyname)
    admin_client = AdminClient(load_balancer_ip, secret)

    version = Version(None, None)
    version.project_id = options.project_id
    version.service_id = options.service_id or DEFAULT_SERVICE
    version.id = DEFAULT_VERSION
    version.serving_status = 'SERVING'

    admin_client.patch_version(version, ['servingStatus'])

    AppScaleLogger.success('Start requested for {}.'.format(options.project_id))
示例#10
0
  def stop_service(cls, options):
    """Instructs AppScale to stop the named service.

    This is applicable for services using manual scaling.

    Args:
      options: A Namespace that has fields for each parameter that can be
        passed in via the command-line interface.
    Raises:
      AppScaleException: If the named service isn't running in this
        AppScale cloud, or if stop is not valid for the service.
    """
    if not options.confirm:
      response = raw_input(
        'Are you sure you want to stop this service? (y/N) ')
      if response.lower() not in ['y', 'yes']:
        raise AppScaleException("Cancelled service stop.")

    load_balancer_ip = LocalState.get_host_with_role(
      options.keyname, 'load_balancer')
    secret = LocalState.get_secret_key(options.keyname)
    admin_client = AdminClient(load_balancer_ip, secret)

    version = Version(None, None)
    version.project_id = options.project_id
    version.service_id = options.service_id or DEFAULT_SERVICE
    version.id = DEFAULT_VERSION
    version.serving_status = 'STOPPED'

    admin_client.patch_version(version, ['servingStatus'])

    AppScaleLogger.success('Stop requested for {}.'.format(options.project_id))
    def test_upload_app(self):
        app_id = 'guestbook'
        source_path = '{}.tar.gz'.format(app_id)
        extracted_dir = '/tmp/{}'.format(app_id)
        head_node = '192.168.33.10'
        secret = 'secret-key'
        operation_id = 'operation-1'
        port = 8080
        version_url = 'http://{}:{}'.format(head_node, port)

        argv = ['--keyname', self.keyname, '--file', source_path, '--test']
        options = ParseArgs(argv, self.function).args

        version = Version('python27', 'app.yaml')
        version.project_id = app_id

        flexmock(LocalState).should_receive('extract_tgz_app_to_dir').\
          and_return('/tmp/{}'.format(app_id))
        flexmock(Version).should_receive('from_tar_gz').and_return(version)
        flexmock(AppEngineHelper).should_receive('validate_app_id')
        flexmock(LocalState).should_receive('get_host_with_role').\
          and_return(head_node)
        flexmock(LocalState).should_receive('get_secret_key').and_return(
            secret)
        flexmock(RemoteHelper).should_receive('copy_app_to_host').\
          with_args(extracted_dir, app_id, self.keyname, {}, None).\
          and_return(source_path)
        flexmock(AdminClient).should_receive('create_version').\
          and_return(operation_id)
        flexmock(AdminClient).should_receive('get_operation').\
          and_return({'done': True, 'response': {'versionUrl': version_url}})
        flexmock(shutil).should_receive('rmtree').with_args(extracted_dir)
        flexmock(AppEngineHelper).should_receive('warn_if_version_defined')

        given_host, given_port = AppScaleTools.upload_app(options)
        self.assertEquals(given_host, head_node)
        self.assertEquals(given_port, port)

        # If provided user is not app admin, deployment should fail.
        flexmock(AdminClient).should_receive('create_version').\
          and_raise(AdminError)
        self.assertRaises(AdminError, AppScaleTools.upload_app, options)

        # An application with the PHP runtime should be deployed successfully.
        version = Version('php', 'app.yaml')
        version.project_id = app_id
        flexmock(Version).should_receive('from_tar_gz').and_return(version)
        flexmock(AdminClient).should_receive('create_version').\
          and_return(operation_id)
        given_host, given_port = AppScaleTools.upload_app(options)
        self.assertEquals(given_host, head_node)
        self.assertEquals(given_port, port)
示例#12
0
  def test_from_contents(self):
    version = Version.from_contents(SIMPLE_APP_YAML, 'app.yaml')
    self.assertEqual(version.runtime, 'python27')

    version = Version.from_contents(SIMPLE_AE_WEB_XML, 'appengine-web.xml')
    self.assertEqual(version.runtime, 'java')
示例#13
0
  def test_from_yaml(self):
    # Ensure an exception is raised if runtime is missing.
    with self.assertRaises(AppEngineConfigException):
      Version.from_yaml({})

    # Ensure runtime string is parsed successfully.
    app_yaml = yaml.safe_load(SIMPLE_APP_YAML)
    version = Version.from_yaml(app_yaml)
    self.assertEqual(version.runtime, 'python27')

    # Ensure project is parsed successfully.
    yaml_with_project = SIMPLE_APP_YAML + 'application: guestbook\n'
    app_yaml = yaml.safe_load(yaml_with_project)
    version = Version.from_yaml(app_yaml)
    self.assertEqual(version.runtime, 'python27')
    self.assertEqual(version.project_id, 'guestbook')

    # Ensure a default service ID is set.
    app_yaml = yaml.safe_load(SIMPLE_APP_YAML)
    version = Version.from_yaml(app_yaml)
    self.assertEqual(version.service_id, 'default')

    # Ensure service ID is parsed correctly.
    yaml_with_module = SIMPLE_APP_YAML + 'module: service1\n'
    app_yaml = yaml.safe_load(yaml_with_module)
    version = Version.from_yaml(app_yaml)
    self.assertEqual(version.service_id, 'service1')

    # Ensure omitted environment variables are handled correctly.
    app_yaml = yaml.safe_load(SIMPLE_APP_YAML)
    version = Version.from_yaml(app_yaml)
    self.assertDictEqual(version.env_variables, {})

    # Ensure environment variables are parsed correctly.
    env_vars = """
    env_variables:
      VAR1: 'foo'
    """.lstrip()
    app_yaml = yaml.safe_load(SIMPLE_APP_YAML + env_vars)
    version = Version.from_yaml(app_yaml)
    self.assertDictEqual(version.env_variables, {'VAR1': 'foo'})

    # Ensure omitted inbound services are handled correctly.
    app_yaml = yaml.safe_load(SIMPLE_APP_YAML)
    version = Version.from_yaml(app_yaml)
    self.assertListEqual(version.inbound_services, [])

    # Ensure inbound services are parsed correctly.
    inbound_services = """
    inbound_services:
      - mail
      - warmup
    """.lstrip()
    app_yaml = yaml.safe_load(SIMPLE_APP_YAML + inbound_services)
    version = Version.from_yaml(app_yaml)
    self.assertListEqual(version.inbound_services, ['mail', 'warmup'])

    # Check empty threadsafe value for non-applicable runtime.
    app_yaml = yaml.safe_load(
      'runtime: go\nhandlers:\n- url: .*\n  script: _go_app\n')
    version = Version.from_yaml(app_yaml)
    self.assertIsNone(version.threadsafe)

    # Check empty threadsafe value for applicable runtime.
    app_yaml = yaml.safe_load('runtime: python27\n')
    with self.assertRaises(AppEngineConfigException):
      Version.from_yaml(app_yaml)

    # Ensure threadsafe value is parsed correctly.
    app_yaml = yaml.safe_load(SIMPLE_APP_YAML)
    version = Version.from_yaml(app_yaml)
    self.assertEqual(version.threadsafe, True)
示例#14
0
  def test_from_xml(self):
    # Check the default runtime string for Java apps.
    # TODO: This should be updated when the Admin API accepts 'java7'.
    appengine_web_xml = ElementTree.fromstring(SIMPLE_AE_WEB_XML)
    version = Version.from_xml(appengine_web_xml)
    self.assertEqual(version.runtime, 'java')

    xml_with_project = AE_WEB_XML_TEMPLATE.format(
      '<application>guestbook</application>')
    appengine_web_xml = ElementTree.fromstring(xml_with_project)
    version = Version.from_xml(appengine_web_xml)
    self.assertEqual(version.runtime, 'java')
    self.assertEqual(version.project_id, 'guestbook')

    # Ensure a default service ID is set.
    appengine_web_xml = ElementTree.fromstring(SIMPLE_AE_WEB_XML)
    version = Version.from_xml(appengine_web_xml)
    self.assertEqual(version.service_id, 'default')

    # Ensure service ID is parsed correctly.
    xml_with_module = AE_WEB_XML_TEMPLATE.format('<module>service1</module>')
    appengine_web_xml = ElementTree.fromstring(xml_with_module)
    version = Version.from_xml(appengine_web_xml)
    self.assertEqual(version.service_id, 'service1')

    # Ensure omitted environment variables are handled correctly.
    appengine_web_xml = ElementTree.fromstring(SIMPLE_AE_WEB_XML)
    version = Version.from_xml(appengine_web_xml)
    self.assertDictEqual(version.env_variables, {})

    # Ensure environment variables are parsed correctly.
    env_vars = """
    <env-variables>
      <env-var name="VAR1" value="foo" />
    </env-variables>
    """.lstrip()
    appengine_web_xml = ElementTree.fromstring(
      AE_WEB_XML_TEMPLATE.format(env_vars))
    version = Version.from_xml(appengine_web_xml)
    self.assertDictEqual(version.env_variables, {'VAR1': 'foo'})

    # Ensure omitted inbound services are handled correctly.
    appengine_web_xml = ElementTree.fromstring(SIMPLE_AE_WEB_XML)
    version = Version.from_xml(appengine_web_xml)
    self.assertListEqual(version.inbound_services, [])

    # Ensure inbound services are parsed correctly.
    env_vars = """
    <inbound-services>
      <service>mail</service>
    </inbound-services>
    """.lstrip()
    appengine_web_xml = ElementTree.fromstring(
        AE_WEB_XML_TEMPLATE.format(env_vars))
    version = Version.from_xml(appengine_web_xml)
    self.assertListEqual(version.inbound_services, ['mail'])

    # Check empty threadsafe value.
    appengine_web_xml = ElementTree.fromstring(
      '<?xml version="1.0" encoding="utf-8"?>'
      '<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">'
      '</appengine-web-app>')
    with self.assertRaises(AppEngineConfigException):
      Version.from_xml(appengine_web_xml)

    # Ensure threadsafe value is parsed correctly.
    appengine_web_xml = ElementTree.fromstring(SIMPLE_AE_WEB_XML)
    version = Version.from_xml(appengine_web_xml)
    self.assertEqual(version.threadsafe, True)
示例#15
0
  def test_from_yaml(self):
    # Ensure an exception is raised if runtime is missing.
    with self.assertRaises(AppEngineConfigException):
      Version.from_yaml({})

    # Ensure runtime string is parsed successfully.
    app_yaml = yaml.safe_load(SIMPLE_APP_YAML)
    version = Version.from_yaml(app_yaml)
    self.assertEqual(version.runtime, 'python27')

    # Ensure project is parsed successfully.
    yaml_with_project = SIMPLE_APP_YAML + 'application: guestbook\n'
    app_yaml = yaml.safe_load(yaml_with_project)
    version = Version.from_yaml(app_yaml)
    self.assertEqual(version.runtime, 'python27')
    self.assertEqual(version.project_id, 'guestbook')

    # Ensure a default service ID is set.
    app_yaml = yaml.safe_load(SIMPLE_APP_YAML)
    version = Version.from_yaml(app_yaml)
    self.assertEqual(version.service_id, 'default')

    # Ensure service ID is parsed correctly.
    yaml_with_module = SIMPLE_APP_YAML + 'module: service1\n'
    app_yaml = yaml.safe_load(yaml_with_module)
    version = Version.from_yaml(app_yaml)
    self.assertEqual(version.service_id, 'service1')

    # Ensure omitted environment variables are handled correctly.
    app_yaml = yaml.safe_load(SIMPLE_APP_YAML)
    version = Version.from_yaml(app_yaml)
    self.assertDictEqual(version.env_variables, {})

    # Ensure environment variables are parsed correctly.
    env_vars = """
    env_variables:
      VAR1: 'foo'
    """.lstrip()
    app_yaml = yaml.safe_load(SIMPLE_APP_YAML + env_vars)
    version = Version.from_yaml(app_yaml)
    self.assertDictEqual(version.env_variables, {'VAR1': 'foo'})

    # Ensure omitted inbound services are handled correctly.
    app_yaml = yaml.safe_load(SIMPLE_APP_YAML)
    version = Version.from_yaml(app_yaml)
    self.assertListEqual(version.inbound_services, [])

    # Ensure inbound services are parsed correctly.
    inbound_services = """
    inbound_services:
      - mail
      - warmup
    """.lstrip()
    app_yaml = yaml.safe_load(SIMPLE_APP_YAML + inbound_services)
    version = Version.from_yaml(app_yaml)
    self.assertListEqual(version.inbound_services, ['mail', 'warmup'])

    # Check empty threadsafe value for non-applicable runtime.
    app_yaml = yaml.safe_load('runtime: go\n')
    version = Version.from_yaml(app_yaml)
    self.assertIsNone(version.threadsafe)

    # Check empty threadsafe value for applicable runtime.
    app_yaml = yaml.safe_load('runtime: python27\n')
    with self.assertRaises(AppEngineConfigException):
      Version.from_yaml(app_yaml)

    # Ensure threadsafe value is parsed correctly.
    app_yaml = yaml.safe_load(SIMPLE_APP_YAML)
    version = Version.from_yaml(app_yaml)
    self.assertEqual(version.threadsafe, True)
示例#16
0
  def test_from_contents(self):
    version = Version.from_contents(SIMPLE_APP_YAML, 'app.yaml')
    self.assertEqual(version.runtime, 'python27')

    version = Version.from_contents(SIMPLE_AE_WEB_XML, 'appengine-web.xml')
    self.assertEqual(version.runtime, 'java')
示例#17
0
  def test_from_yaml_file(self):
    open_path = 'appscale.tools.admin_api.version.open'
    with patch(open_path, mock_open(read_data=SIMPLE_APP_YAML)):
      version = Version.from_yaml_file('/example/app.yaml')

    self.assertEqual(version.runtime, 'python27')
示例#18
0
  def test_from_yaml_file(self):
    open_path = 'appscale.tools.admin_api.version.open'
    with patch(open_path, mock_open(read_data=SIMPLE_APP_YAML)):
      version = Version.from_yaml_file('/example/app.yaml')

    self.assertEqual(version.runtime, 'python27')
示例#19
0
  def upload_app(cls, options):
    """Uploads the given App Engine application into AppScale.

    Args:
      options: A Namespace that has fields for each parameter that can be
        passed in via the command-line interface.
    Returns:
      A tuple containing the host and port where the application is serving
        traffic from.
    """
    custom_service_yaml = None
    if cls.TAR_GZ_REGEX.search(options.file):
      file_location = LocalState.extract_tgz_app_to_dir(options.file)
      created_dir = True
      version = Version.from_tar_gz(options.file)
    elif cls.ZIP_REGEX.search(options.file):
      file_location = LocalState.extract_zip_app_to_dir(options.file)
      created_dir = True
      version = Version.from_zip(options.file)
    elif os.path.isdir(options.file):
      file_location = options.file
      created_dir = False
      version = Version.from_directory(options.file)
    elif options.file.endswith('.yaml'):
      file_location = os.path.dirname(options.file)
      created_dir = False
      version = Version.from_yaml_file(options.file)
      custom_service_yaml = options.file
    else:
      raise AppEngineConfigException('{0} is not a tar.gz file, a zip file, ' \
        'or a directory. Please try uploading either a tar.gz file, a zip ' \
        'file, or a directory.'.format(options.file))

    if options.project:
      if version.runtime == 'java':
        raise BadConfigurationException("AppScale doesn't support --project for"
          "Java yet. Please specify the application id in appengine-web.xml.")

      version.project_id = options.project

    if version.project_id is None:
      if version.config_type == 'app.yaml':
        message = 'Specify --project or define "application" in your app.yaml'
      else:
        message = 'Define "application" in your appengine-web.xml'

      raise AppEngineConfigException(message)

    # Let users know that versions are not supported yet.
    AppEngineHelper.warn_if_version_defined(version, options.test)

    AppEngineHelper.validate_app_id(version.project_id)

    extras = {}
    if version.runtime == 'go':
      extras = LocalState.get_extra_go_dependencies(options.file, options.test)

    if (version.runtime == 'java'
        and AppEngineHelper.is_sdk_mismatch(file_location)):
      AppScaleLogger.warn(
        'AppScale did not find the correct SDK jar versions in your app. The '
        'current supported SDK version is '
        '{}.'.format(AppEngineHelper.SUPPORTED_SDK_VERSION))

    head_node_public_ip = LocalState.get_host_with_role(
      options.keyname, 'shadow')
    secret_key = LocalState.get_secret_key(options.keyname)
    admin_client = AdminClient(head_node_public_ip, secret_key)

    remote_file_path = RemoteHelper.copy_app_to_host(
      file_location, version.project_id, options.keyname,
      extras, custom_service_yaml)

    AppScaleLogger.log(
      'Deploying service {} for {}'.format(version.service_id,
                                           version.project_id))
    operation_id = admin_client.create_version(version, remote_file_path)

    # now that we've told the AppController to start our app, find out what port
    # the app is running on and wait for it to start serving
    AppScaleLogger.log("Please wait for your app to start serving.")

    deadline = time.time() + cls.MAX_OPERATION_TIME
    while True:
      if time.time() > deadline:
        raise AppScaleException('The deployment operation took too long.')
      operation = admin_client.get_operation(version.project_id, operation_id)
      if not operation['done']:
        time.sleep(1)
        continue

      if 'error' in operation:
        raise AppScaleException(operation['error']['message'])
      version_url = operation['response']['versionUrl']
      break

    AppScaleLogger.success(
      'Your app can be reached at the following URL: {}'.format(version_url))

    if created_dir:
      shutil.rmtree(file_location)

    match = re.match('http://(.+):(\d+)', version_url)
    login_host = match.group(1)
    http_port = int(match.group(2))
    return login_host, http_port
示例#20
0
  def test_from_xml(self):
    # Check the default runtime string for Java apps.
    # TODO: This should be updated when the Admin API accepts 'java7'.
    appengine_web_xml = ElementTree.fromstring(SIMPLE_AE_WEB_XML)
    version = Version.from_xml(appengine_web_xml)
    self.assertEqual(version.runtime, 'java')

    xml_with_project = AE_WEB_XML_TEMPLATE.format(
      '<application>guestbook</application>')
    appengine_web_xml = ElementTree.fromstring(xml_with_project)
    version = Version.from_xml(appengine_web_xml)
    self.assertEqual(version.runtime, 'java')
    self.assertEqual(version.project_id, 'guestbook')

    # Ensure a default service ID is set.
    appengine_web_xml = ElementTree.fromstring(SIMPLE_AE_WEB_XML)
    version = Version.from_xml(appengine_web_xml)
    self.assertEqual(version.service_id, 'default')

    # Ensure service ID is parsed correctly.
    xml_with_module = AE_WEB_XML_TEMPLATE.format('<module>service1</module>')
    appengine_web_xml = ElementTree.fromstring(xml_with_module)
    version = Version.from_xml(appengine_web_xml)
    self.assertEqual(version.service_id, 'service1')

    # Ensure omitted environment variables are handled correctly.
    appengine_web_xml = ElementTree.fromstring(SIMPLE_AE_WEB_XML)
    version = Version.from_xml(appengine_web_xml)
    self.assertDictEqual(version.env_variables, {})

    # Ensure environment variables are parsed correctly.
    env_vars = """
    <env-variables>
      <env-var name="VAR1" value="foo" />
    </env-variables>
    """.lstrip()
    appengine_web_xml = ElementTree.fromstring(
      AE_WEB_XML_TEMPLATE.format(env_vars))
    version = Version.from_xml(appengine_web_xml)
    self.assertDictEqual(version.env_variables, {'VAR1': 'foo'})

    # Ensure omitted inbound services are handled correctly.
    appengine_web_xml = ElementTree.fromstring(SIMPLE_AE_WEB_XML)
    version = Version.from_xml(appengine_web_xml)
    self.assertListEqual(version.inbound_services, [])

    # Ensure inbound services are parsed correctly.
    env_vars = """
    <inbound-services>
      <service>mail</service>
    </inbound-services>
    """.lstrip()
    appengine_web_xml = ElementTree.fromstring(
        AE_WEB_XML_TEMPLATE.format(env_vars))
    version = Version.from_xml(appengine_web_xml)
    self.assertListEqual(version.inbound_services, ['mail'])

    # Check empty threadsafe value.
    appengine_web_xml = ElementTree.fromstring(
      '<?xml version="1.0" encoding="utf-8"?>'
      '<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">'
      '</appengine-web-app>')
    with self.assertRaises(AppEngineConfigException):
      Version.from_xml(appengine_web_xml)

    # Ensure threadsafe value is parsed correctly.
    appengine_web_xml = ElementTree.fromstring(SIMPLE_AE_WEB_XML)
    version = Version.from_xml(appengine_web_xml)
    self.assertEqual(version.threadsafe, True)