コード例 #1
0
def _GetDockerfiles(info, dockerfile_dir):
  """Returns map of in-memory Docker-related files to be packaged.

  Returns the files in-memory, so that we don't have to drop them on disk;
  instead, we include them in the archive sent to App Engine directly.

  Args:
    info: (googlecloudsdk.api_lib.app.yaml_parsing.ServiceYamlInfo)
      The service config.
    dockerfile_dir: str, path to the directory to fingerprint and generate
      Dockerfiles for.

  Raises:
    UnsatisfiedRequirementsError: Raised if the code in the directory doesn't
      satisfy the requirements of the specified runtime type.

  Returns:
    A dictionary of filename relative to the archive root (str) to file contents
    (str).
  """
  params = ext_runtime.Params(appinfo=info.parsed, deploy=True)
  configurator = fingerprinter.IdentifyDirectory(dockerfile_dir, params)
  if configurator:
    dockerfiles = configurator.GenerateConfigData()
    return {d.filename: d.contents for d in dockerfiles}
  else:
    raise UnsatisfiedRequirementsError(
        'Your application does not satisfy all of the requirements for a '
        'runtime of type [{0}].  Please correct the errors and try '
        'again.'.format(info.runtime))
コード例 #2
0
def _GetDockerfileCreator(info):
    """Returns a function to create a dockerfile if the user doesn't have one.

  Args:
    info: (googlecloudsdk.api_lib.app.yaml_parsing.ServiceYamlInfo)
      The service config.

  Raises:
    DockerfileError: Raised if a user supplied a Dockerfile and a non-custom
      runtime.
    NoDockerfileError: Raised if a user didn't supply a Dockerfile and chose a
      custom runtime.
    UnsatisfiedRequirementsError: Raised if the code in the directory doesn't
      satisfy the requirements of the specified runtime type.
  Returns:
    callable(), a function that can be used to create the correct Dockerfile
    later on.
  """
    # Use the path to app.yaml (info.file) to determine the location of the
    # Dockerfile.
    dockerfile_dir = os.path.dirname(info.file)
    dockerfile = os.path.join(dockerfile_dir, 'Dockerfile')

    if info.runtime != 'custom' and os.path.exists(dockerfile):
        raise DockerfileError(
            'There is a Dockerfile in the current directory, and the runtime field '
            'in {0} is currently set to [runtime: {1}]. To use your Dockerfile to '
            'build a custom runtime, set the runtime field in {0} to '
            '[runtime: custom]. To continue using the [{1}] runtime, please omit '
            'the Dockerfile from this directory.'.format(
                info.file, info.runtime))

    # If we're "custom" there needs to be a Dockerfile.
    if info.runtime == 'custom':
        if os.path.exists(dockerfile):
            log.info('Using %s found in %s', config.DOCKERFILE, dockerfile_dir)

            def NullGenerator():
                return lambda: None

            return NullGenerator
        else:
            raise NoDockerfileError(
                'You must provide your own Dockerfile when using a custom runtime.  '
                'Otherwise provide a "runtime" field with one of the supported '
                'runtimes.')

    # Check the fingerprinting based code.
    params = ext_runtime.Params(appinfo=info.parsed, deploy=True)
    configurator = fingerprinter.IdentifyDirectory(dockerfile_dir, params)
    if configurator:
        return configurator.GenerateConfigs
    # Then throw an error.
    else:
        raise UnsatisfiedRequirementsError(
            'Your application does not satisfy all of the requirements for a '
            'runtime of type [{0}].  Please correct the errors and try '
            'again.'.format(info.runtime))
