Skip to content

Commit f9bc01e

Browse files
Opta destroy now purges the layer of modules w/o existing resources (#473)
* Opta destroy now purges the layer of modules w/o existing resources I think that this makes sense-- if there are no resources to destroy, then forget about it. Takes a lot of headache away. * testfixing
1 parent 4df2b26 commit f9bc01e

4 files changed

Lines changed: 22 additions & 31 deletions

File tree

opta/commands/destroy.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ def destroy(
9696
abort=True,
9797
)
9898
tf_flags.append("-auto-approve")
99-
99+
modules = Terraform.get_existing_modules(layer)
100+
layer.modules = [x for x in layer.modules if x.name in modules]
100101
gen_all(layer)
101102
Terraform.init(False, "-reconfigure", layer=layer)
102103
logger.info(f"Destroying {layer.name}")

opta/core/terraform.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,22 +163,17 @@ def destroy_resources(
163163

164164
@classmethod
165165
def destroy_all(cls, layer: "Layer", *tf_flags: str) -> None:
166-
existing_modules = Terraform.get_existing_modules(layer)
167-
targets = list(map(lambda x: f"-target=module.{x}", sorted(existing_modules)))
168166

169167
# Refreshing the state is necessary to update terraform outputs.
170168
# This includes fetching the latest EKS cluster auth token, which is
171169
# necessary for destroying many k8s resources.
172-
cls.refresh(layer, *targets)
170+
cls.refresh(layer)
173171
kwargs = cls.insert_extra_env(layer)
174172

175173
idx = len(layer.modules) - 1
176174
for module in reversed(layer.modules):
177175
module_address_prefix = f"module.{module.name}"
178176

179-
if module.name not in existing_modules:
180-
idx -= 1
181-
continue
182177
cls.refresh(layer, f"-target={module_address_prefix}")
183178
nice_run(
184179
["terraform", "destroy", f"-target={module_address_prefix}", *tf_flags],

opta/module_processors/datadog.py

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from opta.core.kubernetes import configure_kubectl
1010
from opta.exceptions import UserErrors
1111
from opta.module_processors.base import ModuleProcessor
12-
from opta.utils import exp_backoff, logger
1312

1413
if TYPE_CHECKING:
1514
from opta.layer import Layer
@@ -22,52 +21,40 @@ def __init__(self, module: "Module", layer: "Layer"):
2221
raise Exception(
2322
f"The module {module.name} was expected to be of type datadog"
2423
)
25-
# If the k8s cluster was recently created, it may take some time for it to be ready.
26-
for _ in exp_backoff(num_tries=3):
27-
try:
28-
configure_kubectl(layer)
29-
break
30-
except Exception as err:
31-
logger.exception(str(err))
32-
logger.info("Retrying attempt to talk to K8s cluster...")
33-
else:
34-
raise Exception("Couldn't connect to the K8s cluster")
35-
36-
load_kube_config()
37-
self.v1 = CoreV1Api()
3824
super(DatadogProcessor, self).__init__(module, layer)
3925

4026
def process(self, module_idx: int) -> None:
27+
configure_kubectl(self.layer)
28+
load_kube_config()
29+
v1 = CoreV1Api()
4130
# Update the secrets
42-
namespaces = self.v1.list_namespace(
43-
field_selector=f"metadata.name={self.layer.name}"
44-
)
31+
namespaces = v1.list_namespace(field_selector=f"metadata.name={self.layer.name}")
4532
if len(namespaces.items) == 0:
46-
self.v1.create_namespace(
33+
v1.create_namespace(
4734
body=V1Namespace(metadata=V1ObjectMeta(name=self.layer.name))
4835
)
4936
try:
50-
secret = self.v1.read_namespaced_secret("secret", self.layer.name)
37+
secret = v1.read_namespaced_secret("secret", self.layer.name)
5138
if (
5239
"DATADOG_API_KEY" not in secret.data
5340
or secret.data["DATADOG_API_KEY"] == ""
5441
):
55-
api_key = self.create_secret()
42+
api_key = self.create_secret(v1)
5643
else:
5744
api_key = base64.b64decode(secret.data["DATADOG_API_KEY"]).decode("utf-8")
5845
except ApiException:
59-
self.v1.create_namespaced_secret(
46+
v1.create_namespaced_secret(
6047
namespace=self.layer.name,
6148
body=V1Secret(
6249
metadata=V1ObjectMeta(name="secret"),
6350
string_data={"DATADOG_API_KEY": ""},
6451
),
6552
)
66-
api_key = self.create_secret()
53+
api_key = self.create_secret(v1)
6754
self.module.data["api_key"] = api_key
6855
super(DatadogProcessor, self).process(module_idx)
6956

70-
def create_secret(self) -> str:
57+
def create_secret(self, v1: CoreV1Api) -> str:
7158
value = self.module.data.get("api_key") or click.prompt(
7259
"Please enter your datadog api key (from https://app.datadoghq.com/account/settings#api)",
7360
type=click.STRING,
@@ -83,7 +70,7 @@ def create_secret(self) -> str:
8370
patch = [
8471
{"op": "replace", "path": "/data/DATADOG_API_KEY", "value": secret_value}
8572
]
86-
self.v1.patch_namespaced_secret("secret", self.layer.name, patch)
73+
v1.patch_namespaced_secret("secret", self.layer.name, patch)
8774
return value
8875

8976
def validate_api_key(self, api_key: str) -> bool:

tests/commands/test_destroy.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from pytest_mock import MockFixture
55

66
from opta.commands.destroy import destroy
7+
from opta.exceptions import UserErrors
78
from tests.util import get_call_args
89

910
FAKE_ENV_CONFIG = os.path.join(
@@ -42,6 +43,7 @@ def test_destroy_env_with_children(mocker: MockFixture) -> None:
4243
result = runner.invoke(destroy, ["--config", FAKE_ENV_CONFIG])
4344

4445
assert result.exit_code == 1
46+
assert isinstance(result.exception, UserErrors)
4547

4648
assert not mocked_gen_all.called
4749

@@ -53,6 +55,9 @@ def test_destroy_env_without_children(mocker: MockFixture) -> None:
5355
mocker.patch("opta.commands.destroy.amplitude_client.send_event")
5456
mocker.patch("opta.commands.destroy.Terraform.init")
5557
mocker.patch("opta.commands.destroy.Terraform.destroy_all")
58+
mocker.patch(
59+
"opta.commands.destroy.Terraform.get_existing_modules", return_value={"base"}
60+
)
5661
mocker.patch("opta.commands.destroy.Terraform.download_state", return_value=True)
5762
mocker.patch("opta.commands.destroy.Layer.verify_cloud_credentials")
5863

@@ -80,6 +85,9 @@ def test_destroy_service(mocker: MockFixture) -> None:
8085
mocker.patch("opta.commands.destroy.amplitude_client.send_event")
8186
mocker.patch("opta.commands.destroy.Terraform.init")
8287
mocker.patch("opta.commands.destroy.Terraform.destroy_all")
88+
mocker.patch(
89+
"opta.commands.destroy.Terraform.get_existing_modules", return_value={"base"}
90+
)
8391
mocker.patch("opta.commands.destroy.Terraform.download_state", return_value=True)
8492
mocker.patch("opta.commands.destroy.Layer.verify_cloud_credentials")
8593

0 commit comments

Comments
 (0)