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

Change create-global-downtime flag to default to true #288

Closed
wants to merge 2 commits into from
Closed
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
11 changes: 11 additions & 0 deletions datadog_sync/commands/shared/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,17 @@ def click_config_file_provider(ctx: Context, opts: CustomOptionClass, value: Non


_common_options = [
option(
"--verify-ddr-status",
envvar=constants.DD_VERIFY_DDR_STATUS,
required=False,
type=bool,
default=True,
show_default=True,
help="Verifies DDR status at the source and destination and will not "
"sync if either is in an ACTIVE or FAILOVER state.",
cls=CustomOptionClass,
),
option(
"--http-client-retry-timeout",
envvar=constants.DD_HTTP_CLIENT_RETRY_TIMEOUT,
Expand Down
17 changes: 17 additions & 0 deletions datadog_sync/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
DD_FILTER_OPERATOR = "DD_FILTER_OPERATOR"
DD_CLEANUP = "DD_CLEANUP"
DD_VALIDATE = "DD_VALIDATE"
DD_VERIFY_DDR_STATUS = "DD_VERIFY_DDR_STATUS"

# Default variables
DEFAULT_API_URL = "https://api.datadoghq.com"
Expand Down Expand Up @@ -59,3 +60,19 @@ class Status(Enum):
SUCCESS = "success"
SKIPPED = "skipped"
FAILURE = "failure"


# DDR Status
class DDR_Status(Enum):
ONBOARDING = 1
PASSIVE = 2
FAILOVER = 3
ACTIVE = 4
RECOVERY = 5


VALID_DDR_STATES = [
DDR_Status.ONBOARDING,
DDR_Status.PASSIVE,
DDR_Status.RECOVERY,
]
35 changes: 35 additions & 0 deletions datadog_sync/utils/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
LOGGER_NAME,
TRUE,
VALIDATE_ENDPOINT,
VALID_DDR_STATES,
)
from datadog_sync.utils.resource_utils import CustomClientHTTPError
from datadog_sync.utils.state import State
Expand All @@ -45,6 +46,7 @@ class Configuration(object):
validate: bool
send_metrics: bool
state: State
verify_ddr_status: bool
resources: Dict[str, BaseResource] = field(default_factory=dict)
resources_arg: List[str] = field(default_factory=list)

Expand All @@ -69,6 +71,28 @@ async def init_async(self, cmd: Command):
exit(1)
self.logger.info("clients validated successfully")

# Don't sync if DDR is active
if self.verify_ddr_status:
try:
await _verify_ddr_status(self.destination_client)
except Exception as err:
self.logger.error(
"The destination DDR verification failed. Use the --verify-ddr-status "
+ f"flag to override, exiting: {err}"
Comment on lines +80 to +81

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 Code Quality Violation

use f-string or .format to format strings (...read more)

Concatenation of multiple strings is not efficient and make the code hard to read and understand.

Instead of concatenating multiple strings, use an f-string or a format string.

Learn More

View in Datadog  Leave us feedback  Documentation

)
exit(1)
try:
await _verify_ddr_status(self.source_client)
except Exception as err:
self.logger.error(
"The source DDR verification failed. Use the --verify-ddr-status "
+ f"flag to override, exiting: {err}"
Comment on lines +88 to +89

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 Code Quality Violation

use f-string or .format to format strings (...read more)

Concatenation of multiple strings is not efficient and make the code hard to read and understand.

Instead of concatenating multiple strings, use an f-string or a format string.

Learn More

View in Datadog  Leave us feedback  Documentation

)
exit(1)
self.logger.info("DDR verified successfully")
else:
self.logger.warning("DDR verification skipped.")

async def exit_async(self):
await self.source_client._end_session()
await self.destination_client._end_session()
Expand Down Expand Up @@ -110,6 +134,7 @@ def build_config(cmd: Command, **kwargs: Optional[Any]) -> Configuration:
max_workers = kwargs.get("max_workers")
create_global_downtime = kwargs.get("create_global_downtime")
validate = kwargs.get("validate")
verify_ddr_status = kwargs.get("verify_ddr_status")

cleanup = kwargs.get("cleanup")
if cleanup:
Expand Down Expand Up @@ -137,6 +162,7 @@ def build_config(cmd: Command, **kwargs: Optional[Any]) -> Configuration:
validate=validate,
send_metrics=send_metrics,
state=state,
verify_ddr_status=verify_ddr_status,
)

# Initialize resource classes
Expand Down Expand Up @@ -187,6 +213,15 @@ def init_resources(cfg: Configuration) -> Dict[str, BaseResource]:
return resources


async def _verify_ddr_status(client: CustomClient) -> None:
ddr_state = await client.get_ddr_status()
if not ddr_state:
raise ConnectionError("Could not get DDR status")

if ddr_state not in VALID_DDR_STATES:
raise ValueError(f"DDR status: {ddr_state.name}")


async def _validate_client(client: CustomClient) -> None:
logger = logging.getLogger(LOGGER_NAME)
try:
Expand Down
22 changes: 21 additions & 1 deletion datadog_sync/utils/custom_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import aiohttp
import certifi

from datadog_sync.constants import LOGGER_NAME, Metrics
from datadog_sync.constants import DDR_Status, LOGGER_NAME, Metrics
from datadog_sync.utils.resource_utils import CustomClientHTTPError

log = logging.getLogger(LOGGER_NAME)
Expand Down Expand Up @@ -185,6 +185,26 @@ async def send_metric(self, metric: str, tags: List[str] = None) -> None:
}
await self.post(path, body)

