Skip to content

Commit 827a68d

Browse files
committed
Initial release
Signed-off-by: Sylvain Hellegouarch <sh@defuze.org>
1 parent c3bf632 commit 827a68d

File tree

17 files changed

+286
-81
lines changed

17 files changed

+286
-81
lines changed

.github/workflows/build.yaml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ on:
88

99
jobs:
1010
build:
11-
runs-on: ubuntu-latest
11+
runs-on: ubuntu-22.04
1212
steps:
13-
- uses: actions/checkout@v2
13+
- uses: actions/checkout@v3
1414
- name: Set up Python
15-
uses: actions/setup-python@v2
15+
uses: actions/setup-python@v4
1616
with:
1717
python-version: '3.7'
1818
- name: Install dependencies
@@ -22,11 +22,11 @@ jobs:
2222
run : |
2323
make build
2424
lint:
25-
runs-on: ubuntu-latest
25+
runs-on: ubuntu-22.04
2626
steps:
27-
- uses: actions/checkout@v2
27+
- uses: actions/checkout@v3
2828
- name: Set up Python
29-
uses: actions/setup-python@v2
29+
uses: actions/setup-python@v4
3030
with:
3131
python-version: '3.7'
3232
- name: Install dependencies
@@ -36,14 +36,14 @@ jobs:
3636
run: |
3737
make lint
3838
test:
39-
runs-on: ubuntu-latest
39+
runs-on: ubuntu-22.04
4040
strategy:
4141
matrix:
4242
python-version: [3.7, 3.8, 3.9, "3.10"]
4343
steps:
44-
- uses: actions/checkout@v2
44+
- uses: actions/checkout@v3
4545
- name: Set up Python ${{ matrix.python-version }}
46-
uses: actions/setup-python@v2
46+
uses: actions/setup-python@v4
4747
with:
4848
python-version: ${{ matrix.python-version }}
4949
- name: Install dependencies

.github/workflows/release.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: Release
33
on:
44
pull_request:
55
branches-ignore:
6-
- 'master'
6+
- 'main'
77
push:
88
tags:
99
- '[0-9]+.[0-9]+.[0-9]+'
@@ -12,11 +12,11 @@ on:
1212
jobs:
1313
release-to-pypi:
1414
needs: [check-release]
15-
runs-on: ubuntu-20.04
15+
runs-on: ubuntu-22.04
1616
steps:
17-
- uses: actions/checkout@v2
17+
- uses: actions/checkout@v3
1818
- name: Set up Python
19-
uses: actions/setup-python@v1
19+
uses: actions/setup-python@v4
2020
with:
2121
python-version: '3.7'
2222
- name: Install dependencies
@@ -25,7 +25,7 @@ jobs:
2525
pip install -U setuptools wheel twine
2626
- name: Build and publish
2727
env:
28-
TWINE_USERNAME: chaostoolkit
28+
TWINE_USERNAME: __token__
2929
TWINE_PASSWORD: ${{ secrets.PYPI_PWD }}
3030
run: |
3131
python3 setup.py release

CHANGELOG.md

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,12 @@
22

33
## [Unreleased][]
44

5-
[Unreleased]: https://github.com/chaostoolkit/chaostoolkit-extension-template/compare/0.1.0...HEAD
6-
7-
## [0.2.0][]
8-
9-
[0.2.0]: https://github.com/chaostoolkit/chaostoolkit-extension-template/compare/0.1.0...0.2.0
10-
11-
### Changed
12-
13-
- Switched to github actions
14-
- Updated dependencies
15-
- Added black and isort for linting
5+
[Unreleased]: https://github.com/chaostoolkit-incubator/chaostoolkit-datadog/compare/0.1.0...HEAD
166

177
## [0.1.0][]
188

19-
[0.1.0]: https://github.com/chaostoolkit/chaostoolkit-extension-template/tree/0.1.0
9+
[0.1.0]: https://github.com/chaostoolkit-incubator/chaostoolkit-datadog/tree/0.1.0
2010

2111
### Added
2212

2313
- Initial release
24-
- Added Travis CI
25-
- Added empty example tests for probes and actions

Makefile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ build:
1414

1515
.PHONY: lint
1616
lint:
17-
flake8 chaosext/ tests/
18-
isort --check-only --profile black chaosext/ tests/
19-
black --check --diff --line-length=80 chaosext/ tests/
17+
flake8 chaosdatadog/ tests/
18+
isort --check-only --profile black chaosdatadog/ tests/
19+
black --check --diff --line-length=80 chaosdatadog/ tests/
2020

