Skip to content

Commit 36dcb1d

Browse files
authored
Custom wait_until_deleted (#73)
Issue #, if available: aws-controllers-k8s/community#1509 Description of changes: By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent f69e689 commit 36dcb1d

File tree

2 files changed

+122
-12
lines changed

2 files changed

+122
-12
lines changed

test/e2e/common/waiter.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
# not use this file except in compliance with the License. A copy of the
5+
# License is located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is distributed
10+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
# express or implied. See the License for the specific language governing
12+
# permissions and limitations under the License.
13+
14+
import datetime
15+
import time
16+
import typing
17+
18+
import boto3
19+
import pytest
20+
21+
DEFAULT_WAIT_UNTIL_TIMEOUT_SECONDS = 60*10
22+
DEFAULT_WAIT_UNTIL_INTERVAL_SECONDS = 15
23+
DEFAULT_WAIT_UNTIL_DELETED_TIMEOUT_SECONDS = 60*20
24+
DEFAULT_WAIT_UNTIL_DELETED_INTERVAL_SECONDS = 30
25+
26+
ClusterMatchFunc = typing.NewType(
27+
'ClusterMatchFunc',
28+
typing.Callable[[dict], bool],
29+
)
30+
31+
class StatusMatcher:
32+
def __init__(self, status):
33+
self.match_on = status
34+
35+
def __call__(self, record: dict) -> bool:
36+
return (record is not None and 'status' in record
37+
and record['status'] == self.match_on)
38+
39+
40+
def status_matches(status: str) -> ClusterMatchFunc:
41+
return StatusMatcher(status)
42+
43+
44+
def wait_until(
45+
eks_cluster_name: str,
46+
match_fn: ClusterMatchFunc,
47+
timeout_seconds: int = DEFAULT_WAIT_UNTIL_TIMEOUT_SECONDS,
48+
interval_seconds: int = DEFAULT_WAIT_UNTIL_INTERVAL_SECONDS,
49+
) -> None:
50+
"""Waits until an EKS cluster with the supplied name is returned from the EKS API
51+
and the matching functor returns True.
52+
53+
Usage:
54+
from e2e.common.waiter import wait_until, status_matches
55+
56+
wait_until(
57+
cluster_name,
58+
status_matches("ACTIVE"),
59+
)
60+
61+
Raises:
62+
pytest.fail upon timeout
63+
"""
64+
now = datetime.datetime.now()
65+
timeout = now + datetime.timedelta(seconds=timeout_seconds)
66+
67+
while not match_fn(get(eks_cluster_name)):
68+
if datetime.datetime.now() >= timeout:
69+
pytest.fail("failed to match Cluster before timeout")
70+
time.sleep(interval_seconds)
71+
72+
73+
def wait_until_deleted(
74+
eks_cluster_name: str,
75+
timeout_seconds: int = DEFAULT_WAIT_UNTIL_DELETED_TIMEOUT_SECONDS,
76+
interval_seconds: int = DEFAULT_WAIT_UNTIL_DELETED_INTERVAL_SECONDS,
77+
) -> None:
78+
"""Waits until an EKS cluster with the supplied name is no longer returned from
79+
the EKS API.
80+
81+
Usage:
82+
from e2e.common.waiter import wait_until_deleted
83+
84+
wait_until_deleted(cluster_name)
85+
86+
Raises:
87+
pytest.fail upon timeout or if the EKS cluster goes to any other status
88+
other than 'DELETING'
89+
"""
90+
now = datetime.datetime.now()
91+
timeout = now + datetime.timedelta(seconds=timeout_seconds)
92+
93+
while True:
94+
if datetime.datetime.now() >= timeout:
95+
pytest.fail("Timed out waiting for cluster to be deleted in EKS API")
96+
time.sleep(interval_seconds)
97+
98+
latest = get(eks_cluster_name)
99+
if latest is None:
100+
break
101+
102+
if latest['status'] != "DELETING":
103+
pytest.fail(
104+
"Status is not 'DELETING' for EKS cluster that was "
105+
"deleted. Status is " + latest['status']
106+
)
107+
108+
def get(eks_cluster_name):
109+
"""Returns a dict containing the EKS cluster record from the EKS API.
110+
111+
If no such cluster exists, returns None.
112+
"""
113+
c = boto3.client('eks')
114+
try:
115+
resp = c.describe_cluster(name=eks_cluster_name)
116+
assert 'cluster' in resp
117+
return resp['cluster']
118+
except c.exceptions.ResourceNotFoundException:
119+
return None

test/e2e/tests/test_cluster.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from acktest.resources import random_suffix_name
2727
from e2e import service_marker, CRD_GROUP, CRD_VERSION, load_eks_resource
2828
from e2e.common.types import CLUSTER_RESOURCE_PLURAL
29+
from e2e.common.waiter import wait_until_deleted
2930
from e2e.replacement_values import REPLACEMENT_VALUES
3031

3132
# Time to wait after modifying the CR for the status to change
@@ -38,16 +39,6 @@ def wait_for_cluster_active(eks_client, cluster_name):
3839
waiter = eks_client.get_waiter('cluster_active')
3940
waiter.wait(name=cluster_name)
4041

41-
def wait_for_cluster_deleted(eks_client, cluster_name):
42-
waiter = eks_client.get_waiter('cluster_deleted')
43-
waiter.wait(
44-
name=cluster_name,
45-
WaiterConfig={
46-
'Delay': 30,
47-
'MaxAttempts': 40, # 20 minutes
48-
},
49-
)
50-
5142
def get_and_assert_status(ref: k8s.CustomResourceReference, expected_status: str, expected_synced: bool):
5243
cr = k8s.get_resource(ref)
5344
assert cr is not None
@@ -94,7 +85,7 @@ def simple_cluster(eks_client):
9485
try:
9586
_, deleted = k8s.delete_custom_resource(ref, 3, 10)
9687
assert deleted
97-
wait_for_cluster_deleted(eks_client, cluster_name)
88+
wait_until_deleted(cluster_name)
9889
except:
9990
pass
10091

@@ -163,4 +154,4 @@ def test_create_update_delete_cluster(self, eks_client, simple_cluster):
163154

164155
# Delete the k8s resource on teardown of the module
165156
k8s.delete_custom_resource(ref)
166-
wait_for_cluster_deleted(eks_client, cluster_name)
157+
wait_until_deleted(cluster_name)

0 commit comments

Comments
 (0)