Skip to content

Commit

Permalink
feat(options) Add a dump script (#50785)
Browse files Browse the repository at this point in the history
The `sentry config dump` script prints out all the options values. 
It allows you to filter options by flags and to filter options to see
only those set in the DB.
It automatically exclude credentials.

Example of use:
```
sentry config dump -s -f 2048 
```
Would print out all the options key/values set in the DB or settings file that can be updated by the automator (2048 = `FLAG_AUTOMATOR_MODIFIABLE`) and skip credentials.
  • Loading branch information
fpacifici authored Jun 13, 2023
1 parent 7a4f069 commit ce78191
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src/sentry/options/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

__all__ = (
"FLAG_AUTOMATOR_MODIFIABLE",
"FLAG_CREDENTIAL",
"NotWritableReason",
"UnknownOption",
"UpdateChannel",
Expand All @@ -36,6 +37,7 @@
"isset",
"lookup_key",
"register",
"unregister",
"set",
)

Expand All @@ -51,6 +53,7 @@
set = default_manager.set
delete = default_manager.delete
register = default_manager.register
unregister = default_manager.unregister
all = default_manager.all
filter = default_manager.filter
isset = default_manager.isset
Expand Down
53 changes: 53 additions & 0 deletions src/sentry/runner/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,59 @@ def delete(option, no_input):
raise click.ClickException("unknown option: %s" % option)


@config.command()
@click.option(
"--flags",
"-f",
default=0,
help=(
"The flags we want to filter. This is supposed to be a disjunction "
"of all required flags. All the flags provided have to be present in "
"the option definition."
),
)
@click.option(
"--only-set",
"-s",
is_flag=True,
default=False,
help="Skip options that are not set in DB/setting fi this flag is set.",
)
@configuration
def dump(flags: int, only_set: bool) -> None:
"""
Dump the values of all options except for those flagged as credential.
For each option it provides name, value, last update channel, whether
the option is set on the DB or disk.
"""
from django.conf import settings

from sentry import options

all_options = options.all()

for opt in all_options:
if not flags or (flags & opt.flags == flags):
is_set = options.isset(opt.name)
set_on_disk = settings.SENTRY_OPTIONS.get(opt.name)
value = options.get(opt.name)
is_credential = opt.has_any_flag({options.FLAG_CREDENTIAL})
last_update_channel = options.get_last_update_channel(opt.name)

if not only_set or (only_set and is_set):
if is_credential:
click.echo(
f"Option: {opt.name} is a credential. Skipping. Not showing this to you."
)

click.echo(
f"Option: {opt.name}. Set: {is_set}. Set in settings: "
f"{set_on_disk is not None}. "
f"Last channel: {last_update_channel.value if last_update_channel else 'None'}. "
f"Value: {value}"
)


@config.command(name="generate-secret-key")
def generate_secret_key():
"Generate a new cryptographically secure secret key value."
Expand Down
51 changes: 51 additions & 0 deletions tests/sentry/runner/commands/test_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from typing import Generator

import pytest

from sentry import options
from sentry.options.manager import FLAG_AUTOMATOR_MODIFIABLE, FLAG_IMMUTABLE, UpdateChannel
from sentry.runner.commands.config import config
from sentry.testutils import CliTestCase

MSG = "Option: %s. Set: %r. Set in settings: %r. Last channel: %s. Value: %s"


class ConfigOptionsTest(CliTestCase):
command = config

@pytest.fixture(autouse=True, scope="class")
def register_options(self) -> Generator[None, None, None]:
options.register("readonly_option", default=10, flags=FLAG_IMMUTABLE)
options.register("int_option", default=20, flags=FLAG_AUTOMATOR_MODIFIABLE)
options.register("str_option", default="blabla", flags=FLAG_AUTOMATOR_MODIFIABLE)
options.register("map_option", default={}, flags=FLAG_AUTOMATOR_MODIFIABLE)
options.register("list_option", default=[1, 2], flags=FLAG_AUTOMATOR_MODIFIABLE)
options.register("drifted_option", default=[], flags=FLAG_AUTOMATOR_MODIFIABLE)
options.register("change_channel_option", default=[], flags=FLAG_AUTOMATOR_MODIFIABLE)
options.register("to_unset_option", default=[], flags=FLAG_AUTOMATOR_MODIFIABLE)

yield

options.unregister("readonly_option")
options.unregister("int_option")
options.unregister("str_option")
options.unregister("map_option")
options.unregister("list_option")
options.unregister("drifted_option")
options.unregister("change_channel_option")
options.unregister("to_unset_option")

def test_dump(self) -> None:
options.set("int_option", 30, channel=UpdateChannel.AUTOMATOR)
options.set("str_option", "blabla2", channel=UpdateChannel.CLI)

rv = self.invoke("dump", "-f 2048", "-s")
assert rv.exit_code == 0, rv.output

output = "\n".join(
[
MSG % ("int_option", True, False, "automator", 30),
MSG % ("str_option", True, False, "cli", "blabla2"),
]
)
assert output in rv.output
14 changes: 13 additions & 1 deletion tests/sentry/runner/commands/test_configoptions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pathlib import Path
from typing import Generator

import pytest

Expand All @@ -19,7 +20,7 @@ class ConfigOptionsTest(CliTestCase):
command = configoptions

@pytest.fixture(autouse=True, scope="class")
def register_options(self) -> None:
def register_options(self) -> Generator[None, None, None]:
options.register("readonly_option", default=10, flags=FLAG_IMMUTABLE)
options.register("int_option", default=20, flags=FLAG_AUTOMATOR_MODIFIABLE)
options.register("str_option", default="blabla", flags=FLAG_AUTOMATOR_MODIFIABLE)
Expand All @@ -29,6 +30,17 @@ def register_options(self) -> None:
options.register("change_channel_option", default=[], flags=FLAG_AUTOMATOR_MODIFIABLE)
options.register("to_unset_option", default=[], flags=FLAG_AUTOMATOR_MODIFIABLE)

yield

options.unregister("readonly_option")
options.unregister("int_option")
options.unregister("str_option")
options.unregister("map_option")
options.unregister("list_option")
options.unregister("drifted_option")
options.unregister("change_channel_option")
options.unregister("to_unset_option")

@pytest.fixture(autouse=True)
def set_options(self) -> None:
options.delete("int_option")
Expand Down

0 comments on commit ce78191

Please sign in to comment.