Example #1
0
def delete_source(source_id, auth_header, koku_uuid):
    """Delete Provider and Source."""
    LOG.info(f"Deactivating Provider {koku_uuid}")
    mark_provider_as_inactive(koku_uuid)
    LOG.info(f"Deleting Provider {koku_uuid} for Source ID: {source_id}")
    coordinator = SourcesProviderCoordinator(source_id, auth_header)
    coordinator.destroy_account(koku_uuid)
Example #2
0
    def test_execute_koku_provider_op_destroy_provider_not_found(self):
        """Test to execute Koku Operations to sync with Sources for destruction with provider missing.

        First, raise ProviderBuilderError. Check that provider and source still exists.
        Then, re-call provider destroy without exception, then see both source and provider are gone.

        """
        source_id = self.source_ids.get(Provider.PROVIDER_AWS)
        provider = Sources(**self.sources.get(Provider.PROVIDER_AWS))
        provider.save()
        # check that the source exists
        self.assertTrue(Sources.objects.filter(source_id=source_id).exists())

        with patch.object(ProviderAccessor, "cost_usage_source_ready", returns=True):
            builder = SourcesProviderCoordinator(source_id, provider.auth_header)
            builder.create_account(provider)

        self.assertTrue(Provider.objects.filter(uuid=provider.source_uuid).exists())
        provider = Sources.objects.get(source_id=source_id)

        msg = {"operation": "destroy", "provider": provider, "offset": provider.offset}
        with patch.object(SourcesHTTPClient, "set_source_status"):
            with patch.object(ProviderBuilder, "destroy_provider", side_effect=raise_provider_manager_error):
                source_integration.execute_koku_provider_op(msg)
                self.assertTrue(Provider.objects.filter(uuid=provider.source_uuid).exists())
                self.assertTrue(Sources.objects.filter(source_uuid=provider.source_uuid).exists())
                self.assertTrue(Sources.objects.filter(koku_uuid=provider.source_uuid).exists())

        with patch.object(SourcesHTTPClient, "set_source_status"):
            source_integration.execute_koku_provider_op(msg)
        self.assertFalse(Provider.objects.filter(uuid=provider.source_uuid).exists())
Example #3
0
 def update_source_name(self):
     """Update source name if it is out of sync with platform."""
     source_details = self.sources_client.get_source_details()
     if source_details.get("name") != self.source.name:
         self.source.name = source_details.get("name")
         self.source.save()
         builder = SourcesProviderCoordinator(self.source_id, self.source.auth_header)
         builder.update_account(self.source)
Example #4
0
    def test_execute_koku_provider_op_update(self):
        """Test to execute Koku Operations to sync with Sources for update."""

        def set_status_helper(*args, **kwargs):
            """helper to clear update flag."""
            storage.clear_update_flag(source_id)

        source_id = self.source_ids.get(Provider.PROVIDER_AWS)
        provider = Sources(**self.sources.get(Provider.PROVIDER_AWS))
        provider.save()

        msg = {"operation": "create", "provider": provider, "offset": provider.offset}
        with patch.object(SourcesHTTPClient, "set_source_status"):
            with patch.object(ProviderAccessor, "cost_usage_source_ready", returns=True):
                source_integration.execute_koku_provider_op(msg)

        builder = SourcesProviderCoordinator(source_id, provider.auth_header)

        source = storage.get_source_instance(source_id)
        uuid = source.koku_uuid

        with patch.object(ProviderAccessor, "cost_usage_source_ready", returns=True):
            builder.update_account(source)

        self.assertEqual(
            Provider.objects.get(uuid=uuid).billing_source.data_source,
            self.sources.get(Provider.PROVIDER_AWS).get("billing_source").get("data_source"),
        )

        provider.billing_source = {"data_source": {"bucket": "new-bucket"}}
        provider.koku_uuid = uuid
        provider.pending_update = True
        provider.save()

        msg = {"operation": "update", "provider": provider, "offset": provider.offset}
        with patch.object(SourcesHTTPClient, "set_source_status", side_effect=set_status_helper):
            with patch.object(ProviderAccessor, "cost_usage_source_ready", returns=True):
                source_integration.execute_koku_provider_op(msg)
        response = Sources.objects.get(source_id=source_id)
        self.assertEqual(response.pending_update, False)
        self.assertEqual(response.billing_source, {"data_source": {"bucket": "new-bucket"}})

        response = Provider.objects.get(uuid=uuid)
        self.assertEqual(response.billing_source.data_source.get("bucket"), "new-bucket")
