Skip to content

Commit

Permalink
revamp the sample app with chaos api (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
HarshCasper authored Jul 26, 2024
1 parent 4672a70 commit 659a64b
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 33 deletions.
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Simulating outages for local cloud apps with LocalStack!
# Simulating outages for local cloud apps with LocalStack's Chaos API

LocalStack's [Outages extension](https://pypi.org/project/localstack-extension-outages/) enables you to start a local outage, right on your developer machine. In this demo, we set up an HTTP CRUD API functioning as a Product Management System, and use the Outages extension to simulate an outage in the DynamoDB table. We'll further use `pytest` to test the application's behavior during the outage. For a more detailed walkthrough, check out our [Simulating Outages for Local Cloud Apps with LocalStack]() blog.
LocalStack's [Chaos API](https://docs.localstack.cloud/user-guide/chaos-engineering/chaos-api/) enables you to simulate a local outage, right on your developer machine. In this demo, we set up an HTTP CRUD API functioning as a Product Management System, and use the Chaos API to simulate an outage in the DynamoDB table. We'll further use `pytest` to test the application's behavior during the outage.

## Architecture

Expand Down Expand Up @@ -41,9 +41,8 @@ docker-compose up

The Docker Compose configuration will:

* Install the Outages extension.
* Create your cloud infrastructure.
* Start the LocalStack container.
* Create your cloud infrastructure.

### Test the infrastructure

Expand Down Expand Up @@ -73,7 +72,7 @@ You can navigate to the [DynamoDB Resource Browser](https://app.localstack.cloud
You can simulate an outage in your local DynamoDB table by running the following command:

```bash
curl --location --request POST 'http://outages.localhost.localstack.cloud:4566/outages' \
curl --location --request POST 'http://localhost.localstack.cloud:4566/_localstack/chaos/faults' \
--header 'Content-Type: application/json' \
--data '
[
Expand Down Expand Up @@ -114,7 +113,7 @@ A DynamoDB error occurred. Message sent to queue.
To stop the outage, run the following command:

```bash
curl --location --request POST 'http://outages.localhost.localstack.cloud:4566/outages' \
curl --location --request POST 'http://localhost.localstack.cloud:4566/_localstack/chaos/faults' \
--header 'Content-Type: application/json' \
--data '[]'
```
Expand Down
2 changes: 0 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ services:
- LOCALSTACK_HOST=localstack
- LAMBDA_DOCKER_NETWORK=ls_network
- LOCALSTACK_AUTH_TOKEN=${LOCALSTACK_AUTH_TOKEN:?}
- LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT=60
- EXTENSION_AUTO_INSTALL=localstack-extension-outages
- LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT=600
volumes:
- "./volume:/var/lib/localstack"
Expand Down
46 changes: 21 additions & 25 deletions tests/test_outage.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,49 @@
import requests

# Replace with your LocalStack endpoint
LOCALSTACK_ENDPOINT = "http://localhost:4566"
LOCALSTACK_ENDPOINT = "http://localhost.localstack.cloud:4566"
CHAOS_ENDPOINT = f"{LOCALSTACK_ENDPOINT}/_localstack/chaos/faults"

# Replace with your LocalStack DynamoDB table name
DYNAMODB_TABLE_NAME = "Products"

# Replace with your Lambda function names
LAMBDA_FUNCTIONS = ["add-product", "get-product", "process-product-events"]


@pytest.fixture(scope="module")
def dynamodb_resource():
return boto3.resource("dynamodb", endpoint_url=LOCALSTACK_ENDPOINT)


@pytest.fixture(scope="module")
def lambda_client():
return boto3.client("lambda", endpoint_url=LOCALSTACK_ENDPOINT)


def test_dynamodb_table_exists(dynamodb_resource):
tables = dynamodb_resource.tables.all()
table_names = [table.name for table in tables]
assert DYNAMODB_TABLE_NAME in table_names


def test_lambda_functions_exist(lambda_client):
functions = lambda_client.list_functions()["Functions"]
function_names = [func["FunctionName"] for func in functions]
assert all(func_name in function_names for func_name in LAMBDA_FUNCTIONS)


def test_dynamodb_outage():
def initiate_dynamodb_outage():
outage_payload = [{"service": "dynamodb", "region": "us-east-1"}]
requests.post(
"http://outages.localhost.localstack.cloud:4566/outages", json=outage_payload
)
requests.post(CHAOS_ENDPOINT, json=outage_payload)
return outage_payload

def check_outage_status(expected_status):
outage_status = requests.get(CHAOS_ENDPOINT).json()
assert outage_status == expected_status

def stop_dynamodb_outage():
requests.post(CHAOS_ENDPOINT, json=[])
check_outage_status([])

def test_dynamodb_outage(dynamodb_resource):
# Initiate DynamoDB outage
outage_payload = initiate_dynamodb_outage()

# Make a request to DynamoDB and assert an error
url = "http://12345.execute-api.localhost.localstack.cloud:4566/dev/productApi"
Expand All @@ -52,31 +59,20 @@ def test_dynamodb_outage():
}

response = requests.post(url, headers=headers, json=data)

assert "error" in response.text

# Check if outage is running
outage_status = requests.get(
"http://outages.localhost.localstack.cloud:4566/outages"
).json()
assert outage_payload == outage_status
check_outage_status(outage_payload)

# Stop the outage
requests.post("http://outages.localhost.localstack.cloud:4566/outages", json=[])

# Check if outage is stopped
outage_status = requests.get(
"http://outages.localhost.localstack.cloud:4566/outages"
).json()
assert not outage_status
stop_dynamodb_outage()

# Wait for a few seconds
time.sleep(60)

# Query if there are items in DynamoDB table
dynamodb = boto3.resource("dynamodb", endpoint_url=LOCALSTACK_ENDPOINT)
table = dynamodb.Table(DYNAMODB_TABLE_NAME)
table = dynamodb_resource.Table(DYNAMODB_TABLE_NAME)
response = table.scan()
items = response["Items"]
print(items)
assert "Super Widget" in [item["name"] for item in items]
assert any(item["name"] == "Super Widget" for item in items)

0 comments on commit 659a64b

Please sign in to comment.