Skip to content

Commit d0bc72a

Browse files
committed
Run session notebook against fresh db
1 parent b448fe6 commit d0bc72a

File tree

3 files changed

+202
-130
lines changed

3 files changed

+202
-130
lines changed

scripts/ci/aura_api_ci.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import logging
2+
import os
3+
import time
4+
from time import sleep
5+
from typing import Any, Dict, Optional
6+
7+
import requests
8+
9+
CLIENT_ID = os.environ["AURA_API_CLIENT_ID"]
10+
CLIENT_SECRET = os.environ["AURA_API_CLIENT_SECRET"]
11+
12+
13+
class AuraApiCI:
14+
class AuraAuthToken:
15+
access_token: str
16+
expires_in: int
17+
token_type: str
18+
19+
def __init__(self, json: Dict[str, Any]) -> None:
20+
self.access_token = json["access_token"]
21+
expires_in: int = json["expires_in"]
22+
self.expires_at = int(time.time()) + expires_in
23+
self.token_type = json["token_type"]
24+
25+
def is_expired(self) -> bool:
26+
return self.expires_at >= int(time.time())
27+
28+
def __init__(self) -> None:
29+
self._token: Optional[AuraApiCI.AuraAuthToken] = None
30+
self._logger = logging.getLogger()
31+
32+
def _build_header(self) -> Dict[str, str]:
33+
return {"Authorization": f"Bearer {self._auth_token()}", "User-agent": "neo4j-graphdatascience-ci"}
34+
35+
def _auth_token(self) -> str:
36+
if self._token is None or self._token.is_expired():
37+
self._token = self._update_token()
38+
return self._token.access_token
39+
40+
def _update_token(self) -> AuraAuthToken:
41+
data = {
42+
"grant_type": "client_credentials",
43+
}
44+
45+
self._logger.debug("Updating oauth token")
46+
47+
response = requests.post("https://api-staging.neo4j.io/oauth/token", data=data, auth=(CLIENT_ID, CLIENT_SECRET))
48+
49+
response.raise_for_status()
50+
51+
return AuraApiCI.AuraAuthToken(response.json())
52+
53+
def create_ds_instance(self, name: str) -> Dict[str, Any]:
54+
return self.create_instance(name, memory="8GB", type="gds")
55+
56+
def create_instance(self, name: str, memory: str, type: str) -> Dict[str, Any]:
57+
CREATE_OK_MAX_WAIT_TIME = 10
58+
59+
data = {
60+
"name": name,
61+
"memory": memory,
62+
"version": "5",
63+
"region": "europe-west1",
64+
"type": type,
65+
"cloud_provider": "gcp",
66+
"tenant_id": self.get_tenant_id(),
67+
}
68+
69+
should_retry = True
70+
wait_time = 1
71+
72+
while should_retry:
73+
sleep(wait_time)
74+
wait_time *= 2
75+
76+
response = requests.post(
77+
"https://api-staging.neo4j.io/v1/instances",
78+
json=data,
79+
headers=self._build_header(),
80+
)
81+
should_retry = response.status_code in [500, 502, 503, 504, 405] and CREATE_OK_MAX_WAIT_TIME > wait_time
82+
83+
if should_retry:
84+
logging.debug(f"Error code: {response.status_code} - Retrying in {wait_time} s")
85+
86+
response.raise_for_status()
87+
88+
return response.json()["data"] # type: ignore
89+
90+
def check_running(self, db_id: str) -> None:
91+
RUNNING_MAX_WAIT_TIME = 60 * 5
92+
93+
should_retry = True
94+
wait_time = 1
95+
96+
while should_retry:
97+
sleep(wait_time)
98+
wait_time *= 2
99+
100+
response = requests.get(
101+
f"https://api-staging.neo4j.io/v1/instances/{db_id}",
102+
headers=self._build_header(),
103+
)
104+
105+
instance_status = "?"
106+
if response.status_code == 200:
107+
instance_status = response.json()["data"]["status"]
108+
109+
should_retry = (
110+
response.status_code in [500, 502, 503, 504] or instance_status == "creating"
111+
) and RUNNING_MAX_WAIT_TIME > wait_time
112+
113+
if should_retry:
114+
logging.debug(
115+
f"Status code: {response.status_code}, Status: {instance_status} - Retrying in {wait_time} s"
116+
)
117+
118+
response.raise_for_status()
119+
120+
def teardown_instance(self, db_id: str) -> None:
121+
TEARDOWN_MAX_WAIT_TIME = 10
122+
123+
should_retry = True
124+
wait_time = 1
125+
126+
while should_retry:
127+
sleep(wait_time)
128+
wait_time *= 2
129+
130+
response = requests.delete(
131+
f"https://api-staging.neo4j.io/v1/instances/{db_id}",
132+
headers=self._build_header(),
133+
)
134+
135+
if response.status_code == 202:
136+
should_retry = False
137+
138+
should_retry = (response.status_code in [500, 502, 503, 504]) and TEARDOWN_MAX_WAIT_TIME > wait_time
139+
140+
if should_retry:
141+
logging.debug(f"Status code: {response.status_code} - Retrying in {wait_time} s")
142+
143+
response.raise_for_status()
144+
145+
def get_tenant_id(self) -> str:
146+
response = requests.get(
147+
"https://api-staging.neo4j.io/v1/tenants",
148+
headers=self._build_header(),
149+
)
150+
response.raise_for_status()
151+
152+
raw_data = response.json()["data"]
153+
assert len(raw_data) == 1
154+
155+
return raw_data[0]["id"] # type: ignore