Example #5
0
 def push_status(self):
     """Push status_msg to platform sources."""
     try:
         status_obj = self.status()
         if self.source.source_type in (Provider.PROVIDER_GCP,
                                        Provider.PROVIDER_GCP_LOCAL):
             builder = SourcesProviderCoordinator(self.source.source_id,
                                                  self.source.auth_header)
             if not status_obj:
                 if self.source.koku_uuid:
                     builder.update_account(self.source)
                 elif self.source.billing_source.get("data_source",
                                                     {}).get("table_id"):
                     builder.create_account(self.source)
         self.sources_client.set_source_status(status_obj)
         self.update_source_name()
         LOG.info(
             f"Source status for Source ID: {str(self.source_id)}: Status: {str(status_obj)}"
         )
     except SkipStatusPush as error:
         LOG.info(
             f"Platform sources status push skipped. Reason: {str(error)}")
     except SourcesHTTPClientError as error:
         err_msg = "Unable to push source status. Reason: {}".format(
             str(error))
         LOG.warning(err_msg)
Example #6
0
def execute_koku_provider_op(msg):
    """
    Execute the 'create' or 'destroy Koku-Provider operations.

    'create' operations:
        Koku POST /providers is executed along with updating the Sources database table with
        the Koku Provider uuid.
    'destroy' operations:
        Koku DELETE /providers is executed along with removing the Sources database entry.

    Two types of exceptions are handled for Koku HTTP operations.  Recoverable client and
    Non-Recoverable client errors.  If the error is recoverable the calling function
    (synchronize_sources) will re-queue the operation.

    Args:
        msg (Asyncio msg): Dictionary messages containing operation,
                                       provider and offset.
            example: {'operation': 'create', 'provider': SourcesModelObj, 'offset': 3}

    Returns:
        None

    """
    provider = msg.get("provider")
    operation = msg.get("operation")
    account_coordinator = SourcesProviderCoordinator(provider.source_id, provider.auth_header)
    sources_client = SourcesHTTPClient(provider.auth_header, provider.source_id)

    try:
        if operation == "create":
            LOG.info(f"Creating Koku Provider for Source ID: {str(provider.source_id)}")
            instance = account_coordinator.create_account(provider)
            LOG.info(f"Creating provider {instance.uuid} for Source ID: {provider.source_id}")
        elif operation == "update":
            instance = account_coordinator.update_account(provider)
            LOG.info(f"Updating provider {instance.uuid} for Source ID: {provider.source_id}")
        elif operation == "destroy":
            account_coordinator.destroy_account(provider)
            LOG.info(f"Destroying provider {provider.koku_uuid} for Source ID: {provider.source_id}")
        else:
            LOG.error(f"unknown operation: {operation}")
        sources_client.set_source_status(None)

    except SourcesProviderCoordinatorError as account_error:
        raise SourcesIntegrationError("Koku provider error: ", str(account_error))
    except ValidationError as account_error:
        err_msg = (
            f"Unable to {operation} provider for Source ID: {str(provider.source_id)}. Reason: {str(account_error)}"
        )
        LOG.warning(err_msg)
        sources_client.set_source_status(account_error)
Example #7
0
 def push_status(self):
     """Push status_msg to platform sources."""
     try:
         status_obj = self.status()
         if self._gcp_bigquery_table_found():
             builder = SourcesProviderCoordinator(self.source.source_id,
                                                  self.source.auth_header)
             if self.source.koku_uuid:
                 builder.update_account(self.source)
             else:
                 builder.create_account(self.source)
             self.sources_client.set_source_status(status_obj)
         self.update_source_name()
         LOG.info(
             f"Source status for Source ID: {str(self.source_id)}: Status: {str(status_obj)}"
         )
     except SkipStatusPush as error:
         LOG.info(
             f"Platform sources status push skipped. Reason: {str(error)}")
     except SourcesHTTPClientError as error:
         err_msg = "Unable to push source status. Reason: {}".format(
             str(error))
         LOG.warning(err_msg)