コード例 #3
0
def GenerateConfigs(path, params=None, config_filename=None):
    """Generate all config files for the path into the current directory.

  As a side-effect, if there is a config file in 'params' with a runtime of
  'custom', this sets params.custom to True.

  Args:
    path: (basestring) Root directory to identify.
    params: (ext_runtime.Params or None) Parameters passed through to the
      fingerprinters.  Uses defaults if not provided.
    config_filename: (str or None) Filename of the config file (app.yaml).

  Raises:
    UnidentifiedDirectoryError: No runtime module matched the directory.
    ConflictingConfigError: Current app.yaml conflicts with other params.

  Returns:
    (callable()) Function to remove all generated files (if desired).
  """
    if not params:
        params = ext_runtime.Params()

    config = params.appinfo
    # An app.yaml exists, results in a lot more cases
    if config and not params.deploy:
        # Enforce --custom
        if not params.custom:
            raise ConflictingConfigError(
                'Configuration file already exists. This command generates an '
                'app.yaml configured to run an application on Google App Engine. '
                'To create the configuration files needed to run this '
                'application with docker, try `gcloud preview app gen-config '
                '--custom`.')
        # Check that current config is for MVM
        if not config.IsVm():
            raise ConflictingConfigError(
                'gen-config is only supported for App Engine Managed VMs.  Please '
                'use "vm: true" in your app.yaml if you would like to use Managed '
                'VMs to run your application.')
        # Check for conflicting --runtime and runtime in app.yaml
        if (config.GetEffectiveRuntime() != 'custom'
                and params.runtime is not None
                and params.runtime != config.GetEffectiveRuntime()):
            raise ConflictingConfigError(
                '[{0}] contains "runtime: {1}" which conficts with '
                '--runtime={2}.'.format(config_filename,
                                        config.GetEffectiveRuntime(),
                                        params.runtime))

    module = IdentifyDirectory(path, params)
    if not module:
        raise UnidentifiedDirectoryError(path)

    try:
        return module.GenerateConfigs()
    except ext_runtime.Error as ex:
        raise ExtRuntimeError(ex.message)
コード例 #4
0
 def GetConfigurator(self, deploy, create_path=True):
     rt = ext_runtime.ExternalizedRuntime.Load(self.runtime_def_dir,
                                               self.env)
     params = ext_runtime.Params(deploy=deploy)
     self.Touch(directory=self.temp_path,
                name='exists',
                contents='my contents',
                create_path=create_path)
     configurator = rt.Detect(self.temp_path, params)
     return configurator
コード例 #5
0
    def testGenerate(self):
        def Fix(unix_path):
            return ext_runtime._NormalizePath('X', unix_path)

        rt = ext_runtime.ExternalizedRuntime(
            Fix('/runtime/def/root'), {
                'generate_configs': {
                    'files_to_copy': ['data/foo', 'data/bar', 'data/exists']
                }
            }, self.env)
        params = ext_runtime.Params()
        cfg = ext_runtime.ExternalRuntimeConfigurator(rt, params, {}, None,
                                                      Fix('/dest/path'),
                                                      self.env)
        copied = []

        # pylint:disable=unused-argument
        def ExistsFake(filename):
            return filename.endswith('exists')

        def IsFileFake(filename):
            return True

        def OpenFake(filename, mode):
            openfile = mock.mock_open(read_data='contents')
            f = openfile()

            def WriteFake(contents):
                copied.append(filename)

            f.write.side_effect = WriteFake
            return f

        with mock.patch.object(os.path, 'exists', new=ExistsFake):
            with mock.patch.object(os.path, 'isfile', new=IsFileFake):
                with mock.patch('gae_ext_runtime.ext_runtime.open',
                                OpenFake,
                                create=True):
                    cfg.GenerateConfigs()
                    self.assertEqual(
                        sorted(copied),
                        [Fix('dest/path/data/bar'),
                         Fix('dest/path/data/foo')])

        self.assertIn(('print',
                       ext_runtime.WRITING_FILE_MESSAGE.format(
                           'data/foo', Fix('/dest/path'))), self.log)
        self.assertIn(('print',
                       ext_runtime.WRITING_FILE_MESSAGE.format(
                           'data/bar', Fix('/dest/path'))), self.log)
        self.assertIn(
            ('print', ext_runtime.FILE_EXISTS_MESSAGE.format('data/exists')),
            self.log)
