Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean remote application when disabling observability #215

Merged
merged 1 commit into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion sunbeam-python/sunbeam/plugins/observability/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from sunbeam.clusterd.service import ClusterServiceUnavailableException
from sunbeam.commands.juju import JujuStepHelper
from sunbeam.commands.k8s import CREDENTIAL_SUFFIX, K8SHelper
from sunbeam.commands.openstack import PatchLoadBalancerServicesStep
from sunbeam.commands.openstack import OPENSTACK_MODEL, PatchLoadBalancerServicesStep
from sunbeam.commands.terraform import TerraformException, TerraformInitStep
from sunbeam.jobs.common import (
BaseStep,
Expand Down Expand Up @@ -178,6 +178,58 @@ def run(self, status: Optional[Status] = None) -> Result:
return Result(ResultType.COMPLETED)


class RemoveSaasApplicationsStep(BaseStep):
"""Removes SAAS offers from given model.

This is a workaround around:
https://github.com/juju/terraform-provider-juju/issues/473
"""

def __init__(self, jhelper: JujuHelper, model: str, offering_model: str):
super().__init__(
f"Purge SAAS Offers: {model}", f"Purging SAAS Offers from {model}"
)
self.jhelper = jhelper
self.model = model
self.offering_model = offering_model
self._remote_app_to_delete = []

def is_skip(self, status: Status | None = None) -> Result:
model = run_sync(self.jhelper.get_model(self.model))
remote_applications = model.remote_applications
LOG.debug(
"Remote applications found: %s", ", ".join(remote_applications.keys())
)
if not remote_applications:
return Result(ResultType.SKIPPED, "No remote applications found")

for name, remote_app in remote_applications.items():
if not remote_app:
continue
offer = remote_app.offer_url
LOG.debug("Processing offer: %s", offer)
model_name = offer.split("/", 1)[1].split(".", 1)[0]
if model_name == self.offering_model:
self._remote_app_to_delete.append(name)

if len(self._remote_app_to_delete) == 0:
return Result(ResultType.SKIPPED, "No remote applications to remove")

return Result(ResultType.COMPLETED)

def run(self, status: Optional[Status] = None) -> Result:
"""Execute configuration using terraform."""
if not self._remote_app_to_delete:
return Result(ResultType.COMPLETED)

model = run_sync(self.jhelper.get_model(self.model))

for saas in self._remote_app_to_delete:
LOG.debug("Removing remote application %s", saas)
run_sync(model.remove_saas(saas))
return Result(ResultType.COMPLETED)


class DeployGrafanaAgentStep(BaseStep, JujuStepHelper):
"""Deploy Grafana Agent using Terraform"""

Expand Down Expand Up @@ -515,11 +567,15 @@ def run_disable_plans(self):
agent_grafana_k8s_plan = [
TerraformInitStep(self.manifest.get_tfhelper(self.tfplan)),
DisableOpenStackApplicationStep(jhelper, self),
RemoveSaasApplicationsStep(jhelper, OPENSTACK_MODEL, OBSERVABILITY_MODEL),
]

grafana_agent_plan = [
TerraformInitStep(self.manifest.get_tfhelper(self.tfplan_grafana_agent)),
RemoveGrafanaAgentStep(self, jhelper),
RemoveSaasApplicationsStep(
jhelper, self.deployment.infrastructure_model, OBSERVABILITY_MODEL
),
]

cos_plan = [
Expand Down
44 changes: 43 additions & 1 deletion sunbeam-python/tests/unit/sunbeam/plugins/test_observability.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.

import asyncio
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock, Mock, patch

import pytest

Expand Down Expand Up @@ -222,3 +222,45 @@ def test_run_waiting_timed_out(self, jhelper, observabilityplugin):
jhelper.wait_application_gone.assert_called_once()
assert result.result_type == ResultType.FAILED
assert result.message == "timed out"


class TestRemoveSaasApplicationsStep:
def test_is_skip(self, jhelper):
jhelper.get_model.return_value = AsyncMock(
remote_applications={
"test-1": Mock(offer_url="admin/offering_model.test-1")
}
)
step = observability_plugin.RemoveSaasApplicationsStep(
jhelper, "test", "offering_model"
)
result = step.is_skip()
assert result.result_type == ResultType.COMPLETED

def test_is_skip_no_remote_app(self, jhelper):
jhelper.get_model.return_value = AsyncMock(remote_applications={})
step = observability_plugin.RemoveSaasApplicationsStep(
jhelper, "test", "offering_model"
)
result = step.is_skip()
assert result.result_type == ResultType.SKIPPED

def test_is_skip_no_saas_app(self, jhelper):
jhelper.get_model.return_value = AsyncMock(
remote_applications={
"test-1": Mock(offer_url="admin/offering_model.test-1")
}
)
step = observability_plugin.RemoveSaasApplicationsStep(
jhelper, "test", "offering_model-no-apps"
)
result = step.is_skip()
assert result.result_type == ResultType.SKIPPED

def test_run(self, jhelper):
step = observability_plugin.RemoveSaasApplicationsStep(
jhelper, "test", "offering_model"
)
step._remote_app_to_delete = ["test-1"]
result = step.run()
assert result.result_type == ResultType.COMPLETED
Loading