1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-04-13 07:47:14 +03:00

Added additional data fields to killswitches

These fields will be used for various things moving forward. The
currently intended use is to redact fields when sending data to
inara/edsm/eddn in case Frontier accidentally includes something that is
unwanted
This commit is contained in:
A_D 2021-07-03 15:17:31 +02:00
parent 57cf4ab78a
commit 2583d07baf
No known key found for this signature in database
GPG Key ID: 4BE9EB7DF45076C4
2 changed files with 133 additions and 46 deletions

View File

@ -12,23 +12,33 @@ Killswitches are stored in a JSON file that is queried by EDMC on startup. The f
| `last_updated` | `string` | When last the kill switches were updated (for human use only) |
| `kill_switches` | `array` | The kill switches this file contains (expanded below) |
The `kill_switches` array contains kill switch objects. Each contains two fields:
The `kill_switches` array contains kill switch objects. Each contains the following fields:
| Key | Type | Description |
| --------: | :-------------------------: | :--------------------------------------------------------------------------- |
| `version` | `version spec` | The version of EDMC these kill switches apply to (Must be valid semver spec) |
| `kills` | `Dict[str, Dict[str, Any]]` | The different keys to disable, and the reason for the disable |
Each entry in `kills` contains a `reason` and `additional_data` field. `reason` is self explanatory, however
additional_data can contain various things. They are outlaid below:
| Key | Type | Description |
| -------------: | :---------: | :-------------------------------------------------------------------- |
| `redact_field` | `List[str]` | A list of fields in the matching event to be redacted, if they exist. |
| Key | Type | Description |
| --------: | :--------------: | :--------------------------------------------------------------------------- |
| `version` | `version spec` | The version of EDMC these kill switches apply to (Must be valid semver spec) |
| `kills` | `Dict[str, str]` | The different keys to disable, and the reason for the disable |
An example follows:
```json
{
"version": 1,
"last_updated": "19 October 2020",
"version": 2,
"last_updated": "3 July 2021",
"kill_switches": [
{
"version": "1.0.0",
"kills": {
"plugins.eddn.send": "some reason"
"plugins.eddn.send": {
"reason": "some reason",
"additional_data": {}
}
}
}
]
@ -45,15 +55,17 @@ Plugins may use the killswitch system simply by hosting their own version of the
using `killswitch.get_kill_switches(target='https://example.com/myplugin_killswitches.json')`. The returned object can
be used to query the kill switch set, see the docstrings for more information on specifying versions.
The version of the JSON file will be automatically upgraded if possible by the code KillSwitch code.
## Currently supported killswitch strings
The current recognised (to EDMC and its internal plugins) killswitch strings are as follows:
| Kill Switch | Description |
| :--------------------------------------------------- | :------------------------------------------------------------------------------------------- |
| `plugins.eddn.send` | Disables all use of the send method on EDDN (effectively disables EDDN updates) |
| `plugins.(eddn\|inara\|edsm\|eddb).journal` | Disables all journal processing for EDDN/EDSM/INARA |
| `plugins.(edsm\|inara).worker` | Disables the EDSM/INARA worker thread (effectively disables updates) (does not close thread) |
| `plugins.(eddn\|inara\|edsm).journal.event.$eventname`| Specific events to disable processing for |
| Kill Switch | Description |
| :----------------------------------------------------- | :------------------------------------------------------------------------------------------- |
| `plugins.eddn.send` | Disables all use of the send method on EDDN (effectively disables EDDN updates) |
| `plugins.(eddn\|inara\|edsm\|eddb).journal` | Disables all journal processing for EDDN/EDSM/INARA |
| `plugins.(edsm\|inara).worker` | Disables the EDSM/INARA worker thread (effectively disables updates) (does not close thread) |
| `plugins.(eddn\|inara\|edsm).journal.event.$eventname` | Specific events to disable processing for |
## File location

View File

@ -1,8 +1,12 @@
"""Fetch kill switches from EDMC Repo."""
from typing import Dict, List, NamedTuple, Optional, Union, cast
from __future__ import annotations
from os import kill
from typing import Any, Dict, List, NamedTuple, Optional, TypedDict, Union, cast
import requests
import semantic_version
from copy import deepcopy
from semantic_version.base import Version
import config
import EDMCLogging
@ -10,31 +14,57 @@ import EDMCLogging
logger = EDMCLogging.get_main_logger()
DEFAULT_KILLSWITCH_URL = 'https://raw.githubusercontent.com/EDCD/EDMarketConnector/releases/killswitches.json'
CURRENT_KILLSWITCH_VERSION = 2
_current_version: semantic_version.Version = config.appversion_nobuild()
class KillSwitch(NamedTuple):
class SingleKill(NamedTuple):
"""A single KillSwitch. Possibly with additional data."""
match: str
reason: str
additional_data: Dict[str, Any]
class KillSwitches(NamedTuple):
"""One version's set of kill switches."""
version: semantic_version.SimpleSpec
kills: Dict[str, str]
kills: Dict[str, SingleKill]
@staticmethod
def from_dict(data: KillSwitchSetJSON) -> KillSwitches:
"""Create a KillSwitches instance from a dictionary."""
ks = {}
for match, ks_data in data['kills'].items():
ks[match] = SingleKill(
match=match, reason=ks_data['reason'], additional_data=ks_data.get('additional_data', {})
)
return KillSwitches(version=semantic_version.SimpleSpec(data['version']), kills=ks)
class DisabledResult(NamedTuple):
"""DisabledResult is the result returned from various is_disabled calls."""
disabled: bool
reason: str
kill: Optional[SingleKill]
@ property
def reason(self) -> str:
"""Reason provided for why this killswitch exists."""
return self.kill.reason if self.kill is not None else ""
class KillSwitchSet:
"""Queryable set of kill switches."""
def __init__(self, kill_switches: List[KillSwitch]) -> None:
def __init__(self, kill_switches: List[KillSwitches]) -> None:
self.kill_switches = kill_switches
def get_disabled(self, id: str, *, version=_current_version) -> DisabledResult:
def get_disabled(self, id: str, *, version: Union[Version, str] = _current_version) -> DisabledResult:
"""
Return whether or not the given feature ID is disabled by a killswitch for the given version.
@ -46,9 +76,9 @@ class KillSwitchSet:
if version not in ks.version:
continue
return DisabledResult(id in ks.kills, ks.kills.get(id, ""))
return DisabledResult(id in ks.kills, ks.kills.get(id, None))
return DisabledResult(False, "")
return DisabledResult(False, None)
def is_disabled(self, id: str, *, version: semantic_version.Version = _current_version) -> bool:
"""Return whether or not a given feature ID is disabled for the given version."""
@ -58,7 +88,7 @@ class KillSwitchSet:
"""Return a reason for why the given id is disabled for the given version, if any."""
return self.get_disabled(id, version=version).reason
def kills_for_version(self, version: semantic_version.Version = _current_version) -> List[KillSwitch]:
def kills_for_version(self, version: semantic_version.Version = _current_version) -> List[KillSwitches]:
"""
Get all killswitch entries that apply to the given version.
@ -76,17 +106,23 @@ class KillSwitchSet:
return f'KillSwitchSet(kill_switches={self.kill_switches!r})'
KILL_SWITCH_JSON = List[Dict[str, Union[str, List[str]]]]
KILL_SWITCH_JSON_DICT = Dict[
str, Union[
str, # Last updated
int, # Version
KILL_SWITCH_JSON # kills
]
]
class SingleKillSwitchJSON(TypedDict): # noqa: D101
reason: str
additional_data: Dict[str, Any]
def fetch_kill_switches(target=DEFAULT_KILLSWITCH_URL) -> Optional[KILL_SWITCH_JSON_DICT]:
class KillSwitchSetJSON(TypedDict): # noqa: D101
version: str
kills: Dict[str, SingleKillSwitchJSON]
class KillSwitchJSONFile(TypedDict): # noqa: D101
version: int
last_updated: str
kill_switches: List[KillSwitchSetJSON]
def fetch_kill_switches(target=DEFAULT_KILLSWITCH_URL) -> Optional[KillSwitchJSONFile]:
"""
Fetch the JSON representation of our kill switches.
@ -108,34 +144,70 @@ def fetch_kill_switches(target=DEFAULT_KILLSWITCH_URL) -> Optional[KILL_SWITCH_J
return data
def parse_kill_switches(data: KILL_SWITCH_JSON_DICT) -> List[KillSwitch]:
class _KillSwitchV1(TypedDict):
version: str
kills: Dict[str, str]
class _KillSwitchJSONFileV1(TypedDict):
version: int
last_updated: str
kill_switches: List[_KillSwitchV1]
def _upgrade_kill_switch_dict(data: KillSwitchJSONFile) -> KillSwitchJSONFile:
version = data['version']
if version == CURRENT_KILLSWITCH_VERSION:
return data
if version == 1:
logger.info('Got an old version killswitch file (v1) upgrading!')
to_return: KillSwitchJSONFile = deepcopy(data)
data_v1 = cast(_KillSwitchJSONFileV1, data)
# reveal_type(to_return['kill_switches'])
to_return['kill_switches'] = [
cast(KillSwitchSetJSON, { # I need to cheat here a touch. It is this I promise
'version': d['version'],
'kills': {
match: {'reason': reason, 'additional_data': {}} for match, reason in d['kills'].items()
}
})
for d in data_v1['kill_switches']
]
to_return['version'] = CURRENT_KILLSWITCH_VERSION
return to_return
return data
def parse_kill_switches(data: KillSwitchJSONFile) -> List[KillSwitches]:
"""
Parse kill switch dict to List of KillSwitches.
:param data: dict containing raw killswitch data
:return: a list of all provided killswitches
"""
data = _upgrade_kill_switch_dict(data)
last_updated = data['last_updated']
ks_version = data['version']
logger.info(f'Kill switches last updated {last_updated}')
if ks_version != 1:
logger.warning(f'Unknown killswitch version {ks_version}. Bailing out')
if ks_version != CURRENT_KILLSWITCH_VERSION:
logger.warning(f'Unknown killswitch version {ks_version} (expected {CURRENT_KILLSWITCH_VERSION}). Bailing out')
return []
kill_switches = cast(KILL_SWITCH_JSON, data['kill_switches'])
out: List[KillSwitch] = []
kill_switches = data['kill_switches']
out = []
for idx, ks_data in enumerate(kill_switches):
try:
ver = semantic_version.SimpleSpec(ks_data['version'])
ks = KillSwitches.from_dict(ks_data)
out.append(ks)
except ValueError as e:
logger.warning(f'could not parse killswitch idx {idx}: {e}')
continue
ks = KillSwitch(version=ver, kills=cast(Dict[str, str], ks_data['kills']))
out.append(ks)
except Exception as e:
logger.exception(f'Could not parse killswitch idx {idx}: {e}')
return out
@ -169,6 +241,9 @@ def setup_main_list():
global active
active = KillSwitchSet(parse_kill_switches(data))
logger.trace('Active Killswitches:')
for v in active.kill_switches:
logger.trace(v)
def get_disabled(id: str, *, version: semantic_version.Version = _current_version) -> DisabledResult:
@ -190,6 +265,6 @@ def get_reason(id: str, *, version: semantic_version.Version = _current_version)
return active.get_reason(id, version=version)
def kills_for_version(version: semantic_version.Version = _current_version) -> List[KillSwitch]:
def kills_for_version(version: semantic_version.Version = _current_version) -> List[KillSwitches]:
"""Query the global KillSwitchSet for kills matching a particular version."""
return active.kills_for_version(version)