コード例 #6
0
def _GetDockerfiles(info, dockerfile_dir):
    """Returns file objects to create dockerfiles if the user doesn't have them.

  Args:
    info: (googlecloudsdk.api_lib.app.yaml_parsing.ServiceYamlInfo)
      The service config.
    dockerfile_dir: str, path to the directory with the Dockerfile
  Raises:
    DockerfileError: Raised if a user supplied a Dockerfile and a non-custom
      runtime.
    NoDockerfileError: Raised if a user didn't supply a Dockerfile and chose a
      custom runtime.
    UnsatisfiedRequirementsError: Raised if the code in the directory doesn't
      satisfy the requirements of the specified runtime type.
  Returns:
    A dictionary of filename to (str) Dockerfile contents.
  """
    dockerfile = os.path.join(dockerfile_dir, 'Dockerfile')

    if info.runtime != 'custom' and os.path.exists(dockerfile):
        raise DockerfileError(
            'There is a Dockerfile in the current directory, and the runtime field '
            'in {0} is currently set to [runtime: {1}]. To use your Dockerfile to '
            'build a custom runtime, set the runtime field in {0} to '
            '[runtime: custom]. To continue using the [{1}] runtime, please omit '
            'the Dockerfile from this directory.'.format(
                info.file, info.runtime))

    # If we're "custom" there needs to be a Dockerfile.
    if info.runtime == 'custom':
        if os.path.exists(dockerfile):
            log.info('Using %s found in %s', config.DOCKERFILE, dockerfile_dir)
            return {}
        else:
            raise NoDockerfileError(
                'You must provide your own Dockerfile when using a custom runtime.  '
                'Otherwise provide a "runtime" field with one of the supported '
                'runtimes.')

    # Check the fingerprinting based code.
    gen_files = {}
    params = ext_runtime.Params(appinfo=info.parsed, deploy=True)
    configurator = fingerprinter.IdentifyDirectory(dockerfile_dir, params)
    if configurator:
        dockerfiles = configurator.GenerateConfigData()
        gen_files.update((d.filename, d.contents) for d in dockerfiles)
        return gen_files
    # Then throw an error.
    else:
        raise UnsatisfiedRequirementsError(
            'Your application does not satisfy all of the requirements for a '
            'runtime of type [{0}].  Please correct the errors and try '
            'again.'.format(info.runtime))
コード例 #7
0
 def testGenerateBadFile(self):
     rt = ext_runtime.ExternalizedRuntime(
         '/runtime/def/root',
         {'generate_configs': {
             'files_to_copy': ['data/foo', 'data/bar']
         }}, self.env)
     params = ext_runtime.Params()
     cfg = ext_runtime.ExternalRuntimeConfigurator(rt, params, {}, None,
                                                   '/dest/path', self.env)
     with mock.patch.object(os.path, 'isfile', new=lambda path: False):
         with self.assertRaises(ext_runtime.InvalidRuntimeDefinition):
             cfg.GenerateConfigs()
         with self.assertRaises(ext_runtime.InvalidRuntimeDefinition):
             cfg.GenerateConfigData()
コード例 #8
0
def _GetModule(path, params=None, config_filename=None):
    """Helper function for generating configs.

  Args:
    path: (basestring) Root directory to identify.
    params: (ext_runtime.Params or None) Parameters passed through to the
      fingerprinters.  Uses defaults if not provided.
    config_filename: (str or None) Filename of the config file (app.yaml).

  Raises:
    UnidentifiedDirectoryError: No runtime module matched the directory.
    ConflictingConfigError: Current app.yaml conflicts with other params.

  Returns:
    ext_runtime.Configurator, the configurator for the path
  """
    if not params:
        params = ext_runtime.Params()

    config = params.appinfo
    # An app.yaml exists, results in a lot more cases
    if config and not params.deploy:
        # Enforce --custom
        if not params.custom:
            raise ConflictingConfigError(
                'Configuration file already exists. This command generates an '
                'app.yaml configured to run an application on Google App Engine. '
                'To create the configuration files needed to run this '
                'application with docker, try `gcloud preview app gen-config '
                '--custom`.')
        # Check that current config is for MVM
        if not config.IsVm():
            raise ConflictingConfigError(
                'gen-config is only supported for App Engine Flexible.  Please '
                'use "vm: true" in your app.yaml if you would like to use App Engine '
                'Flexible to run your application.')
        # Check for conflicting --runtime and runtime in app.yaml
        if (config.GetEffectiveRuntime() != 'custom'
                and params.runtime is not None
                and params.runtime != config.GetEffectiveRuntime()):
            raise ConflictingConfigError(
                '[{0}] contains "runtime: {1}" which conficts with '
                '--runtime={2}.'.format(config_filename,
                                        config.GetEffectiveRuntime(),
                                        params.runtime))

    module = IdentifyDirectory(path, params)
    if not module:
        raise UnidentifiedDirectoryError(path)
    return module