2121
.PHONY: format
2222
format:
23-
isort --profile black chaosext/ tests/
24-
black --line-length=80 chaosext/ tests/
23+
isort --profile black chaosdatadog/ tests/
24+
black --line-length=80 chaosdatadog/ tests/
2525

2626
.PHONY: tests
2727
tests:

README.md

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
# Chaos Toolkit Extension Template
22

3-
[![Version](https://img.shields.io/pypi/v/chaostoolkit-my-extension.svg)](https://img.shields.io/pypi/v/chaostoolkit-lib.svg)
4-
[![License](https://img.shields.io/pypi/l/chaostoolkit-my-extension.svg)](https://img.shields.io/pypi/l/chaostoolkit-lib.svg)
3+
[![Version](https://img.shields.io/pypi/v/chaostoolkit-datadog.svg)](https://img.shields.io/pypi/v/chaostoolkit-datadog.svg)
4+
[![License](https://img.shields.io/pypi/l/chaostoolkit-datadog.svg)](https://img.shields.io/pypi/l/chaostoolkit-datadog.svg)
55

6-
![Build](https://github.com/chaostoolkit/chaostoolkit-lib/workflows/Build/badge.svg)
7-
[![codecov](https://codecov.io/gh/chaostoolkit/chaostoolkit-my-extension/branch/master/graph/badge.svg)](https://codecov.io/gh/chaostoolkit/chaostoolkit-lib)
8-
[![Python versions](https://img.shields.io/pypi/pyversions/chaostoolkit-my-extension.svg)](https://www.python.org/)
6+
![Build](https://github.com/chaostoolkit-incubator/chaostoolkit-datadog/workflows/Build/badge.svg)
7+
[![Python versions](https://img.shields.io/pypi/pyversions/chaostoolkit-datadog.svg)](https://www.python.org/)
98

10-
This project should be used as a starting point to create your own
11-
Chaos Toolkit extension.
9+
This project contains Chaos Toolkit activities and tolerances to work
10+
with DataDog.
1211

1312
## Install
1413

@@ -20,20 +19,74 @@ environment where [chaostoolkit][] already lives.
2019
[chaostoolkit]: https://github.com/chaostoolkit/chaostoolkit
2120

2221
```
23-
$ pip install chaostoolkit-<your extension name here>
22+
$ pip install chaostoolkit-datadog
2423
```
2524

2625
## Usage
2726

28-
<Explain your probes and actions usage from the experiment.json here>
27+
A typical experiment using this extension would look like this:
28+
29+
```json
30+
{
31+
"version": "1.0.0",
32+
"title": "Run a, experiment using a DataDog SLO to verify our system",
33+
"description": "n/a",
34+
"configuration": {
35+
"datadog_host": "https://datadoghq.eu"
36+
},
37+
"steady-state-hypothesis": {
38+
"title": "n/a",
39+
"probes": [
40+
{
41+
"type": "probe",
42+
"name": "read-slo",
43+
"tolerance": {
44+
"type": "probe",
45+
"name": "check-slo",
46+
"provider": {
47+
"type": "python",
48+
"module": "chaosdatadog.slo.tolerances",
49+
"func": "slo_must_be_met",
50+
"arguments": {
51+
"threshold": "7d"
52+
}
53+
}
54+
},
55+
"provider": {
56+
"type": "python",
57+
"module": "chaosdatadog.slo.probes",
58+
"func": "get_slo",
59+
"arguments": {
60+
"slo_id": "..."
61+
}
62+
}
63+
}
64+
]
65+
},
66+
"method": []
67+
}
68+
```
2969

3070
That's it!
3171

3272
Please explore the code to see existing probes and actions.
3373

3474
## Configuration
3575

36-
<Specify any extra configuration your extension relies on here>
76+
In the `configuration` block you may want to specify the DataDog host you are
77+
targetting:
78+
79+
```json
80+
"configuration": {
81+
"datadog_host": "https://datadoghq.eu"
82+
},
83+
```
84+
85+
The authentication can be set using the typical DataDog environment variables,
86+
notably:
87+
88+
* `DD_API_KEY`: the API key
89+
* `DD_APP_KEY`: the application key
3790

3891
## Test
3992

chaosdatadog/__init__.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# -*- coding: utf-8 -*-
2+
import warnings
3+
from contextlib import contextmanager
4+
from typing import Generator, List
5+
6+
from chaoslib.discovery.discover import (
7+
discover_probes,
8+
initialize_discovery_result,
9+
)
10+
from chaoslib.types import (
11+
Configuration,
12+
DiscoveredActivities,
13+
Discovery,
14+
Secrets,
15+
)
16+
from datadog_api_client import ApiClient
17+
from datadog_api_client import Configuration as DDCfg
18+
from logzero import logger
19+
20+
warnings.filterwarnings("ignore", message="Using unstable operation")
21+
__version__ = "0.1.0"
22+
23+
24+
@contextmanager
25+
def get_client(
26+
configuration: Configuration = None, secrets: Secrets = None, **kwargs
27+
) -> Generator[ApiClient, None, None]:
28+
configuration = configuration or {}
29+
secrets = secrets or {}
30+
31+
dd_host = configuration.get("datadog_host", "https://api.datadoghq.com")
32+
api_key = secrets.get("api_key")
33+
app_key = secrets.get("app_key")
34+
35+
keys = None
36+
if api_key or app_key:
37+
keys = {"apiKeyAuth": api_key, "appKeyAuth": app_key}
38+
39+
c = DDCfg(api_key=keys, host=dd_host)
40+
with ApiClient(c) as api:
41+
yield api
42+
43+
44+
def discover(discover_system: bool = True) -> Discovery:
45+
"""
46+
Discover DataDog capabilities from this extension.
47+
"""
48+
logger.info("Discovering capabilities from chaostoolkit-datadog")
49+
50+
discovery = initialize_discovery_result(
51+
"chaostoolkit-datadog", __version__, "datadog"
52+
)
53+
discovery["activities"].extend(load_exported_activities())
54+
55+
return discovery
56+
57+
58+
###############################################################################
59+
# Private functions
60+
###############################################################################
61+
def load_exported_activities() -> List[DiscoveredActivities]:
62+
"""
63+
Extract metadata from actions, probes and tolerances
64+
exposed by this extension.
65+
"""
66+
activities = [] # type: ignore
67+
68+
activities.extend(discover_probes("chaosdatadog.slo.probes"))
69+
70+
return activities

chaosdatadog/slo/__init__.py

Whitespace-only changes.

chaosdatadog/slo/probes.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from typing import Any, Dict
2+
3+
import arrow
4+
from chaoslib.types import Configuration, Secrets
5+
from datadog_api_client.v1.api.service_level_objectives_api import (
6+
ServiceLevelObjectivesApi,
7+
)
8+
from logzero import logger
9+
10+
from chaosdatadog import get_client
11+
12+
__all__ = ["get_slo", "get_slo_details"]
13+
14+
15+
def get_slo_details(
16+
slo_id: str, configuration: Configuration = None, secrets: Secrets = None
17+
) -> Dict[str, Any]:
18+
"""
19+
Get a SLO's details.
20+
21+
Please visit https://docs.datadoghq.com/api/latest/service-level-objectives/#get-an-slos-details
22+
for more information on the response payload, which is returned as a
23+
dictionary.
24+
""" # noqa: E501
25+
with get_client(configuration, secrets) as c:
26+
api = ServiceLevelObjectivesApi(c)
27+
response = api.get_slo(slo_id=slo_id)
28+
29+
return response.to_dict()
30+
31+
32+
def get_slo(
33+
slo_id: str,
34+
start_period: str = "2 minutes ago",
35+
end_period: str = None,
36+
configuration: Configuration = None,
37+
secrets: Secrets = None,
38+
) -> Dict[str, Any]:
39+
"""
40+
Get a SLO's history for the given period.
41+
42+
Periods should be given relative to each other. If `end_period` isn't
43+
provided it will resolve to now (UTC). `start_period` is always relative
44+
to `end_period`. You can use a format such as: `"X minutes ago"` for both.
45+
46+
Please visit https://docs.datadoghq.com/api/latest/service-level-objectives/#get-an-slos-history
47+
for more information on the response payload, which is returned as a
48+
dictionary.
49+
""" # noqa: E501
50+
with get_client(configuration, secrets) as c:
51+
c.configuration.unstable_operations["get_slo_history"] = True
52+
api = ServiceLevelObjectivesApi(c)
53+
54+
now = arrow.utcnow()
55+
if not end_period:
56+
end = now
57+
else:
58+
end = now.dehumanize(end_period)
59+
60+
if not start_period:
61+
start = now
62+
else:
63+
start = end.dehumanize(start_period)
64+
65+
response = api.get_slo_history(
66+
slo_id=slo_id, from_ts=start.int_timestamp, to_ts=now.int_timestamp
67+
)
68+
69+
slo = response.to_dict()
70+
logger.debug(f"SLO history is: {slo}")
71+
return slo

0 commit comments

Comments
 (0)