Beispiel #1
0
 def __init__(self,
              resource_id: str,
              body_patch: dict,
              project_id: Optional[str] = None,
              request_id: Optional[str] = None,
              gcp_conn_id: str = 'google_cloud_default',
              api_version: str = 'v1',
              validate_body: bool = True,
              *args,
              **kwargs) -> None:
     self.body_patch = body_patch
     self.request_id = request_id
     self._field_validator = None  # Optional[GcpBodyFieldValidator]
     if 'name' not in self.body_patch:
         raise AirflowException(
             "The body '{}' should contain at least "
             "name for the new operator in the 'name' field".format(
                 body_patch))
     if validate_body:
         self._field_validator = GcpBodyFieldValidator(
             GCE_INSTANCE_TEMPLATE_VALIDATION_PATCH_SPECIFICATION,
             api_version=api_version)
     self._field_sanitizer = GcpBodyFieldSanitizer(
         GCE_INSTANCE_TEMPLATE_FIELDS_TO_SANITIZE)
     super().__init__(project_id=project_id,
                      zone='global',
                      resource_id=resource_id,
                      gcp_conn_id=gcp_conn_id,
                      api_version=api_version,
                      *args,
                      **kwargs)
Beispiel #2
0
class ComputeEngineCopyInstanceTemplateOperator(ComputeEngineBaseOperator):
    """
    Copies the instance template, applying specified changes.

    .. seealso::
        For more information on how to use this operator, take a look at the guide:
        :ref:`howto/operator:ComputeEngineCopyInstanceTemplateOperator`

    :param resource_id: Name of the Instance Template
    :type resource_id: str
    :param body_patch: Patch to the body of instanceTemplates object following rfc7386
        PATCH semantics. The body_patch content follows
        https://cloud.google.com/compute/docs/reference/rest/v1/instanceTemplates
        Name field is required as we need to rename the template,
        all the other fields are optional. It is important to follow PATCH semantics
        - arrays are replaced fully, so if you need to update an array you should
        provide the whole target array as patch element.
    :type body_patch: dict
    :param project_id: Optional, Google Cloud Platform Project ID where the Compute
        Engine Instance exists.  If set to None or missing, the default project_id from the GCP connection
        is used.
    :type project_id: str
    :param request_id: Optional, unique request_id that you might add to achieve
        full idempotence (for example when client call times out repeating the request
        with the same request id will not create a new instance template again).
        It should be in UUID format as defined in RFC 4122.
    :type request_id: str
    :param gcp_conn_id: Optional, The connection ID used to connect to Google Cloud
        Platform. Defaults to 'google_cloud_default'.
    :type gcp_conn_id: str
    :param api_version: Optional, API version used (for example v1 - or beta). Defaults
        to v1.
    :type api_version: str
    :param validate_body: Optional, If set to False, body validation is not performed.
        Defaults to False.
    :type validate_body: bool
    """
    # [START gce_instance_template_copy_operator_template_fields]
    template_fields = ('project_id', 'resource_id', 'request_id',
                       'gcp_conn_id', 'api_version')
    # [END gce_instance_template_copy_operator_template_fields]

    @apply_defaults
    def __init__(self,
                 resource_id: str,
                 body_patch: dict,
                 project_id: Optional[str] = None,
                 request_id: Optional[str] = None,
                 gcp_conn_id: str = 'google_cloud_default',
                 api_version: str = 'v1',
                 validate_body: bool = True,
                 *args,
                 **kwargs) -> None:
        self.body_patch = body_patch
        self.request_id = request_id
        self._field_validator = None  # Optional[GcpBodyFieldValidator]
        if 'name' not in self.body_patch:
            raise AirflowException(
                "The body '{}' should contain at least "
                "name for the new operator in the 'name' field".format(
                    body_patch))
        if validate_body:
            self._field_validator = GcpBodyFieldValidator(
                GCE_INSTANCE_TEMPLATE_VALIDATION_PATCH_SPECIFICATION,
                api_version=api_version)
        self._field_sanitizer = GcpBodyFieldSanitizer(
            GCE_INSTANCE_TEMPLATE_FIELDS_TO_SANITIZE)
        super().__init__(project_id=project_id,
                         zone='global',
                         resource_id=resource_id,
                         gcp_conn_id=gcp_conn_id,
                         api_version=api_version,
                         *args,
                         **kwargs)

    def _validate_all_body_fields(self):
        if self._field_validator:
            self._field_validator.validate(self.body_patch)

    def execute(self, context):
        hook = ComputeEngineHook(gcp_conn_id=self.gcp_conn_id,
                                 api_version=self.api_version)
        self._validate_all_body_fields()
        try:
            # Idempotence check (sort of) - we want to check if the new template
            # is already created and if is, then we assume it was created by previous run
            # of CopyTemplate operator - we do not check if content of the template
            # is as expected. Templates are immutable so we cannot update it anyway
            # and deleting/recreating is not worth the hassle especially
            # that we cannot delete template if it is already used in some Instance
            # Group Manager. We assume success if the template is simply present
            existing_template = hook.get_instance_template(
                resource_id=self.body_patch['name'],
                project_id=self.project_id)
            self.log.info(
                "The %s template already existed. It was likely created by previous run of the operator. "
                "Assuming success.", existing_template)
            return existing_template
        except HttpError as e:
            # We actually expect to get 404 / Not Found here as the template should
            # not yet exist
            if not e.resp.status == 404:
                raise e
        old_body = hook.get_instance_template(resource_id=self.resource_id,
                                              project_id=self.project_id)
        new_body = deepcopy(old_body)
        self._field_sanitizer.sanitize(new_body)
        new_body = merge(new_body, self.body_patch)
        self.log.info("Calling insert instance template with updated body: %s",
                      new_body)
        hook.insert_instance_template(body=new_body,
                                      request_id=self.request_id,
                                      project_id=self.project_id)
        return hook.get_instance_template(resource_id=self.body_patch['name'],
                                          project_id=self.project_id)