コード例 #9
0
    def testRunPlugin(self):
        rt = ext_runtime.ExternalizedRuntime.Load(self.runtime_def_dir,
                                                  self.env)
        rt.RunPlugin('fake_section_name', {'python': 'bin/plugin1'},
                     ext_runtime.Params())

        # This output comes from ./testdata/runtime_def/bin/plugin1
        self.assertEqual(
            self.log, [('info', 'fake_section_name: this should go to info'),
                       ('msg', {
                           'type': 'info',
                           'message': 'this should also go to info'
                       })])
        self.assertEqual(
            self.err,
            [('warn', 'fake_section_name: this should go to warning')])
コード例 #10
0
def IdentifyDirectory(path, params=None):
    """Try to identify the given directory.

  As a side-effect, if there is a config file in 'params' with a runtime of
  'custom', this sets params.custom to True.

  Args:
    path: (basestring) Root directory to identify.
    params: (ext_runtime.Params or None) Parameters passed through to the
      fingerprinters.  Uses defaults if not provided.

  Returns:
    (ext_runtime.Configurator or None) Returns a module if we've identified
    it, None if not.
  """
    if not params:
        params = ext_runtime.Params()

    # Parameter runtime has precedence
    if params.runtime:
        specified_runtime = params.runtime
    elif params.appinfo:
        specified_runtime = params.appinfo.GetEffectiveRuntime()
    else:
        specified_runtime = None

    if specified_runtime == 'custom':
        params.custom = True

    for runtime in RUNTIMES:

        # If we have an app.yaml, don't fingerprint for any runtimes that don't
        # allow the runtime name it specifies.
        if (specified_runtime and runtime.ALLOWED_RUNTIME_NAMES
                and specified_runtime not in runtime.ALLOWED_RUNTIME_NAMES):
            log.info('Not checking for [%s] because runtime is [%s]' %
                     (runtime.NAME, specified_runtime))
            continue

        try:
            configurator = runtime.Fingerprint(path, params)
        except ext_runtime.Error as ex:
            raise ExtRuntimeError(ex.message)
        if configurator:
            return configurator
    return None
コード例 #11
0
def _Run(args):
    """Run the `gcloud app gen-config` command."""
    if args.config:
        # If the user has specified an config file, use that.
        config_filename = args.config
    else:
        # Otherwise, check for an app.yaml in the source directory.
        config_filename = os.path.join(args.source_dir, 'app.yaml')
        if not os.path.exists(config_filename):
            config_filename = None

    # If there was an config file either specified by the user or in the source
    # directory, load it.
    if config_filename:
        try:
            myi = yaml_parsing.ServiceYamlInfo.FromFile(config_filename)
            config = myi.parsed
        except IOError as ex:
            log.error('Unable to open %s: %s', config_filename, ex)
            return
    else:
        config = None

    fingerprinter.GenerateConfigs(
        args.source_dir,
        ext_runtime.Params(appinfo=config,
                           custom=args.custom,
                           runtime=args.runtime), config_filename)

    # If the user has a config file, make sure that they're using a custom
    # runtime.
    # TODO(b/36050339): If --config is given, should it still be modified?
    if config and args.custom and config.GetEffectiveRuntime() != 'custom':
        alter = console_io.PromptContinue(
            default=False,
            message=output_helpers.RUNTIME_MISMATCH_MSG.format(
                config_filename),
            prompt_string='Would you like to update it now?')
        if alter:
            _AlterRuntime(config_filename, 'custom')
            log.status.Print('[{0}] has been updated.'.format(config_filename))
        else:
            log.status.Print(
                'Please update [{0}] manually by changing the runtime '
                'field to custom.'.format(config_filename))