async def get_ddr_status(self) -> Dict:
path = "/api/v2/hamr"
resp = await self.get(path)
if not resp:
return None

data = resp.get("data")
if not data:
return None

attributes = data.get("attributes")
if not attributes:
return None

ddr_status = attributes.get("HamrStatus")
if not ddr_status:
return None

return DDR_Status(ddr_status)


def build_default_headers(auth_obj: Dict[str, str]) -> Dict[str, str]:
headers = {
Expand Down
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ def config():
create_global_downtime=False,
validate=False,
state=State(),
verify_ddr_status=False,
send_metrics=True,
)

Expand Down
9 changes: 9 additions & 0 deletions tests/integration/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def test_resource_import(self, runner, caplog):
"--validate=false",
f"--resources={self.resource_type}",
f"--filter={self.filter}",
"--verify-ddr-status=False",
],
)
assert 0 == ret.exit_code
Expand All @@ -60,6 +61,7 @@ def test_resource_import(self, runner, caplog):
f"--resources={self.resource_type}",
f"--filter={self.filter}",
"--skip-failed-resource-connections=false",
"--verify-ddr-status=False",
],
)
assert 0 == ret.exit_code
Expand All @@ -74,6 +76,7 @@ def test_resource_sync(self, runner, caplog):
"--validate=false",
f"--resources={self.resource_type}",
f"--filter={self.filter}",
"--verify-ddr-status=False",
]
if self.force_missing_deps:
cmd_list.append("--force-missing-dependencies")
Expand Down Expand Up @@ -115,6 +118,7 @@ def test_resource_update_sync(self, runner, caplog):
"--validate=false",
f"--resources={self.resource_type}",
f"--filter={self.filter}",
"--verify-ddr-status=False",
],
)
assert caplog.text
Expand All @@ -128,6 +132,7 @@ def test_resource_update_sync(self, runner, caplog):
"--validate=false",
f"--resources={self.resource_type}",
f"--filter={self.filter}",
"--verify-ddr-status=False",
],
)
assert 0 == ret.exit_code
Expand All @@ -141,6 +146,7 @@ def test_resource_update_sync(self, runner, caplog):
"--validate=false",
f"--resources={self.resource_type}",
f"--filter={self.filter}",
"--verify-ddr-status=False",
],
)
assert 0 == ret.exit_code
Expand All @@ -162,6 +168,7 @@ def test_no_resource_diffs(self, runner, caplog):
"--validate=false",
f"--resources={self.resource_type}",
f"--filter={self.filter}",
"--verify-ddr-status=False",
],
)

