1# -*- coding: utf-8 -*- #
2# Copyright 2015 Google LLC. All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#    http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""The `app instances enable-debug` command."""
17
18from __future__ import absolute_import
19from __future__ import division
20from __future__ import unicode_literals
21
22from googlecloudsdk.api_lib.app import appengine_api_client
23from googlecloudsdk.api_lib.app import env
24from googlecloudsdk.api_lib.app import instances_util
25from googlecloudsdk.calliope import base
26from googlecloudsdk.core import properties
27from googlecloudsdk.core import resources
28from googlecloudsdk.core.console import console_io
29from googlecloudsdk.core.console import progress_tracker
30
31
32class EnableDebug(base.Command):
33  """Enable debug mode for an instance (only works on the flexible environment).
34
35  When in debug mode, SSH will be enabled on the VMs, and you can use
36  `gcloud compute ssh` to login to them. They will be removed from the health
37  checking pools, but they still receive requests.
38
39  Note that any local changes to an instance will be *lost* if debug mode is
40  disabled on the instance. New instance(s) may spawn depending on the app's
41  scaling settings.
42
43  Additionally, debug mode doesn't work for applications using the
44  App Engine standard environment.
45  """
46
47  detailed_help = {
48      'EXAMPLES': """\
49          To enable debug mode for a particular instance, run:
50
51              $ {command} --service=s1 --version=v1 i1
52
53          To enable debug mode for an instance chosen interactively, run:
54
55              $ {command}
56          """,
57  }
58
59  @staticmethod
60  def Args(parser):
61    parser.add_argument(
62        'instance', nargs='?',
63        help="""\
64        Instance ID to enable debug mode on. If not specified,
65        select instance interactively. Must uniquely specify (with other
66        flags) exactly one instance""")
67
68    parser.add_argument(
69        '--service', '-s',
70        help="""\
71        If specified, only match instances belonging to the given service.
72        This affects both interactive and non-interactive selection.""")
73
74    parser.add_argument(
75        '--version', '-v',
76        help="""\
77        If specified, only match instances belonging to the given version.
78        This affects both interactive and non-interactive selection.""")
79
80  def Run(self, args):
81    api_client = appengine_api_client.GetApiClientForTrack(self.ReleaseTrack())
82    all_instances = list(api_client.GetAllInstances(
83        args.service, args.version,
84        version_filter=lambda v: v.environment in [env.FLEX, env.MANAGED_VMS]))
85    try:
86      res = resources.REGISTRY.Parse(args.instance)
87    except Exception:  # pylint:disable=broad-except
88      # If parsing fails, use interactive selection or provided instance ID.
89      instance = instances_util.GetMatchingInstance(
90          all_instances, service=args.service, version=args.version,
91          instance=args.instance)
92    else:
93      instance = instances_util.GetMatchingInstance(
94          all_instances, service=res.servicesId, version=res.versionsId,
95          instance=res.instancesId)
96
97    console_io.PromptContinue(
98        'About to enable debug mode for instance [{0}].'.format(instance),
99        cancel_on_no=True)
100    message = 'Enabling debug mode for instance [{0}]'.format(instance)
101    res = resources.REGISTRY.Parse(
102        instance.id,
103        params={
104            'appsId': properties.VALUES.core.project.GetOrFail,
105            'versionsId': instance.version,
106            'instancesId': instance.id,
107            'servicesId': instance.service,
108        },
109        collection='appengine.apps.services.versions.instances')
110    with progress_tracker.ProgressTracker(message):
111      api_client.DebugInstance(res)
112