checks.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. # Copyright Materialize, Inc. and contributors. All rights reserved.
  2. #
  3. # Use of this software is governed by the Business Source License
  4. # included in the LICENSE file at the root of this repository.
  5. #
  6. # As of the Change Date specified in that file, in accordance with
  7. # the Business Source License, use of this software will be governed
  8. # by the Apache License, Version 2.0.
  9. from random import Random
  10. from typing import TYPE_CHECKING
  11. from materialize import buildkite
  12. from materialize.buildkite import BuildkiteEnvVar
  13. from materialize.checks.actions import Testdrive
  14. from materialize.checks.executors import Executor
  15. from materialize.checks.features import Features
  16. from materialize.mz_version import MzVersion
  17. if TYPE_CHECKING:
  18. from materialize.checks.actions import Action
  19. TESTDRIVE_NOP = "$ nop"
  20. class Check:
  21. # Has to be set for the class already, not just in the constructor, so that
  22. # we can change the value for the entire class in the decorator
  23. enabled: bool = True
  24. externally_idempotent: bool = True
  25. def __init__(
  26. self, base_version: MzVersion, rng: Random | None, features: Features | None
  27. ) -> None:
  28. self.base_version = base_version
  29. self.rng = rng
  30. self.features = features
  31. def _can_run(self, e: Executor) -> bool:
  32. return True
  33. def _kafka_broker(self) -> str:
  34. return "BROKER '${testdrive.kafka-addr}', SECURITY PROTOCOL PLAINTEXT"
  35. def _unsafe_schema(self) -> str:
  36. """
  37. :return: the schema containing unsafe functions, such as `mz_sleep`.
  38. """
  39. return "mz_unsafe"
  40. def _default_cluster(self) -> str:
  41. """
  42. :return: name of the cluster created in all environments.
  43. """
  44. return "quickstart"
  45. def initialize(self) -> Testdrive:
  46. return Testdrive(TESTDRIVE_NOP)
  47. def manipulate(self) -> list[Testdrive]:
  48. raise NotImplementedError
  49. def validate(self) -> Testdrive:
  50. """Note that the validation method may be invoked multiple times (depending on the scenario)."""
  51. raise NotImplementedError
  52. def start_initialize(self, e: Executor, a: "Action") -> None:
  53. if self._can_run(e) and self.enabled:
  54. self.current_version = e.current_mz_version
  55. self._initialize = self.initialize()
  56. self._initialize.execute(e, a.mz_service)
  57. def join_initialize(self, e: Executor) -> None:
  58. if self._can_run(e) and self.enabled:
  59. self._initialize.join(e)
  60. def start_manipulate(self, e: Executor, a: "Action") -> None:
  61. if self._can_run(e) and self.enabled:
  62. self.current_version = e.current_mz_version
  63. self._manipulate = self.manipulate()
  64. assert (
  65. len(self._manipulate) == 2
  66. ), f"manipulate() should return a list with exactly 2 elements, but actually returns {len(self._manipulate)} elements"
  67. assert a.phase is not None
  68. self._manipulate[a.phase].execute(e, a.mz_service)
  69. def join_manipulate(self, e: Executor, a: "Action") -> None:
  70. if self._can_run(e) and self.enabled:
  71. assert a.phase is not None
  72. self._manipulate[a.phase].join(e)
  73. def start_validate(self, e: Executor, a: "Action") -> None:
  74. if self._can_run(e) and self.enabled:
  75. self.current_version = e.current_mz_version
  76. self._validate = self.validate()
  77. self._validate.execute(e, a.mz_service)
  78. def join_validate(self, e: Executor) -> None:
  79. if self._can_run(e) and self.enabled:
  80. self._validate.join(e)
  81. def is_running_as_cloudtest(self) -> bool:
  82. return buildkite.get_var(BuildkiteEnvVar.BUILDKITE_STEP_KEY, "") in {
  83. "cloudtest-upgrade",
  84. "testdrive-in-cloudtest",
  85. }
  86. def disabled(ignore_reason: str):
  87. def decorator(cls):
  88. cls.enabled = False
  89. return cls
  90. return decorator
  91. def externally_idempotent(externally_idempotent: bool = True):
  92. def decorator(cls):
  93. cls.externally_idempotent = externally_idempotent
  94. return cls
  95. return decorator