Skip to content

Commit dadc204

Browse files
author
Kumar Gaurav Sharma
committed
Support customResourceReference at Scenario level and ability to override it at Step level
1 parent 3420ded commit dadc204

File tree

7 files changed

+95
-46
lines changed

7 files changed

+95
-46
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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.

test/declarative_test_fwk/helper.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,11 @@ def delete(self, reference: k8s.CustomResourceReference):
8181
sleep(self.DEFAULT_WAIT_SECS)
8282
self.wait_for_delete(reference) # throws exception if wait fails
8383

84-
def assert_expectations(self, expectations: dict, reference: k8s.CustomResourceReference):
84+
def assert_expectations(self, verb: str, input_data: dict, expectations: dict, reference: k8s.CustomResourceReference):
8585
"""
8686
Asserts custom resource reference against supplied expectations
87+
:param verb: expectations after performing the verb (apply, patch, delete)
88+
:param input_data: input data to verb
8789
:param expectations: expectations to assert
8890
:param reference: custom resource reference
8991
:return: None

test/declarative_test_fwk/loader.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"""Test Scenarios loader for Declarative tests framework for custom resources
1515
"""
1616

17-
from declarative_test_fwk import model, helper
17+
from declarative_test_fwk import model
1818
import pytest
1919
import os
2020
from typing import Iterable
@@ -34,11 +34,12 @@ def list_scenarios(scenarios_directory: Path) -> Iterable[Path]:
3434
scenario_file_full_path = join(scenarios_directory, scenario_file)
3535
if not isfile(scenario_file_full_path) or not scenario_file.endswith(".yaml"):
3636
continue
37-
scenarios_list.append(Path(scenario_file_full_path))
37+
scenario = load_scenario(Path(scenario_file_full_path))
38+
scenarios_list.append(pytest.param(Path(scenario_file_full_path),marks=marks(scenario)))
3839
return scenarios_list
3940

4041

41-
def load_scenario(scenario_file: Path, replacements: dict = {}) -> Iterable:
42+
def load_scenario(scenario_file: Path, replacements: dict = {}) -> model.Scenario:
4243
"""
4344
Loads scenario from given scenario_file
4445
:param scenario_file: yaml file containing scenarios
@@ -50,7 +51,7 @@ def load_scenario(scenario_file: Path, replacements: dict = {}) -> Iterable:
5051
replacements["RANDOM_SUFFIX"] = random_suffix_name("", 32)
5152
scenario = model.Scenario(load_resource_file(
5253
scenario_file.parent, scenario_name, additional_replacements=replacements), replacements)
53-
yield pytest.param(scenario, marks=marks(scenario))
54+
return scenario
5455

5556

5657
def idfn(scenario_file_full_path: Path) -> str:

test/declarative_test_fwk/model.py

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,36 +23,55 @@ class Step:
2323
"""
2424
indent = "\t\t"
2525

26-
def __init__(self, config: dict, replacements: dict = {}):
26+
def __init__(self, config: dict, custom_resource_details: dict, replacements: dict = {}):
2727
self.config = config
28+
self.custom_resource_details = custom_resource_details
2829
self.replacements = replacements
2930

3031
self.verb = None
31-
self.input_data = None
32+
self.input_data = {}
3233
self.expectations = None
33-
self.resource_helper = None
3434

3535
# (k8s.CustomResourceReference, ko) to teardown
3636
self.teardown_list = []
3737

38-
supported_verbs=["create", "patch", "delete"]
38+
supported_verbs = ["create", "patch", "delete"]
3939
for verb in supported_verbs:
4040
if verb not in self.config:
4141
continue
4242
self.verb = verb
4343
self.input_data = self.config.get(verb)
44+
if type(self.input_data) is str:
45+
# consider the input as resource name
46+
# confirm that self.custom_resource_details must be provided with same name
47+
if self.custom_resource_details["metadata"]["name"] != self.input_data:
48+
raise ValueError(f"Unable to determine input data for '{self.verb}' at step: {self.id()}")
49+
# self.custom_resource_details will be mixed in into self.input_data
50+
self.input_data = {}
4451
break
4552

46-
if self.input_data:
47-
self.expectations = self.config.get("expect")
48-
self.resource_helper = helper.get_resource_helper(self.input_data.get("kind"))
53+
if len(self.input_data) == 0 and not self.custom_resource_details:
54+
raise ValueError(f"Unable to determine custom resource at step: {self.id()}")
55+
56+
if self.custom_resource_details:
57+
self.input_data = {**self.custom_resource_details, **self.input_data}
58+
self.expectations = self.config.get("expect")
4959

5060
def id(self) -> str:
5161
return self.config.get("id", "")
5262

5363
def description(self) -> str:
5464
return self.config.get("description", "")
5565

66+
def resource_kind(self) -> str:
67+
return self.input_data.get("kind")
68+
69+
def __str__(self) -> str:
70+
return f"Step(id='{self.id()}')"
71+
72+
def __repr__(self) -> str:
73+
return str(self)
74+
5675

5776
class Scenario:
5877
"""
@@ -63,8 +82,9 @@ def __init__(self, config: dict, replacements: dict = {}):
6382
self.config = config
6483
self.test_steps = []
6584
self.replacements = replacements
85+
custom_resource_details = self.config.get("customResourceReference", {})
6686
for step in self.config.get("steps", []):
67-
self.test_steps.append(Step(step, replacements))
87+
self.test_steps.append(Step(step, custom_resource_details.copy(), replacements))
6888