scripts/ci/run_targeting_aura.py

Lines changed: 5 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -2,106 +2,11 @@
22
import os
33
import random as rd
44
import sys
5-
from time import sleep
6-
from typing import Any, Dict
75

8-
import requests as req
6+
from aura_api_ci import AuraApiCI
97

108
logging.basicConfig(level=logging.INFO)
119

12-
CLIENT_ID = os.environ["AURA_API_CLIENT_ID"]
13-
CLIENT_SECRET = os.environ["AURA_API_CLIENT_SECRET"]
14-
15-
16-
def get_access_token() -> str:
17-
data = {
18-
"grant_type": "client_credentials",
19-
}
20-
21-
# getting a token like {'access_token':'X','expires_in':3600,'token_type':'bearer'}
22-
response = req.post("https://api-staging.neo4j.io/oauth/token", data=data, auth=(CLIENT_ID, CLIENT_SECRET))
23-
24-
response.raise_for_status()
25-
26-
return response.json()["access_token"] # type: ignore
27-
28-
29-
def create_instance(name: str, access_token: str) -> Dict[str, Any]:
30-
CREATE_OK_MAX_WAIT_TIME = 10
31-
32-
data = {
33-
"name": name,
34-
"memory": "8GB",
35-
"version": "5",
36-
"region": "europe-west1",
37-
"type": "gds",
38-
"cloud_provider": "gcp",
39-
"tenant_id": get_tenant_id(access_token),
40-
}
41-
42-
should_retry = True
43-
wait_time = 1
44-
45-
while should_retry:
46-
sleep(wait_time)
47-
wait_time *= 2
48-
49-
response = req.post(
50-
"https://api-staging.neo4j.io/v1/instances",
51-
json=data,
52-
headers={"Authorization": f"Bearer {access_token}"},
53-
)
54-
should_retry = response.status_code in [500, 502, 503, 504, 405] and CREATE_OK_MAX_WAIT_TIME > wait_time
55-
56-
if should_retry:
57-
logging.debug(f"Error code: {response.status_code} - Retrying in {wait_time} s")
58-
59-
response.raise_for_status()
60-
61-
return response.json()["data"] # type: ignore
62-
63-
64-
def check_running(access_token: str, db_id: str) -> None:
65-
RUNNING_MAX_WAIT_TIME = 60 * 5
66-
67-
should_retry = True
68-
wait_time = 1
69-
70-
while should_retry:
71-
sleep(wait_time)
72-
wait_time *= 2
73-
74-
response = req.get(
75-
f"https://api-staging.neo4j.io/v1/instances/{db_id}",
76-
headers={"Authorization": f"Bearer {access_token}"},
77-
)
78-
79-
instance_status = "?"
80-
if response.status_code == 200:
81-
instance_status = response.json()["data"]["status"]
82-
83-
should_retry = (
84-
response.status_code in [500, 502, 503, 504] or instance_status == "creating"
85-
) and RUNNING_MAX_WAIT_TIME > wait_time
86-
87-
if should_retry:
88-
logging.debug(f"Status code: {response.status_code}, Status: {instance_status} - Retrying in {wait_time} s")
89-
90-
response.raise_for_status()
91-
92-
93-
def get_tenant_id(access_token: str) -> str:
94-
response = req.get(
95-
"https://api-staging.neo4j.io/v1/tenants",
96-
headers={"Authorization": f"Bearer {access_token}"},
97-
)
98-
response.raise_for_status()
99-
100-
raw_data = response.json()["data"]
101-
assert len(raw_data) == 1
102-
103-
return raw_data[0]["id"] # type: ignore
104-
10510

