1# -*- coding: utf-8 -*- #
2# Copyright 2020 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"""Service-specific printer."""
17
18from __future__ import absolute_import
19from __future__ import division
20from __future__ import print_function
21from __future__ import unicode_literals
22
23import collections
24
25from googlecloudsdk.api_lib.run import container_resource
26from googlecloudsdk.api_lib.run import revision
27from googlecloudsdk.command_lib.run import k8s_object_printer
28from googlecloudsdk.core.resource import custom_printer_base as cp
29import six
30
31
32REVISION_PRINTER_FORMAT = 'revision'
33
34
35def FormatSecretKeyRef(v):
36  return '{}:{}'.format(v.secretKeyRef.name, v.secretKeyRef.key)
37
38
39def FormatSecretVolumeSource(v):
40  if v.items:
41    return '{}:{}'.format(v.secretName, v.items[0].key)
42  else:
43    return v.secretName
44
45
46def FormatConfigMapKeyRef(v):
47  return '{}:{}'.format(v.configMapKeyRef.name, v.configMapKeyRef.key)
48
49
50def FormatConfigMapVolumeSource(v):
51  if v.items:
52    return '{}:{}'.format(v.name, v.items[0].key)
53  else:
54    return v.name
55
56
57class RevisionPrinter(k8s_object_printer.K8sObjectPrinter):
58  """Prints the run Revision in a custom human-readable format.
59
60  Format specific to Cloud Run revisions. Only available on Cloud Run commands
61  that print revisions.
62  """
63
64  def Transform(self, record):
65    """Transform a service into the output structure of marker classes."""
66    fmt = cp.Lines([
67        self._GetHeader(record),
68        self._GetLabels(record.labels),
69        ' ',
70        self.TransformSpec(record),
71        self._GetReadyMessage(record)])
72    return fmt
73
74  @staticmethod
75  def GetLimits(rev):
76    return collections.defaultdict(str, rev.resource_limits)
77
78  @staticmethod
79  def GetUserEnvironmentVariables(record):
80    return cp.Mapped(
81        k8s_object_printer.OrderByKey(
82            record.env_vars.literals))
83
84  @staticmethod
85  def GetSecrets(record):
86    secrets = {}
87    secrets.update({
88        k: FormatSecretKeyRef(v)
89        for k, v in record.env_vars.secrets.items()
90    })
91    secrets.update({
92        k: FormatSecretVolumeSource(v)
93        for k, v in record.MountedVolumeJoin('secrets').items()
94    })
95    return cp.Mapped(k8s_object_printer.OrderByKey(secrets))
96
97  @staticmethod
98  def GetConfigMaps(record):
99    config_maps = {}
100    config_maps.update({
101        k: FormatConfigMapKeyRef(v)
102        for k, v in record.env_vars.config_maps.items()
103    })
104    config_maps.update({
105        k: FormatConfigMapVolumeSource(v)
106        for k, v in record.MountedVolumeJoin('config_maps').items()
107    })
108    return cp.Mapped(k8s_object_printer.OrderByKey(config_maps))
109
110  @staticmethod
111  def GetCloudSqlInstances(record):
112    instances = record.annotations.get(container_resource.CLOUDSQL_ANNOTATION,
113                                       '')
114    return instances.replace(',', ', ')
115
116  @staticmethod
117  def GetTimeout(record):
118    if record.timeout is not None:
119      return '{}s'.format(record.timeout)
120    return None
121
122  @staticmethod
123  def GetVpcConnector(record):
124    return cp.Labeled([
125        ('Name',
126         record.annotations.get(container_resource.VPC_ACCESS_ANNOTATION, '')),
127        ('Egress',
128         record.annotations.get(container_resource.EGRESS_SETTINGS_ANNOTATION,
129                                ''))
130    ])
131
132  @staticmethod
133  def GetMinInstances(record):
134    return record.annotations.get(revision.MIN_SCALE_ANNOTATION, '')
135
136  @staticmethod
137  def GetMaxInstances(record):
138    return record.annotations.get(revision.MAX_SCALE_ANNOTATION, '')
139
140  @staticmethod
141  def TransformSpec(record):
142    limits = RevisionPrinter.GetLimits(record)
143    return cp.Labeled([
144        ('Image', record.UserImage()),
145        ('Command', ' '.join(record.container.command)),
146        ('Args', ' '.join(record.container.args)),
147        ('Port', ' '.join(
148            six.text_type(p.containerPort)
149            for p in record.container.ports)),
150        ('Memory', limits['memory']),
151        ('CPU', limits['cpu']),
152        ('Service account', record.spec.serviceAccountName),
153        ('Env vars', RevisionPrinter.GetUserEnvironmentVariables(record)),
154        ('Secrets', RevisionPrinter.GetSecrets(record)),
155        ('Config Maps', RevisionPrinter.GetConfigMaps(record)),
156        ('Concurrency', record.concurrency),
157        ('Min Instances', RevisionPrinter.GetMinInstances(record)),
158        ('Max Instances', RevisionPrinter.GetMaxInstances(record)),
159        ('SQL connections', RevisionPrinter.GetCloudSqlInstances(record)),
160        ('Timeout', RevisionPrinter.GetTimeout(record)),
161        ('VPC connector', RevisionPrinter.GetVpcConnector(record)),
162    ])
163