コード例 #12
0
    def testDetect(self):
        rt = ext_runtime.ExternalizedRuntime.Load(self.runtime_def_dir,
                                                  self.env)
        params = ext_runtime.Params()
        configurator = rt.Detect(self.temp_path, params)
        self.assertIsInstance(configurator,
                              ext_runtime.ExternalRuntimeConfigurator)
        self.assertEqual(configurator.runtime, rt)
        self.assertEqual(configurator.params, params)
        self.assertEqual(
            configurator.data, {
                'a': 'got data',
                'test_string': 'test value',
                'user_data': 'user response'
            })

        # Make sure the prompt gave us back what we expect.
        self.assertIn('gimme some data', self.prompt_message)
        self.assertIn('reasonable default', self.prompt_message)
コード例 #13
0
def CreateAppYamlForAppDirectory(directory):
    """Ensures that an app.yaml exists or creates it if necessary.

  Attempt to fingerprint the directory and create one. This is an interactive
  process. If this does not raise an error, the app.yaml is guaranteed to exist
  once this is done.

  Args:
    directory: str, The path to the directory to create the app.yaml in.

  Raises:
    NoAppIdentifiedError, If the application type could not be identified, or
        if a yaml file could not be generated based on the state of the source.

  Returns:
    str, The path to the created app.yaml file.
  """
    console_io.PromptContinue(
        'Deployment to Google App Engine requires an app.yaml file. '
        'This command will run `gcloud beta app gen-config` to generate an '
        'app.yaml file for you in the current directory (if the current '
        'directory does not contain an App Engine service, please answer '
        '"no").',
        cancel_on_no=True)
    # This indicates we don't have an app.yaml, we do not want to generate
    # docker files (we will do that in a single place later), and that we don't
    # want to persist the dockerfiles.
    params = ext_runtime.Params(appinfo=None, deploy=False, custom=False)
    configurator = fingerprinter.IdentifyDirectory(directory, params=params)
    if configurator is None:
        raise app_exc.NoAppIdentifiedError(
            'Could not identify an app in the current directory.\n\n'
            'Please prepare an app.yaml file for your application manually '
            'and deploy again.')
    configurator.MaybeWriteAppYaml()
    yaml_path = os.path.join(directory, DEFAULT_DEPLOYABLE)
    if not os.path.exists(yaml_path):
        raise app_exc.NoAppIdentifiedError(
            'Failed to create an app.yaml for your app.\n\n'
            'Please prepare an app.yaml file for your application manually '
            'and deploy again.')
    return yaml_path
コード例 #14
0
ファイル: testutil.py プロジェクト: AlexisMarie8330/Doll
    def maybe_get_configurator(self, params=None, **kwargs):
        """Load the runtime definition.

        Args:
            params: (ext_runtime.Params) Runtime parameters.  DEPRECATED.
                Use the keyword args, instead.
            **kwargs: ({str: object, ...}) If specified, these are the
                arguments to the ext_runtime.Params() constructor
                (valid args are at this time are: appinfo, custom and deploy,
                check ext_runtime.Params() for full details)

        Returns:
            configurator or None if configurator didn't match
        """
        rt = ext_runtime.ExternalizedRuntime.Load(self.runtime_def_root,
                                                  self.exec_env)
        params = params or ext_runtime.Params(**kwargs)
        print params.ToDict()
        configurator = rt.Detect(self.temp_path, params)
        return configurator
コード例 #15
0
ファイル: testutil.py プロジェクト: AlexisMarie8330/Doll
    def detect(self, params=None, **kwargs):
        """Load the runtime definition and generate configs from it.

        Args:
            params: (ext_runtime.Params) Runtime parameters.  DEPRECATED.
                Use the keyword args, instead.
            **kwargs: ({str: object, ...}) If specified, these are the
                arguments to the ext_runtime.Params() constructor
                (valid args are at this time are: appinfo, custom and deploy,
                check ext_runtime.Params() for full details)

        Returns:
            (ext_runtime.Configurator or None) the identified runtime if found,
            None if not.
        """
        rt = ext_runtime.ExternalizedRuntime.Load(self.runtime_def_root,
                                                  self.exec_env)
        params = params or ext_runtime.Params(**kwargs)
        configurator = rt.Detect(self.temp_path, params)

        return configurator
