diff --git a/datadog_sync/commands/__init__.py b/datadog_sync/commands/__init__.py index 32724782..438bce28 100644 --- a/datadog_sync/commands/__init__.py +++ b/datadog_sync/commands/__init__.py @@ -6,10 +6,12 @@ from datadog_sync.commands.sync import sync from datadog_sync.commands._import import _import from datadog_sync.commands.diffs import diffs +from datadog_sync.commands.migrate import migrate ALL_COMMANDS = [ sync, _import, diffs, + migrate, ] diff --git a/datadog_sync/commands/_import.py b/datadog_sync/commands/_import.py index 4543b2ab..114d9066 100644 --- a/datadog_sync/commands/_import.py +++ b/datadog_sync/commands/_import.py @@ -5,13 +5,13 @@ from click import command -from datadog_sync.constants import Command from datadog_sync.commands.shared.options import ( common_options, destination_auth_options, source_auth_options, ) from datadog_sync.commands.shared.utils import run_cmd +from datadog_sync.constants import Command @command(Command.IMPORT.value, short_help="Import Datadog resources.") diff --git a/datadog_sync/commands/diffs.py b/datadog_sync/commands/diffs.py index 3cf49241..b102beab 100644 --- a/datadog_sync/commands/diffs.py +++ b/datadog_sync/commands/diffs.py @@ -7,19 +7,19 @@ from datadog_sync.commands.shared.options import ( common_options, - source_auth_options, destination_auth_options, - non_import_common_options, + diffs_common_options, + source_auth_options, ) -from datadog_sync.constants import Command from datadog_sync.commands.shared.utils import run_cmd +from datadog_sync.constants import Command @command(Command.DIFFS.value, short_help="Log resource diffs.") @source_auth_options @destination_auth_options @common_options -@non_import_common_options +@diffs_common_options def diffs(**kwargs): """Log Datadog resources diffs.""" run_cmd(Command.DIFFS, **kwargs) diff --git a/datadog_sync/commands/migrate.py b/datadog_sync/commands/migrate.py new file mode 100644 index 00000000..37f23181 --- /dev/null +++ b/datadog_sync/commands/migrate.py @@ -0,0 +1,27 @@ +# Unless explicitly stated otherwise all files in this repository are licensed +# under the 3-clause BSD style license (see LICENSE). +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019 Datadog, Inc. + +from click import command + +from datadog_sync.commands.shared.options import ( + common_options, + destination_auth_options, + diffs_common_options, + source_auth_options, + sync_common_options, +) +from datadog_sync.commands.shared.utils import run_cmd +from datadog_sync.constants import Command + + +@command(Command.MIGRATE.value, short_help="Migrate Datadog resources from one datacenter to another.") +@source_auth_options +@destination_auth_options +@common_options +@diffs_common_options +@sync_common_options +def migrate(**kwargs): + """Migrate Datadog resources from one datqaacenter to another.""" + run_cmd(Command.MIGRATE, **kwargs) diff --git a/datadog_sync/commands/shared/options.py b/datadog_sync/commands/shared/options.py index d8b334d1..54627903 100644 --- a/datadog_sync/commands/shared/options.py +++ b/datadog_sync/commands/shared/options.py @@ -94,7 +94,7 @@ def click_config_file_provider(ctx: Context, opts: CustomOptionClass, value: Non type=int, default=60, show_default=True, - help="The HTTP request retry timeout period. Defaults to 60s", + help="The HTTP request retry timeout period in seconds.", cls=CustomOptionClass, ), option( @@ -104,7 +104,7 @@ def click_config_file_provider(ctx: Context, opts: CustomOptionClass, value: Non type=int, default=30, show_default=True, - help="The HTTP request timeout period. Defaults to 30s", + help="The HTTP request timeout period in seconds.", cls=CustomOptionClass, ), option( @@ -126,6 +126,7 @@ def click_config_file_provider(ctx: Context, opts: CustomOptionClass, value: Non "--max-workers", envvar=constants.MAX_WORKERS, default=100, + show_default=True, required=False, type=int, help="Max number of workers when running operations in multi-threads.", @@ -136,6 +137,7 @@ def click_config_file_provider(ctx: Context, opts: CustomOptionClass, value: Non envvar=constants.DD_FILTER_OPERATOR, required=False, default="OR", + show_default=True, help="Filter operator when multiple filters are passed. Supports `AND` or `OR`.", cls=CustomOptionClass, ), @@ -161,7 +163,8 @@ def click_config_file_provider(ctx: Context, opts: CustomOptionClass, value: Non default=True, show_default=True, help="Enables validation of the provided API during client initialization. On import, " - "only source api key is validated. On sync/diffs, only destination api key is validated.", + "only source api key is validated. On sync/diffs, only destination api key is validated. " + "On migrate, both source and destination api keys are validated.", cls=CustomOptionClass, ), option( @@ -169,13 +172,14 @@ def click_config_file_provider(ctx: Context, opts: CustomOptionClass, value: Non type=bool, required=False, default=True, + show_default=True, help="Enables sync-cli metrics being sent to both source and destination", cls=CustomOptionClass, ), ] -_non_import_common_options = [ +_diffs_common_options = [ option( "--skip-failed-resource-connections", type=bool, @@ -199,6 +203,29 @@ def click_config_file_provider(ctx: Context, opts: CustomOptionClass, value: Non ] +_sync_common_options = [ + option( + "--force-missing-dependencies", + required=False, + is_flag=True, + default=False, + show_default=True, + help="Force importing and syncing resources that could be potential dependencies to the requested resources.", + cls=CustomOptionClass, + ), + option( + "--create-global-downtime", + required=False, + is_flag=True, + default=False, + show_default=True, + help="Scheduled downtime is meant to be removed during failover when " + "user determines monitors have enough telemetry to trigger appropriately.", + cls=CustomOptionClass, + ), +] + + def source_auth_options(func: Callable) -> Callable: return _build_options_helper(func, _source_auth_options) @@ -211,8 +238,12 @@ def common_options(func: Callable) -> Callable: return _build_options_helper(func, _common_options) -def non_import_common_options(func: Callable) -> Callable: - return _build_options_helper(func, _non_import_common_options) +def diffs_common_options(func: Callable) -> Callable: + return _build_options_helper(func, _diffs_common_options) + + +def sync_common_options(func: Callable) -> Callable: + return _build_options_helper(func, _sync_common_options) def _build_options_helper(func: Callable, options: List[Callable]) -> Callable: diff --git a/datadog_sync/commands/shared/utils.py b/datadog_sync/commands/shared/utils.py index 0c6a3912..36f7f2fb 100644 --- a/datadog_sync/commands/shared/utils.py +++ b/datadog_sync/commands/shared/utils.py @@ -17,7 +17,7 @@ def run_cmd(cmd: Command, **kwargs): asyncio.run(run_cmd_async(cfg, handler, cmd)) except KeyboardInterrupt: cfg.logger.error("Process interrupted by user") - if cmd == Command.SYNC: + if cmd in [Command.SYNC, Command.MIGRATE]: cfg.logger.info("Writing synced resources to disk before exit...") cfg.state.dump_state() exit(0) @@ -41,6 +41,9 @@ async def run_cmd_async(cfg: Configuration, handler: ResourcesHandler, cmd: Comm await handler.apply_resources() elif cmd == Command.DIFFS: await handler.diffs() + elif cmd == Command.MIGRATE: + await handler.import_resources() + await handler.apply_resources() else: cfg.logger.error(f"Command {cmd.value} not found") return diff --git a/datadog_sync/commands/sync.py b/datadog_sync/commands/sync.py index d8f02d42..7ef6d92f 100644 --- a/datadog_sync/commands/sync.py +++ b/datadog_sync/commands/sync.py @@ -3,41 +3,25 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2019 Datadog, Inc. -from click import command, option +from click import command -from datadog_sync.constants import Command from datadog_sync.commands.shared.options import ( - CustomOptionClass, common_options, - source_auth_options, destination_auth_options, - non_import_common_options, + diffs_common_options, + source_auth_options, + sync_common_options, ) from datadog_sync.commands.shared.utils import run_cmd +from datadog_sync.constants import Command @command(Command.SYNC.value, short_help="Sync Datadog resources to destination.") @source_auth_options @destination_auth_options @common_options -@non_import_common_options -@option( - "--force-missing-dependencies", - required=False, - is_flag=True, - default=False, - help="Force importing and syncing resources that could be potential dependencies to the requested resources.", - cls=CustomOptionClass, -) -@option( - "--create-global-downtime", - required=False, - is_flag=True, - default=False, - help="Scheduled downtime is meant to be removed during failover when " - "user determines monitors have enough telemetry to trigger appropriately.", - cls=CustomOptionClass, -) +@diffs_common_options +@sync_common_options def sync(**kwargs): """Sync Datadog resources to destination.""" run_cmd(Command.SYNC, **kwargs) diff --git a/datadog_sync/constants.py b/datadog_sync/constants.py index 99365324..cd2e2aa7 100644 --- a/datadog_sync/constants.py +++ b/datadog_sync/constants.py @@ -38,6 +38,7 @@ class Command(Enum): IMPORT = "import" SYNC = "sync" DIFFS = "diffs" + MIGRATE = "migrate" # Origin diff --git a/datadog_sync/utils/configuration.py b/datadog_sync/utils/configuration.py index 1056beb2..e55b1045 100644 --- a/datadog_sync/utils/configuration.py +++ b/datadog_sync/utils/configuration.py @@ -57,12 +57,12 @@ async def init_async(self, cmd: Command): # Validate the clients. For import we only validate the source client # For sync/diffs we validate the destination client. if self.validate: - if cmd in [Command.SYNC, Command.DIFFS]: + if cmd in [Command.SYNC, Command.DIFFS, Command.MIGRATE]: try: await _validate_client(self.destination_client) except Exception: exit(1) - if cmd == Command.IMPORT: + if cmd in [Command.IMPORT, Command.MIGRATE]: try: await _validate_client(self.source_client) except Exception: