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"""Wraps a Cloud Run job message with convenience methods.""" 16 17from __future__ import absolute_import 18from __future__ import division 19from __future__ import unicode_literals 20 21import enum 22from googlecloudsdk.api_lib.run import container_resource 23from googlecloudsdk.api_lib.run import k8s_object 24 25AUTHOR_ANNOTATION = k8s_object.RUN_GROUP + '/creator' 26 27STARTED_CONDITION = 'Started' 28COMPLETED_CONDITION = 'Completed' 29 30 31class RestartPolicy(enum.Enum): 32 NEVER = 'Never' 33 ON_FAILURE = 'OnFailure' 34 35 36class Job(k8s_object.KubernetesObject): 37 """Wraps a Cloud Run job message, making fields more convenient.""" 38 39 API_CATEGORY = 'run.googleapis.com' 40 KIND = 'Job' 41 READY_CONDITION = COMPLETED_CONDITION 42 TERMINAL_CONDITIONS = frozenset({STARTED_CONDITION, READY_CONDITION}) 43 44 @classmethod 45 def New(cls, client, namespace): 46 """Produces a new Job object. 47 48 Args: 49 client: The Cloud Run API client. 50 namespace: str, The serving namespace. 51 52 Returns: 53 A new Job object to be deployed. 54 """ 55 ret = super(Job, cls).New(client, namespace) 56 ret.spec.template.spec.containers = [client.MESSAGES_MODULE.Container()] 57 return ret 58 59 class InstanceTemplateSpec(container_resource.ContainerResource): 60 """Wrapper class for Job subfield InstanceTemplateSpec.""" 61 62 KIND = 'InstanceTemplateSpec' 63 64 @classmethod 65 def SpecAndAnnotationsOnly(cls, job): 66 """Special wrapper for spec only that also covers metadata annotations. 67 68 For a message type without its own metadata, like InstanceTemplateSpec, 69 metadata fields should either raise AttributeErrors or refer to the 70 metadata of a different message depending on use case. This method handles 71 the annotations of metadata by referencing the parent job's annotations. 72 All other metadata fields will fall through to k8s_object which will 73 lead to AttributeErrors. 74 75 Args: 76 job: The parent job for this InstanceTemplateSpec 77 78 Returns: 79 A new k8s_object to wrap the InstanceTemplateSpec with only the spec 80 fields and the metadata annotations. 81 """ 82 spec_wrapper = super(Job.InstanceTemplateSpec, 83 cls).SpecOnly(job.spec.template.spec, 84 job.MessagesModule()) 85 # pylint: disable=protected-access 86 spec_wrapper._annotations = job.annotations 87 return spec_wrapper 88 89 @property 90 def annotations(self): 91 """Override to return the parent job's annotations.""" 92 try: 93 return self._annotations 94 except AttributeError: 95 raise ValueError( 96 'Job templates do not have their own annotations. Initialize the ' 97 'wrapper with SpecAndAnnotationsOnly to be able to use annotations.' 98 ) 99 100 @property 101 def restart_policy(self): 102 """Returns the enum version of the restart policy.""" 103 return RestartPolicy(self.spec.restartPolicy) 104 105 @restart_policy.setter 106 def restart_policy(self, enum_value): 107 self.spec.restartPolicy = enum_value.value 108 109 @property 110 def service_account(self): 111 """The service account to use as the container identity.""" 112 return self.spec.serviceAccountName 113 114 @service_account.setter 115 def service_account(self, value): 116 self.spec.serviceAccountName = value 117 118 @property 119 def template(self): 120 return Job.InstanceTemplateSpec.SpecAndAnnotationsOnly(self) 121 122 @property 123 def author(self): 124 return self.annotations.get(AUTHOR_ANNOTATION) 125 126 @property 127 def image(self): 128 return self.template.image 129 130 @image.setter 131 def image(self, value): 132 self.template.image = value 133 134 @property 135 def parallelism(self): 136 return self.spec.parallelism 137 138 @parallelism.setter 139 def parallelism(self, value): 140 self.spec.parallelism = value 141 142 @property 143 def completions(self): 144 return self.spec.completions 145 146 @completions.setter 147 def completions(self, value): 148 self.spec.completions = value 149 150 @property 151 def backoff_limit(self): 152 return self.spec.backoffLimit 153 154 @backoff_limit.setter 155 def backoff_limit(self, value): 156 self.spec.backoffLimit = value 157