コード例 #16
0
    def testGenerateNoWrite(self):
        """Test the ext_runtime generates contents of config files correctly."""
        def Fix(unix_path):
            return ext_runtime._NormalizePath('X', unix_path)

        rt = ext_runtime.ExternalizedRuntime(
            Fix('/runtime/def/root'), {
                'generate_configs': {
                    'files_to_copy': ['data/foo', 'data/bar', 'data/exists']
                }
            }, self.env)
        params = ext_runtime.Params()
        cfg = ext_runtime.ExternalRuntimeConfigurator(rt, params, {}, None,
                                                      Fix('/dest/path'),
                                                      self.env)

        # pylint:disable=unused-argument
        def ExistsFake(filename):
            return filename.endswith('exists')

        def IsFileFake(filename):
            return True

        def OpenFake(filename, mode):
            openfile = mock.mock_open(read_data='contents')
            return openfile()

        with mock.patch.object(os.path, 'exists', new=ExistsFake):
            with mock.patch.object(os.path, 'isfile', new=IsFileFake):
                with mock.patch('gae_ext_runtime.ext_runtime.open',
                                OpenFake,
                                create=True):
                    cfg_files = cfg.GenerateConfigData()
                    self.assertEqual(
                        ['contents', 'contents'],
                        [cfg_file.contents for cfg_file in cfg_files])
                    self.assertEqual(
                        {'data/bar', 'data/foo'},
                        {cfg_file.filename
                         for cfg_file in cfg_files})
コード例 #17
0
 def testMaybeWriteAppYaml_AppYamlAlreadyExists(self):
     """Tests that file exists message is printed if app.yaml exists."""
     rt = ext_runtime.ExternalizedRuntime.Load(self.runtime_def_dir,
                                               self.env)
     runtime_config = yaml.load(
         textwrap.dedent("""\
     runtime: python
     env: 2
     entrypoint: run_me_some_python!
     handlers:
     - url: .*
       script: request
     """))
     params = ext_runtime.Params()
     self.Touch(directory=self.temp_path,
                name='app.yaml',
                contents='my contents')
     configurator = rt.Detect(self.temp_path, params)
     configurator.SetGeneratedAppInfo(runtime_config)
     configurator.MaybeWriteAppYaml()
     self.assertIn(
         ('print', ext_runtime.FILE_EXISTS_MESSAGE.format('app.yaml')),
         self.log)
コード例 #18
0
 def testPluginNotFound(self):
     rt = ext_runtime.ExternalizedRuntime.Load(self.runtime_def_dir,
                                               self.env)
     with self.assertRaises(ext_runtime.PluginInvocationFailed):
         rt.RunPlugin('fake_section_name', {'python': 'bin/does_not_exist'},
                      ext_runtime.Params())