6989
def id(self) -> str:
7090
return self.config.get("id", "")
@@ -80,3 +100,9 @@ def marks(self) -> list:
80100

81101
def steps(self):
82102
return self.test_steps
103+
104+
def __str__(self) -> str:
105+
return f"Scenario(id='{self.id()}')"
106+
107+
def __repr__(self) -> str:
108+
return str(self)

test/declarative_test_fwk/runner.py

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"""Runner for Declarative tests framework scenarios for custom resources
1515
"""
1616

17-
from declarative_test_fwk import model
17+
from declarative_test_fwk import model, helper
1818
import pytest
1919
import sys
2020
import logging
@@ -28,7 +28,7 @@ def run(scenario: model.Scenario) -> None:
2828
if not scenario:
2929
return
3030

31-
logging.info(f"Execute: Scenario: {scenario.id()}")
31+
logging.info(f"Execute: {scenario}")
3232
for step in scenario.steps():
3333
run_step(step)
3434

@@ -40,20 +40,20 @@ def teardown(scenario: model.Scenario) -> None:
4040
if not scenario:
4141
return
4242

43-
logging.info(f"Teardown: Scenario: {scenario.id()}")
43+
logging.info(f"Teardown: {scenario}")
4444
teardown_failures = []
4545
# tear down steps in reverse order
4646
for step in reversed(scenario.steps()):
4747
try:
4848
teardown_step(step)
4949
except:
50-
error = f"Failed to teardown Step: {step.id()}. " \
50+
error = f"Failed to teardown: {step}. " \
5151
f"Unexpected error: {sys.exc_info()[0]}"
5252
teardown_failures.append(error)
5353
logging.debug(error)
5454

5555
if len(teardown_failures) != 0:
56-
teardown_failures.insert(0, f"Failures during teardown. Scenario: {scenario.id()}")
56+
teardown_failures.insert(0, f"Failures during teardown: {scenario}")
5757
failures = "\n\t- ".join(teardown_failures)
5858
logging.error(failures)
5959
pytest.fail(failures)
@@ -68,54 +68,61 @@ def run_step(step: model.Step) -> None:
6868

6969
if not step.verb:
7070
logging.warning(
71-
f"skipped: Step: {step.id()}. No matching verb found."
71+
f"skipped: {step}. No matching verb found."
7272
f" Supported verbs: create, patch, delete.")
7373
return
7474

75+
logging.info(f"Execute: {step}")
7576
if step.verb == "create":
7677
create_resource(step)
7778
elif step.verb == "patch":
7879
patch_resource(step)
7980
elif step.verb == "delete":
80-
pass
81+
delete_resource(step)
8182
assert_expectations(step)
8283

8384

8485
def create_resource(step: model.Step) -> None:
85-
logging.debug(f"create: Step: {step.id()}")
86+
logging.debug(f"create: {step}")
8687
if not step.input_data:
8788
return
88-
89-
(reference, ko) = step.resource_helper.create(step.input_data, step.replacements)
89+
resource_helper = helper.get_resource_helper(step.resource_kind())
90+
(reference, ko) = resource_helper.create(step.input_data, step.replacements)
9091
# track created reference to teardown later
9192
step.teardown_list.append((reference, ko))
9293

9394

9495
def patch_resource(step: model.Step) -> None:
95-
logging.debug(f"patch: Step: {step.id()}")
96+
logging.debug(f"patch: {step}")
9697
if not step.input_data:
9798
return
9899