10611
def run_tests(uri: str, username: str, password: str) -> None:
10712
cmd = (
@@ -120,45 +25,18 @@ def run_notebooks(uri: str, username: str, password: str) -> None:
12025
raise Exception("Failed to run notebooks")
12126

12227

123-
def teardown_instance(access_token: str, db_id: str) -> None:
124-
TEARDOWN_MAX_WAIT_TIME = 10
125-
126-
should_retry = True
127-
wait_time = 1
128-
129-
while should_retry:
130-
sleep(wait_time)
131-
wait_time *= 2
132-
133-
response = req.delete(
134-
f"https://api-staging.neo4j.io/v1/instances/{db_id}",
135-
headers={"Authorization": f"Bearer {access_token}"},
136-
)
137-
138-
if response.status_code == 202:
139-
should_retry = False
140-
141-
should_retry = (response.status_code in [500, 502, 503, 504]) and TEARDOWN_MAX_WAIT_TIME > wait_time
142-
143-
if should_retry:
144-
logging.debug(f"Status code: {response.status_code} - Retrying in {wait_time} s")
145-
146-
response.raise_for_status()
147-
148-
14928
def main() -> None:
150-
access_token = get_access_token()
151-
logging.info("Access token for creation acquired")
29+
aura_api = AuraApiCI()
15230

15331
MAX_INT = 1000000
15432
instance_name = f"ci-build-{sys.argv[2]}" if len(sys.argv) > 1 else "ci-instance-" + str(rd.randint(0, MAX_INT))
15533

156-
create_result = create_instance(instance_name, access_token)
34+
create_result = aura_api.create_ds_instance(instance_name)
15735
instance_id = create_result["id"]
15836
logging.info("Creation of database accepted")
15937

16038
try:
161-
check_running(access_token, instance_id)
39+
aura_api.check_running(instance_id)
16240
logging.info("Database %s up and running", instance_id)
16341

16442
if sys.argv[1] == "tests":
@@ -178,10 +56,7 @@ def main() -> None:
17856
else:
17957
logging.error(f"Invalid target: {sys.argv[1]}")
18058
finally:
181-
access_token = get_access_token()
182-
logging.info("Access token for teardown acquired")
183-
184-
teardown_instance(access_token, instance_id)
59+
aura_api.teardown_instance(instance_id)
18560
logging.info("Teardown of instance %s successful", instance_id)
18661

18762

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# run `tox -e jupyter-notebook-session-ci`
2+
3+
import logging
4+
import os
5+
import random as rd
6+
import sys
7+
8+
from aura_api_ci import AuraApiCI
9+
10+
logging.basicConfig(level=logging.INFO)
11+
12+
13+
def main() -> None:
14+
aura_api = AuraApiCI()
15+
16+
MAX_INT = 1000000
17+
instance_name = f"ci-build-{sys.argv[1]}" if len(sys.argv) > 0 else "ci-instance-" + str(rd.randint(0, MAX_INT))
18+
19+
create_result = aura_api.create_instance(instance_name, memory="1GB", type="professional-db")
20+
instance_id = create_result["id"]
21+
logging.info("Creation of database accepted")
22+
23+
try:
24+
aura_api.check_running(instance_id)
25+
logging.info("Database %s up and running", instance_id)
26+
27+
uri = (create_result["connection_url"],)
28+
username = (create_result["username"],)
29+
password = (create_result["password"],)
30+
31+
cmd = f"AURA_DB_ADDRESS={uri} AURA_DB_USER={username} AURA_DB_PW={password} tox -e jupyter-notebook-session-ci"
32+
33+
if os.system(cmd) != 0:
34+
raise Exception("Failed to run notebooks")
35+
36+
finally:
37+
aura_api.teardown_instance(instance_id)
38+
logging.info("Teardown of instance %s successful", instance_id)
39+
40+
41+
if __name__ == "__main__":
42+
main()

0 commit comments

Comments
 (0)