コード例 #19
0
    def Run(self, args):
        project = properties.VALUES.core.project.Get(required=True)
        version = args.version or util.GenerateVersionId()
        use_cloud_build = properties.VALUES.app.use_cloud_build.GetBool()

        config_cleanup = None
        if args.deployables:
            app_config = yaml_parsing.AppConfigSet(args.deployables)
        else:
            if not os.path.exists(DEFAULT_DEPLOYABLE):
                console_io.PromptContinue(
                    'Deployment to Google App Engine requires an app.yaml file. '
                    'This command will run `gcloud preview app gen-config` to generate '
                    'an app.yaml file for you in the current directory (if the current '
                    'directory does not contain an App Engine module, please answer '
                    '"no").',
                    cancel_on_no=True)
                # This generates the app.yaml AND the Dockerfile (and related files).
                params = ext_runtime.Params(deploy=True)
                configurator = fingerprinter.IdentifyDirectory(os.getcwd(),
                                                               params=params)
                if configurator is None:
                    raise NoAppIdentifiedError(
                        'Could not identify an app in the current directory.\n\n'
                        'Please prepare an app.yaml file for your application manually '
                        'and deploy again.')
                config_cleanup = configurator.GenerateConfigs()
                log.status.Print(
                    '\nCreated [{0}] in the current directory.\n'.format(
                        DEFAULT_DEPLOYABLE))
            app_config = yaml_parsing.AppConfigSet([DEFAULT_DEPLOYABLE])

        # If the app has enabled Endpoints API Management features, pass
        # control to the cloud_endpoints handler.
        for _, module in app_config.Modules().items():
            if module and module.parsed and module.parsed.beta_settings:
                bs = module.parsed.beta_settings
                use_endpoints = bs.get('use_endpoints_api_management',
                                       '').lower()
                if (use_endpoints in ('true', '1', 'yes')
                        and bs.get('endpoints_swagger_spec_file')):
                    cloud_endpoints.PushServiceConfig(
                        bs.get('endpoints_swagger_spec_file'), project,
                        apis.GetClientInstance('servicemanagement', 'v1',
                                               self.Http()),
                        apis.GetMessagesModule('servicemanagement', 'v1'))

        remote_build = True
        docker_build_property = properties.VALUES.app.docker_build.Get()
        if args.docker_build:
            remote_build = args.docker_build == 'remote'
        elif docker_build_property:
            remote_build = docker_build_property == 'remote'

        clients = _AppEngineClients(
            appengine_client.AppengineClient(args.server,
                                             args.ignore_bad_certs),
            appengine_api_client.GetApiClient(self.Http(timeout=None)))
        log.debug(
            'API endpoint: [{endpoint}], API version: [{version}]'.format(
                endpoint=clients.api.client.url,
                version=clients.api.api_version))
        cloudbuild_client = apis.GetClientInstance('cloudbuild', 'v1',
                                                   self.Http())
        storage_client = apis.GetClientInstance('storage', 'v1', self.Http())
        promote = properties.VALUES.app.promote_by_default.GetBool()
        deployed_urls = _DisplayProposedDeployment(project, app_config,
                                                   version, promote)
        if args.version or promote:
            # Prompt if there's a chance that you're overwriting something important:
            # If the version is set manually, you could be deploying over something.
            # If you're setting the new deployment to be the default version, you're
            # changing the target of the default URL.
            # Otherwise, all existing URLs will continue to work, so need to prompt.
            console_io.PromptContinue(default=True,
                                      throw_if_unattended=False,
                                      cancel_on_no=True)

        log.status.Print('Beginning deployment...')

        source_contexts = []
        if args.repo_info_file:
            if args.image_url:
                raise NoRepoInfoWithImageUrlError()

            try:
                with open(args.repo_info_file, 'r') as f:
                    source_contexts = json.load(f)
            except (ValueError, IOError) as ex:
                raise RepoInfoLoadError(args.repo_info_file, ex)
            if isinstance(source_contexts, dict):
                # This is an old-style source-context.json file. Convert to a new-
                # style array of extended contexts.
                source_contexts = [
                    context_util.ExtendContextDict(source_contexts)
                ]

        code_bucket_ref = None
        if use_cloud_build or app_config.NonHermeticModules():
            # If using Argo CloudBuild, we'll need to upload source to a GCS bucket.
            code_bucket_ref = self._GetCodeBucket(clients.api, args)
            metrics.CustomTimedEvent(metric_names.GET_CODE_BUCKET)
            log.debug('Using bucket [{b}].'.format(b=code_bucket_ref))

        modules = app_config.Modules()
        if any([m.RequiresImage() for m in modules.values()]):
            deploy_command_util.DoPrepareManagedVms(clients.gae)
        if args.image_url:
            if len(modules) != 1:
                raise MultiDeployError()
            for registry in constants.ALL_SUPPORTED_REGISTRIES:
                if args.image_url.startswith(registry):
                    break
            else:
                raise UnsupportedRegistryError(args.image_url)
            module = modules.keys()[0]
            images = {module: args.image_url}
        else:
            images = deploy_command_util.BuildAndPushDockerImages(
                modules, version, cloudbuild_client, storage_client,
                self.Http(), code_bucket_ref, self.cli, remote_build,
                source_contexts, config_cleanup)

        deployment_manifests = {}
        if app_config.NonHermeticModules():
            if properties.VALUES.app.use_gsutil.GetBool():
                copy_func = deploy_app_command_util.CopyFilesToCodeBucket
                metric_name = metric_names.COPY_APP_FILES
            else:
                copy_func = deploy_app_command_util.CopyFilesToCodeBucketNoGsUtil
                metric_name = metric_names.COPY_APP_FILES_NO_GSUTIL

            deployment_manifests = copy_func(
                app_config.NonHermeticModules().items(), code_bucket_ref,
                source_contexts, storage_client)
            metrics.CustomTimedEvent(metric_name)

        all_services = clients.api.ListServices()
        # Now do deployment.
        for (module, info) in app_config.Modules().iteritems():
            message = 'Updating module [{module}]'.format(module=module)
            with console_io.ProgressTracker(message):
                if args.force:
                    log.warning(
                        'The --force argument is deprecated and no longer '
                        'required. It will be removed in a future release.')

                clients.api.DeployModule(module, version, info,
                                         deployment_manifests.get(module),
                                         images.get(module))
                metrics.CustomTimedEvent(metric_names.DEPLOY_API)

                stop_previous_version = (
                    deploy_command_util.GetStopPreviousVersionFromArgs(args))
                if promote:
                    new_version = version_util.Version(project, module,
                                                       version)
                    _Promote(all_services, new_version, clients,
                             stop_previous_version)
                elif stop_previous_version:
                    log.info(
                        'Not stopping previous version because new version was not '
                        'promoted.')

        # Config files.
        for (c, info) in app_config.Configs().iteritems():
            message = 'Updating config [{config}]'.format(config=c)
            with console_io.ProgressTracker(message):
                clients.gae.UpdateConfig(c, info.parsed)
        return deployed_urls