99-
(reference, ko) = step.resource_helper.patch(step.input_data, step.replacements)
100+
resource_helper = helper.get_resource_helper(step.resource_kind())
101+
(reference, ko) = resource_helper.patch(step.input_data, step.replacements)
100102
# no need to teardown patched reference, its creator should tear it down.
101103

102104

103105
def delete_resource(step: model.Step, reference: k8s.CustomResourceReference = None) -> None:
106+
resource_helper = helper.get_resource_helper(step.resource_kind())
104107
if not reference:
105-
logging.debug(f"delete: Step: {step.id()}")
106-
reference = step.resource_helper.custom_resource_reference(step.input_data, step.replacements)
108+
logging.debug(f"delete: {step}")
109+
reference = resource_helper.custom_resource_reference(step.input_data, step.replacements)
107110

108-
step.resource_helper.delete(reference)
111+
resource_helper.delete(reference)
109112

110113

111114
def assert_expectations(step: model.Step) -> None:
112-
logging.debug(f"assert: Step: {step.id()}")
115+
logging.info(f"assert: {step}")
113116
if not step.expectations:
114117
return
115118

116-
resource_helper = step.resource_helper
119+
resource_helper = helper.get_resource_helper(step.resource_kind())
117120
reference = resource_helper.custom_resource_reference(step.input_data, step.replacements)
118-
resource_helper.assert_expectations(step.expectations, reference)
121+
try:
122+
resource_helper.assert_expectations(step.verb, step.input_data, step.expectations, reference)
123+
except AssertionError as ae:
124+
logging.error(f"AssertionError at {step}")
125+
raise ae
119126

120127

121128
def teardown_step(step: model.Step) -> None:
@@ -125,7 +132,7 @@ def teardown_step(step: model.Step) -> None:
125132
if not step or len(step.teardown_list) == 0:
126133
return
127134

128-
logging.info(f"teardown: Step: {step.id()}")
135+
logging.info(f"teardown: {step}")
129136

130137
for (reference, _) in step.teardown_list:
131138
if reference:

test/e2e/scenarios/scenario1.yaml

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ usecases:
66
#marks:
77
# - slow
88
# - blocked
9+
customResourceReference:
10+
apiVersion: $CRD_GROUP/$CRD_VERSION
11+
kind: ReplicationGroup
12+
metadata:
13+
name: test$RANDOM_SUFFIX
914
steps:
1015
- id: "create_CMD_replication_group"
1116
description: "Initial config"
1217
create:
13-
apiVersion: $CRD_GROUP/$CRD_VERSION
14-
kind: ReplicationGroup
15-
metadata:
16-
name: test$RANDOM_SUFFIX
1718
spec:
1819
engine: redis
1920
replicationGroupID: test$RANDOM_SUFFIX
@@ -28,10 +29,6 @@ steps:
2829
- id: "invalid_cacheNodeType_on_CMD_replication_group_causes_Terminal_condition"
2930
description: "Negative test include transitEncryptionEnabled"
3031
patch:
31-
apiVersion: $CRD_GROUP/$CRD_VERSION
32-
kind: ReplicationGroup
33-
metadata:
34-
name: test$RANDOM_SUFFIX
3532
spec:
3633
cacheNodeType: cache.micro # invalid value
3734
expect:
@@ -40,8 +37,4 @@ steps:
4037
ACK.Terminal: "True"
4138
- id: "delete_CMD_RG"
4239
description: "Delete cluster mode disabled replication group"
43-
delete:
44-
apiVersion: $CRD_GROUP/$CRD_VERSION
45-
kind: ReplicationGroup
46-
metadata:
47-
name: test$RANDOM_SUFFIX
40+
delete: test$RANDOM_SUFFIX

test/e2e/declarative_tests.py renamed to test/e2e/tests/test_scenarios.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@
2929

3030
@helper.resource_helper("ReplicationGroup")
3131
class ReplicationGroupHelper(helper.ResourceHelper):
32+
def assert_expectations(self, verb: str, input_data: dict, expectations: dict,
33+
reference: k8s.CustomResourceReference):
34+
# default assertions
35+
super().assert_expectations(verb, input_data, expectations, reference)
36+
37+
# perform custom server side checks based on:
38+
# verb, input data to verb, expectations for given resource
39+
3240
"""
3341
Helper for replication group scenarios
3442
"""
@@ -83,7 +91,7 @@ def scenario(request, input_replacements):
8391

8492

8593
@service_marker
86-
class TestSuite:
94+
class TestScenarios:
8795
"""
8896
Declarative scenarios based test suite
8997
"""

0 commit comments

Comments
 (0)