Skip to content

Commit

Permalink
feat: Introduce 'infection' logic (#512)
Browse files Browse the repository at this point in the history
Co-authored-by: Alan Christie <[email protected]>
  • Loading branch information
alanbchristie and Alan Christie authored Feb 1, 2024
1 parent f496abe commit 9c74317
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 0 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,26 @@ In `settings.py`, this is controlled by setting the value of `FRAGALYSIS_BACKEND
which is also exposed in the developer docker-compose file.
To enable it, you need to set it to a valid Sentry DNS value.

## Deployment mode
The stack can be deployed in one of tweo modes: - `DEVELOPMENT` or `PRODUCTION`.
The mode is controlled by the `DEPLOYMENT_MODE` environment variable and is used
by the backend in order to tailor the behaviour of the application.

In `PRODUCTION` mode the API is typically a little more strict than in `DEVELOPMENT` mode.

## Forced errors ("infections")
In order to allow error paths of various elements of the stack to be tested, the
developer can _inject_ specific errors ("infections"). This is
achieved by setting the environment variable `INFECTIONS` in the `docker-compose.yml` file
or, for kubernetes deployments, using the ansible variable `stack_infections`.

Known errors are documented in the `api/infections.py` module. To induce the error
(at thew appropriate point in the stack) provide the infection name as the value of the
`INFECTIONS` environment variable. You can provide more than one name by separating
them with a comma.

Infections are ignored in `PRODUCTION` mode.

## Compiling the documentation
Because the documentation uses Sphinx and its `autodoc` module, compiling the
documentation needs all the application requirements. As this is often impractical
Expand Down
39 changes: 39 additions & 0 deletions api/infections.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# infections.py
#
# A utility that provides a list of infections (forced internal errors).
# Infections are injected into the application via the environment variable
# 'INFECTIONS', a comma-separated list of infection names.

import os
from typing import Dict, Set

from api.utils import deployment_mode_is_production

# The built-in set of infections.
# Must be lowercase, but user can use any case in the environment variable.
# Define every name as a constant, and add it and a description to the _CATALOGUE.
INFECTION_STRUCTURE_DOWNLOAD: str = 'structure-download'

# The index is the short-form name of the infection, and the value is the
# description of the infection.
_CATALOGUE: Dict[str, str] = {
INFECTION_STRUCTURE_DOWNLOAD: 'An error in the DownloadStructures view'
}

# What infection have been set?
_INFECTIONS: str = os.environ.get('INFECTIONS', '').lower()


def have_infection(name: str) -> bool:
"""Returns True if we've been given the named infection.
Infections are never present in production mode."""
return False if deployment_mode_is_production() else name in _get_infections()


def _get_infections() -> Set[str]:
if _INFECTIONS == '':
return set()
infections: set[str] = {
infection for infection in _INFECTIONS.split(',') if infection in _CATALOGUE
}
return infections
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ services:
AUTHENTICATE_UPLOAD: ${AUTHENTICATE_UPLOAD:-True}
DEPLOYMENT_MODE: 'development'
POSTGRESQL_USER: postgres
# Comma-separated dforced errors (infections?)
INFECTIONS: ''
# Celery tasks need to run synchronously
CELERY_TASK_ALWAYS_EAGER: 'True'
# Error reporting and default/root log-level
Expand Down
6 changes: 6 additions & 0 deletions viewer/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from rest_framework.response import Response
from rest_framework.views import APIView

from api.infections import INFECTION_STRUCTURE_DOWNLOAD, have_infection
from api.security import ISpyBSafeQuerySet
from api.utils import get_highlighted_diffs, get_params, pretty_request
from viewer import filters, models, serializers
Expand Down Expand Up @@ -1508,6 +1509,11 @@ def create(self, request):
}
return Response(content, status=status.HTTP_404_NOT_FOUND)

# Forced errors?
if have_infection(INFECTION_STRUCTURE_DOWNLOAD):
content = {'message': f'Download Error! ({INFECTION_STRUCTURE_DOWNLOAD})'}
return Response(content, status=status.HTTP_404_NOT_FOUND)

filename_url = create_or_return_download_link(request, target, site_obvs)
assert filename_url is not None
return Response({"file_url": filename_url})
Expand Down

0 comments on commit 9c74317

Please sign in to comment.