コード例 #20
0
def _GetDockerfileCreator(info, config_cleanup=None):
    """Returns a function to create a dockerfile if the user doesn't have one.

  Args:
    info: (googlecloudsdk.api_lib.app.yaml_parsing.ModuleYamlInfo)
      The module config.
    config_cleanup: (callable() or None) If a temporary Dockerfile has already
      been created during the course of the deployment, this should be a
      callable that deletes it.

  Raises:
    DockerfileError: Raised if a user supplied a Dockerfile and a non-custom
      runtime.
    NoDockerfileError: Raised if a user didn't supply a Dockerfile and chose a
      custom runtime.
    UnsupportedRuntimeError: Raised if we can't detect a runtime.
  Returns:
    callable(), a function that can be used to create the correct Dockerfile
    later on.
  """
    # Use the path to app.yaml (info.file) to determine the location of the
    # Dockerfile.
    dockerfile_dir = os.path.dirname(info.file)
    dockerfile = os.path.join(dockerfile_dir, 'Dockerfile')

    if config_cleanup:
        # Dockerfile has already been generated. It still needs to be cleaned up.
        # This must be before the other conditions, since it's a special case.
        return lambda: config_cleanup

    if info.runtime != 'custom' and os.path.exists(dockerfile):
        raise DockerfileError(
            'There is a Dockerfile in the current directory, and the runtime field '
            'in {0} is currently set to [runtime: {1}]. To use your Dockerfile to '
            'build a custom runtime, set the runtime field in {0} to '
            '[runtime: custom]. To continue using the [{1}] runtime, please omit '
            'the Dockerfile from this directory.'.format(
                info.file, info.runtime))

    # If we're "custom" there needs to be a Dockerfile.
    if info.runtime == 'custom':
        if os.path.exists(dockerfile):
            log.info('Using %s found in %s', config.DOCKERFILE, dockerfile_dir)

            def NullGenerator():
                return lambda: None

            return NullGenerator
        else:
            raise NoDockerfileError(
                'You must provide your own Dockerfile when using a custom runtime.  '
                'Otherwise provide a "runtime" field with one of the supported '
                'runtimes.')

    # Check the fingerprinting based code.
    params = ext_runtime.Params(appinfo=info.parsed, deploy=True)
    configurator = fingerprinter.IdentifyDirectory(dockerfile_dir, params)
    if configurator:
        return configurator.GenerateConfigs
    # Then throw an error.
    else:
        raise UnsupportedRuntimeError(
            'We were unable to detect the runtime to use for this application. '
            'Please specify the [runtime] field in your application yaml file '
            'or check that your application is configured correctly.')