def CreateDomainMapping(self, domain_mapping_ref, service_name): """Create a domain mapping. Args: domain_mapping_ref: Resource, domainmapping resource. service_name: str, the service to which to map domain. Returns: A domain_mapping.DomainMapping object. """ messages = self._messages_module new_mapping = domain_mapping.DomainMapping.New( self._client, domain_mapping_ref.namespacesId) new_mapping.name = domain_mapping_ref.domainmappingsId new_mapping.route_name = service_name request = messages.RunNamespacesDomainmappingsCreateRequest( domainMapping=new_mapping.Message(), parent=domain_mapping_ref.Parent().RelativeName()) with metrics.RecordDuration(metric_names.CREATE_DOMAIN_MAPPING): response = self._client.namespaces_domainmappings.Create(request) # 'run domain-mappings create' is synchronous. Poll for its completion. getter = functools.partial(self.GetDomainMapping, domain_mapping_ref) with progress_tracker.StagedProgressTracker( 'Creating...', stages.DomainMappingStages(), failure_message='Domain mapping failed') as tracker: self.WaitForCondition(DomainMappingConditionPoller( getter, tracker)) return domain_mapping.DomainMapping(response, messages)
def GetConfiguration(self, service_or_configuration_ref): """Return the relevant Configuration from the server, or None if 404.""" messages = self.messages_module if hasattr(service_or_configuration_ref, 'servicesId'): name = self._registry.Parse( service_or_configuration_ref.servicesId, params={ 'namespacesId': service_or_configuration_ref.namespacesId, }, collection='run.namespaces.configurations').RelativeName() else: name = service_or_configuration_ref.RelativeName() configuration_get_request = ( messages.RunNamespacesConfigurationsGetRequest( name=name)) try: with metrics.RecordDuration(metric_names.GET_CONFIGURATION): configuration_get_response = self._client.namespaces_configurations.Get( configuration_get_request) return configuration.Configuration(configuration_get_response, messages) except api_exceptions.InvalidDataFromServerError as e: serverless_exceptions.MaybeRaiseCustomFieldMismatch(e) except api_exceptions.HttpNotFoundError: return None
def CreateTrigger(self, trigger_ref, source_obj, event_type, target_service): """Create a trigger that sends events to the target service. Args: trigger_ref: googlecloudsdk.core.resources.Resource, trigger resource. source_obj: source.Source. The source object to be created after the trigger. event_type: custom_resource_definition.EventTypeDefinition, the event type the source will filter by. target_service: str, name of the Cloud Run service to subscribe. Returns: trigger.Trigger of the created trigger. """ trigger_obj = trigger.Trigger.New(self._client, trigger_ref.Parent().Name()) trigger_obj.name = trigger_ref.Name() trigger_obj.dependency = source_obj # TODO(b/141617597): Set to str(random.random()) without prepended string trigger_obj.filter_attributes[ trigger.SOURCE_TRIGGER_LINK_FIELD] = 'link{}'.format(random.random()) trigger_obj.filter_attributes[ trigger.EVENT_TYPE_FIELD] = event_type.type trigger_obj.subscriber = target_service request = self.messages.RunNamespacesTriggersCreateRequest( trigger=trigger_obj.Message(), parent=trigger_ref.Parent().RelativeName()) with metrics.RecordDuration(metric_names.CREATE_TRIGGER): try: response = self._client.namespaces_triggers.Create(request) except api_exceptions.HttpConflictError: raise exceptions.TriggerCreationError( 'Trigger [{}] already exists.'.format(trigger_obj.name)) return trigger.Trigger(response, self.messages)
def ListSourceCustomResourceDefinitions(self): """Returns a list of CRDs for event sources.""" # Passing the parent field is only needed for hosted, but shouldn't hurt # against an actual cluster namespace_ref = resources.REGISTRY.Parse( properties.VALUES.core.project.Get(), collection='anthosevents.namespaces', api_version=self._api_version) messages = self._crd_client.MESSAGES_MODULE request = messages.AnthoseventsCustomresourcedefinitionsListRequest( parent=namespace_ref.RelativeName(), labelSelector=_EVENT_SOURCES_LABEL_SELECTOR) with metrics.RecordDuration(metric_names.LIST_SOURCE_CRDS): response = self._crd_client.customresourcedefinitions.List(request) source_crds = [ custom_resource_definition.SourceCustomResourceDefinition( item, messages) for item in response.items ] # The customresourcedefinition received in listResponse is missing their # apiVersion's (intended), so manually insert apiVersion from listResponse. for source_crd in source_crds: source_crd.setApiVersion(response.apiVersion) # Only include CRDs for source kinds that are defined in the api. return [ s for s in source_crds if hasattr(self.messages, s.source_kind) ]
def ListTriggers(self, namespace_ref): messages = self._messages_module request = messages.RunNamespacesTriggersListRequest( parent=namespace_ref.RelativeName()) with metrics.RecordDuration(metric_names.LIST_TRIGGERS): response = self._client.namespaces_triggers.List(request) return [trigger.Trigger(item, messages) for item in response.items]
def ListRevisions(self, namespace_ref, service_name): """List all revisions for the given service. Revision list gets sorted by service name and creation timestamp. Args: namespace_ref: Resource, namespace to list revisions in service_name: str, The service for which to list revisions. Returns: A list of revisions for the given service. """ messages = self._messages_module request = messages.RunNamespacesRevisionsListRequest( parent=namespace_ref.RelativeName(), ) if service_name is not None: # For now, same as the service name, and keeping compatible with # 'service-less' operation. request.labelSelector = 'serving.knative.dev/service = {}'.format( service_name) with metrics.RecordDuration(metric_names.LIST_REVISIONS): response = self._client.namespaces_revisions.List(request) # Server does not sort the response so we'll need to sort client-side revisions = [ revision.Revision(item, messages) for item in response.items ] # Newest first revisions.sort(key=lambda r: r.creation_timestamp, reverse=True) revisions.sort(key=lambda r: r.service_name) return revisions
def ListServices(self, namespace_ref): messages = self._messages_module request = messages.RunNamespacesServicesListRequest( parent=namespace_ref.RelativeName()) with metrics.RecordDuration(metric_names.LIST_SERVICES): response = self._client.namespaces_services.List(request) return [service.Service(item, messages) for item in response.items]
def CopyTarballToGCS(self, storage_client, gcs_object, ignore_file=None): """Copy a tarball of the snapshot to GCS. Args: storage_client: storage_api.StorageClient, The storage client to use for uploading. gcs_object: storage.objects Resource, The GCS object to write. ignore_file: Override .gcloudignore file to specify skip files. Returns: storage_v1_messages.Object, The written GCS object. """ with metrics.RecordDuration(metric_names.UPLOAD_SOURCE): with files.ChDir(self.src_dir): with files.TemporaryDirectory() as tmp: archive_path = os.path.join(tmp, 'file.tgz') tf = self._MakeTarball(archive_path) tf.close() ignore_file_path = os.path.join(self.src_dir, ignore_file or gcloudignore.IGNORE_FILE_NAME) if self.any_files_ignored: if os.path.exists(ignore_file_path): log.info('Using ignore file [{}]'.format(ignore_file_path)) else: log.status.Print(_IGNORED_FILE_MESSAGE.format( log_file=log.GetLogFilePath())) log.status.write( 'Uploading tarball of [{src_dir}] to ' '[gs://{bucket}/{object}]\n'.format( src_dir=self.src_dir, bucket=gcs_object.bucket, object=gcs_object.object, ), ) return storage_client.CopyFileToGCS(archive_path, gcs_object)
def ListTriggers(self, namespace_ref): """Returns a list of existing triggers in the given namespace.""" request = self.messages.AnthoseventsNamespacesTriggersListRequest( parent=namespace_ref.RelativeName()) with metrics.RecordDuration(metric_names.LIST_TRIGGERS): response = self._client.namespaces_triggers.List(request) return [trigger.Trigger(item, self.messages) for item in response.items]
def ListRoutes(self, namespace_ref): messages = self._messages_module request = messages.RunNamespacesRoutesListRequest( parent=namespace_ref.RelativeName()) with metrics.RecordDuration(metric_names.LIST_ROUTES): response = self._client.namespaces_routes.List(request) return [route.Route(item, messages) for item in response.items]
def ListConfigurations(self, namespace_ref): messages = self.messages_module request = messages.RunNamespacesConfigurationsListRequest( parent=namespace_ref.RelativeName()) with metrics.RecordDuration(metric_names.LIST_CONFIGURATIONS): response = self._client.namespaces_configurations.List(request) return [configuration.Configuration(item, messages) for item in response.items]
def CreateDomainMapping(self, domain_mapping_ref, service_name, config_changes, force_override=False): """Create a domain mapping. Args: domain_mapping_ref: Resource, domainmapping resource. service_name: str, the service to which to map domain. config_changes: list of ConfigChanger to modify the domainmapping with force_override: bool, override an existing mapping of this domain. Returns: A domain_mapping.DomainMapping object. """ messages = self.messages_module new_mapping = domain_mapping.DomainMapping.New( self._client, domain_mapping_ref.namespacesId) new_mapping.name = domain_mapping_ref.domainmappingsId new_mapping.route_name = service_name new_mapping.force_override = force_override for config_change in config_changes: new_mapping = config_change.Adjust(new_mapping) request = messages.RunNamespacesDomainmappingsCreateRequest( domainMapping=new_mapping.Message(), parent=domain_mapping_ref.Parent().RelativeName()) with metrics.RecordDuration(metric_names.CREATE_DOMAIN_MAPPING): try: response = self._client.namespaces_domainmappings.Create(request) except api_exceptions.HttpConflictError: raise serverless_exceptions.DomainMappingCreationError( 'Domain mapping to [{}] for service [{}] already exists.'.format( domain_mapping_ref.Name(), service_name)) # 'run domain-mappings create' is synchronous. Poll for its completion.x with progress_tracker.ProgressTracker('Creating...'): mapping = waiter.PollUntilDone( DomainMappingResourceRecordPoller(self), domain_mapping_ref) ready = mapping.conditions.get('Ready') message = None if ready and ready.get('message'): message = ready['message'] if not mapping.records: if (mapping.ready_condition['reason'] == domain_mapping.MAPPING_ALREADY_EXISTS_CONDITION_REASON): raise serverless_exceptions.DomainMappingAlreadyExistsError( 'Domain mapping to [{}] is already in use elsewhere.'.format( domain_mapping_ref.Name())) raise serverless_exceptions.DomainMappingCreationError( message or 'Could not create domain mapping.') if message: log.status.Print(message) return mapping return domain_mapping.DomainMapping(response, messages)
def GetTrigger(self, trigger_ref): """Returns the referenced trigger.""" request = self.messages.AnthoseventsNamespacesTriggersGetRequest( name=trigger_ref.RelativeName()) try: with metrics.RecordDuration(metric_names.GET_TRIGGER): response = self._client.namespaces_triggers.Get(request) except api_exceptions.HttpNotFoundError: return None return trigger.Trigger(response, self.messages)
def ListTriggers(self, namespace_ref): """Returns a list of existing triggers in the given namespace.""" client = self.ClientFromCrd(_TRIGGERS_CRD_NAME) messages = client.MESSAGES_MODULE request = messages.AnthoseventsNamespacesTriggersListRequest( parent=namespace_ref.RelativeName()) with metrics.RecordDuration(metric_names.LIST_TRIGGERS): response = client.namespaces_triggers.List(request) return [trigger.Trigger(item, messages) for item in response.items]
def DeleteTrigger(self, trigger_ref): """Deletes the referenced trigger.""" request = self.messages.AnthoseventsNamespacesTriggersDeleteRequest( name=trigger_ref.RelativeName()) try: with metrics.RecordDuration(metric_names.DELETE_TRIGGER): self._client.namespaces_triggers.Delete(request) except api_exceptions.HttpNotFoundError: raise exceptions.TriggerNotFound('Trigger [{}] not found.'.format( trigger_ref.Name()))
def GetSource(self, source_ref, source_crd): """Returns the referenced source.""" request_method = self.SourceGetMethod(source_crd) request_message_type = request_method.GetRequestType() request = request_message_type(name=source_ref.RelativeName()) try: with metrics.RecordDuration(metric_names.GET_SOURCE): response = request_method.Call(request, client=self._client) except api_exceptions.HttpNotFoundError: return None return source.Source(response, self.messages, source_crd.source_kind)
def ListServices(self, namespace_ref): """Returns all services in the namespace.""" messages = self.messages_module request = messages.RunNamespacesServicesListRequest( parent=namespace_ref.RelativeName()) try: with metrics.RecordDuration(metric_names.LIST_SERVICES): response = self._client.namespaces_services.List(request) return [service.Service(item, messages) for item in response.items] except api_exceptions.InvalidDataFromServerError as e: serverless_exceptions.MaybeRaiseCustomFieldMismatch(e)
def ListSourceCustomResourceDefinitions(self): """Returns a list of CRDs for event sources.""" messages = self._crd_client.MESSAGES_MODULE request = messages.RunCustomresourcedefinitionsListRequest( labelSelector=_EVENT_SOURCES_LABEL_SELECTOR) with metrics.RecordDuration(metric_names.LIST_SOURCE_CRDS): response = self._crd_client.customresourcedefinitions.List(request) return [ custom_resource_definition.SourceCustomResourceDefinition( item, messages) for item in response.items ]
def DeleteSource(self, source_ref, source_crd): """Deletes the referenced source.""" request_method = self.SourceDeleteMethod(source_crd) request_message_type = request_method.GetRequestType() request = request_message_type(name=source_ref.RelativeName()) try: with metrics.RecordDuration(metric_names.DELETE_SOURCE): request_method.Call(request, client=self._client) except api_exceptions.HttpNotFoundError: raise exceptions.SourceNotFound( '{} events source [{}] not found.'.format( source_crd.source_kind, source_ref.Name()))
def DeleteDomainMapping(self, domain_mapping_ref): """Delete a domain mapping. Args: domain_mapping_ref: Resource, domainmapping resource. """ messages = self._messages_module request = messages.RunNamespacesDomainmappingsDeleteRequest( name=domain_mapping_ref.RelativeName()) with metrics.RecordDuration(metric_names.DELETE_DOMAIN_MAPPING): self._client.namespaces_domainmappings.Delete(request)
def GetService(self, service_ref): """Return the relevant Service from the server, or None if 404.""" messages = self._messages_module service_get_request = messages.RunNamespacesServicesGetRequest( name=service_ref.RelativeName()) try: with metrics.RecordDuration(metric_names.GET_SERVICE): service_get_response = self._client.namespaces_services.Get( service_get_request) return service.Service(service_get_response, messages) except api_exceptions.HttpNotFoundError: return None
def GetTrigger(self, trigger_ref): """Returns the referenced trigger.""" client = self.ClientFromCrd(_TRIGGERS_CRD_NAME) messages = client.MESSAGES_MODULE request = messages.AnthoseventsNamespacesTriggersGetRequest( name=trigger_ref.RelativeName()) try: with metrics.RecordDuration(metric_names.GET_TRIGGER): response = client.namespaces_triggers.Get(request) except api_exceptions.HttpNotFoundError: return None return trigger.Trigger(response, messages)
def ListSourceCustomResourceDefinitions(self): """Returns a list of CRDs for event sources.""" messages = self._crd_client.MESSAGES_MODULE request = messages.RunCustomresourcedefinitionsListRequest( labelSelector=_EVENT_SOURCES_LABEL_SELECTOR) with metrics.RecordDuration(metric_names.LIST_SOURCE_CRDS): response = self._crd_client.customresourcedefinitions.List(request) source_crds = [ custom_resource_definition.SourceCustomResourceDefinition( item, messages) for item in response.items ] # Only include CRDs for source kinds that are defined in the api. return [s for s in source_crds if hasattr(self.messages, s.source_kind)]
def CreateSource(self, source_obj, source_crd, owner_trigger, namespace_ref, broker_name, parameters): """Create an source with the specified event type and owner trigger. Args: source_obj: source.Source. The source object being created. source_crd: custom_resource_definition.SourceCRD, the source crd for the source to create owner_trigger: trigger.Trigger, trigger to associate as an owner of the source. namespace_ref: googlecloudsdk.core.resources.Resource, namespace resource. broker_name: str, name of the broker to act as a sink. parameters: dict, additional parameters to set on the source spec. Returns: source.Source of the created source. """ client = self.ClientFromCrd(source_crd) messages = client.MESSAGES_MODULE source_obj.ce_overrides[trigger.SOURCE_TRIGGER_LINK_FIELD] = ( owner_trigger.filter_attributes[trigger.SOURCE_TRIGGER_LINK_FIELD]) source_obj.owners.append( messages.OwnerReference(apiVersion=owner_trigger.apiVersion, kind=owner_trigger.kind, name=owner_trigger.name, uid=owner_trigger.uid, controller=True)) source_obj.set_sink(broker_name, self._api_version) # Parse parameters flags into source's spec source.ParseDynamicFieldsIntoMessage(source_obj.spec, parameters) source.SourceFix(source_obj) request_method = self.SourceCreateMethod(source_crd) request_message_type = request_method.GetRequestType() request = request_message_type( **{ request_method.request_field: source_obj.Message(), 'parent': namespace_ref.RelativeName() }) try: with metrics.RecordDuration(metric_names.CREATE_SOURCE): response = request_method.Call(request, client=client) except api_exceptions.HttpConflictError: raise exceptions.SourceCreationError( 'Source [{}] already exists.'.format(source_obj.name)) return source.Source(response, messages, source_crd.source_kind)
def GetDomainMapping(self, domain_mapping_ref): """Get a domain mapping. Args: domain_mapping_ref: Resource, domainmapping resource. Returns: A domain_mapping.DomainMapping object. """ messages = self._messages_module request = messages.RunNamespacesDomainmappingsGetRequest( name=domain_mapping_ref.RelativeName()) with metrics.RecordDuration(metric_names.GET_DOMAIN_MAPPING): response = self._client.namespaces_domainmappings.Get(request) return domain_mapping.DomainMapping(response, messages)
def UpdateNamespaceWithLabels(self, namespace_ref, labels): """Updates an existing namespace with the labels provided. If a label already exists, this will replace that label with the value provided. This is akin to specifying --overwrite with kubectl. Args: namespace_ref: googlecloudsdk.core.resources.Resource, namespace resource. Note that this should be of the collection "run.api.v1.namespaces" and *not* "run.namespaces". labels: map[str, str] of label keys and values to patch. Returns: Namespace that was patched. """ messages = self._core_client.MESSAGES_MODULE namespace = messages.Namespace() arg_utils.SetFieldInMessage(namespace, _METADATA_LABELS_FIELD, labels) old_additional_headers = {} try: # We need to specify a special content-type for k8s to accept our PATCH. # However, this appears to only be settable at the client level, not at # the request level. So we'll update the client for our request, and the # set it back to the old value afterwards. old_additional_headers = self._core_client.additional_http_headers additional_headers = old_additional_headers.copy() additional_headers['content-type'] = 'application/merge-patch+json' self._core_client.additional_http_headers = additional_headers except AttributeError: # TODO(b/150229881): Remove this try/except block and below. # The mocked test client does not have an additional_http_headers attr # So we won't be able to test this part. pass with metrics.RecordDuration(metric_names.UPDATE_NAMESPACE): try: request = messages.RunApiV1NamespacesPatchRequest( name=namespace_ref.RelativeName(), namespace=namespace, updateMask=_METADATA_LABELS_FIELD) response = self._core_client.api_v1_namespaces.Patch(request) finally: try: self._core_client.additional_http_headers = old_additional_headers except AttributeError: # The mocked test client does not have an additional_http_headers attr pass return response
def ListDomainMappings(self, namespace_ref): """List all domain mappings. Args: namespace_ref: Resource, namespace to list domain mappings in. Returns: A list of domain mappings. """ messages = self.messages_module request = messages.RunNamespacesDomainmappingsListRequest( parent=namespace_ref.RelativeName()) with metrics.RecordDuration(metric_names.LIST_DOMAIN_MAPPINGS): response = self._client.namespaces_domainmappings.List(request) return [domain_mapping.DomainMapping(item, messages) for item in response.items]
def CreateDomainMapping(self, domain_mapping_ref, service_name): """Create a domain mapping. Args: domain_mapping_ref: Resource, domainmapping resource. service_name: str, the service to which to map domain. Returns: A domain_mapping.DomainMapping object. """ messages = self._messages_module new_mapping = domain_mapping.DomainMapping.New( self._client, domain_mapping_ref.namespacesId) new_mapping.name = domain_mapping_ref.domainmappingsId new_mapping.route_name = service_name request = messages.RunNamespacesDomainmappingsCreateRequest( domainMapping=new_mapping.Message(), parent=domain_mapping_ref.Parent().RelativeName()) with metrics.RecordDuration(metric_names.CREATE_DOMAIN_MAPPING): try: response = self._client.namespaces_domainmappings.Create( request) except api_exceptions.HttpConflictError: raise serverless_exceptions.DomainMappingCreationError( 'Domain mapping to [{}] for service [{}] already exists.'. format(domain_mapping_ref.Name(), service_name)) # 'run domain-mappings create' is synchronous. Poll for its completion.x with progress_tracker.ProgressTracker('Creating...'): mapping = waiter.PollUntilDone( DomainMappingResourceRecordPoller(self), domain_mapping_ref) ready = mapping.conditions.get('Ready') records = getattr(mapping.status, 'resourceRecords', None) message = None if ready and ready.get('message'): message = ready['message'] if not records: raise serverless_exceptions.DomainMappingCreationError( message or 'Could not create domain mapping.') if message: log.status.Print(message) return records return domain_mapping.DomainMapping(response, messages)
def GetRevision(self, revision_ref): """Get the revision. Args: revision_ref: Resource, revision to get. Returns: A revision.Revision object. """ messages = self._messages_module revision_name = revision_ref.RelativeName() request = messages.RunNamespacesRevisionsGetRequest(name=revision_name) try: with metrics.RecordDuration(metric_names.GET_REVISION): response = self._client.namespaces_revisions.Get(request) return revision.Revision(response, messages) except api_exceptions.HttpNotFoundError: return None
def CreateTrigger(self, trigger_ref, source_obj, event_type, trigger_filters, target_service, broker): """Create a trigger that sends events to the target service. Args: trigger_ref: googlecloudsdk.core.resources.Resource, trigger resource. source_obj: source.Source. The source object to be created after the trigger. If creating a custom event, this may be None. event_type: str, the event type the source will filter by. trigger_filters: collections.OrderedDict() target_service: str, name of the Cloud Run service to subscribe. broker: str, name of the broker to act as a sink for the source. Returns: trigger.Trigger of the created trigger. """ trigger_obj = trigger.Trigger.New(self._client, trigger_ref.Parent().Name()) trigger_obj.name = trigger_ref.Name() if source_obj is not None: trigger_obj.dependency = source_obj # TODO(b/141617597): Set to str(random.random()) without prepended string trigger_obj.filter_attributes[ trigger.SOURCE_TRIGGER_LINK_FIELD] = 'link{}'.format( random.random()) trigger_obj.filter_attributes[trigger.EVENT_TYPE_FIELD] = event_type # event/flags.py ensures filter key doesn't include disallowed fields trigger_obj.filter_attributes.update(trigger_filters) trigger_obj.subscriber = target_service trigger_obj.broker = broker request = self.messages.RunNamespacesTriggersCreateRequest( trigger=trigger_obj.Message(), parent=trigger_ref.Parent().RelativeName()) try: with metrics.RecordDuration(metric_names.CREATE_TRIGGER): response = self._client.namespaces_triggers.Create(request) except api_exceptions.HttpConflictError: raise exceptions.TriggerCreationError( 'Trigger [{}] already exists.'.format(trigger_obj.name)) return trigger.Trigger(response, self.messages)