Expand All @@ -188,6 +195,7 @@ def test_resource_cleanup(self, runner, caplog):
"--validate=false",
f"--resources={self.resource_type}",
f"--filter={self.resources_to_preserve_filter}",
"--verify-ddr-status=False",
],
)
assert 0 == ret.exit_code
Expand All @@ -201,6 +209,7 @@ def test_resource_cleanup(self, runner, caplog):
f"--resources={self.resource_type}",
f"--filter={self.filter}",
"--cleanup=force",
"--verify-ddr-status=False",
],
)

Expand Down
1 change: 1 addition & 0 deletions tests/integration/resources/test_logs_indexes.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def test_resource_cleanup(self, runner, caplog):
f"--resources={self.resource_type}",
f"--filter={self.filter}",
"--cleanup=force",
"--verify-ddr-status=False",
],
)
assert 0 == ret.exit_code
Expand Down
30 changes: 26 additions & 4 deletions tests/integration/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,17 @@ def setup(self, tmpdir_factory):
def test_import(self, runner, caplog):
caplog.set_level(logging.DEBUG)
# Import
ret = runner.invoke(cli, ["import", "--validate=false", f"--resources={self.resources}"])
ret = runner.invoke(
cli, ["import", "--validate=false", f"--resources={self.resources}", "--verify-ddr-status=false"]
)
assert 0 == ret.exit_code

caplog.clear()
# Check diff
ret = runner.invoke(cli, ["diffs", "--validate=false", "--skip-failed-resource-connections=False"])
ret = runner.invoke(
cli,
["diffs", "--validate=false", "--skip-failed-resource-connections=False", "--verify-ddr-status=false"],
)
# assert diffs are produced
assert caplog.text
assert 0 == ret.exit_code
Expand All @@ -62,6 +67,7 @@ def test_sync(self, runner, caplog):
"--validate=false",
f"--resources={self.resources}",
"--skip-failed-resource-connections=False",
"--verify-ddr-status=false",
],
)
assert 0 == ret.exit_code
Expand All @@ -75,6 +81,7 @@ def test_sync(self, runner, caplog):
"--validate=false",
f"--resources={self.resources}",
"--skip-failed-resource-connections=False",
"--verify-ddr-status=false",
],
)
# assert no diffs are produced
Expand All @@ -99,6 +106,7 @@ def test_cleanup(self, runner, caplog):
"--resources=roles,users",
"--filter=Type=roles;Name=attributes.user_count;Value=[^0]+;Operator=SubString",
"--filter=Type=users;Name=attributes.status;Value=Active",
"--verify-ddr-status=false",
],
)
assert 0 == ret.exit_code
Expand All @@ -112,6 +120,7 @@ def test_cleanup(self, runner, caplog):
f"--resources={self.resources}",
"--cleanup=force",
"--skip-failed-resource-connections=False",
"--verify-ddr-status=false",
],
)
if ret.exit_code != 0:
Expand All @@ -128,6 +137,7 @@ def test_cleanup(self, runner, caplog):
f"--resources={self.resources}",
"--cleanup=force",
"--skip-failed-resource-connections=False",
"--verify-ddr-status=false",
],
)

Expand All @@ -142,6 +152,7 @@ def test_cleanup(self, runner, caplog):
"--validate=false",
f"--resources={self.resources}",
"--skip-failed-resource-connections=False",
"--verify-ddr-status=false",
],
)
# assert no diffs are produced
Expand All @@ -154,12 +165,23 @@ def test_cleanup(self, runner, caplog):
def test_migrate(self, runner, caplog):
caplog.set_level(logging.DEBUG)
# Migrate
ret = runner.invoke(cli, ["migrate", "--validate=false", f"--resources={self.resources}"])
ret = runner.invoke(
cli,
[
"migrate",
"--validate=false",
f"--resources={self.resources}",
"--verify-ddr-status=false",
],
)
assert 0 == ret.exit_code

caplog.clear()
# Check diff
ret = runner.invoke(cli, ["diffs", "--validate=false", "--skip-failed-resource-connections=False"])
ret = runner.invoke(
cli,
["diffs", "--validate=false", "--skip-failed-resource-connections=False", "--verify-ddr-status=false"],
)
# assert diffs are produced
assert caplog.text
assert 0 == ret.exit_code
Loading