Skip to content

Commit 7f5f350

Browse files
authored
Merge pull request #678 from FlorentinD/notebooksessiontest-on-new-instance
Run session notebook against fresh db
2 parents b448fe6 + a61f8ba commit 7f5f350

File tree

3 files changed

+209
-130
lines changed

3 files changed

+209
-130
lines changed

scripts/ci/aura_api_ci.py

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

scripts/ci/run_targeting_aura.py

Lines changed: 8 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,21 @@ 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+
client_id = os.environ["AURA_API_CLIENT_ID"]
30+
client_secret = os.environ["AURA_API_CLIENT_SECRET"]
31+
tenant_id = os.environ.get("TENANT_ID")
32+
aura_api = AuraApiCI(client_id=client_id, client_secret=client_secret, tenant_id=tenant_id)
15233

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

156-
create_result = create_instance(instance_name, access_token)
37+
create_result = aura_api.create_ds_instance(instance_name)
15738
instance_id = create_result["id"]
15839
logging.info("Creation of database accepted")
15940

16041
try:
161-
check_running(access_token, instance_id)
42+
aura_api.check_running(instance_id)
16243
logging.info("Database %s up and running", instance_id)
16344

16445
if sys.argv[1] == "tests":
@@ -178,10 +59,7 @@ def main() -> None:
17859
else:
17960
logging.error(f"Invalid target: {sys.argv[1]}")
18061
finally:
181-
access_token = get_access_token()
182-
logging.info("Access token for teardown acquired")
183-
184-
teardown_instance(access_token, instance_id)
62+
aura_api.teardown_instance(instance_id)
18563
logging.info("Teardown of instance %s successful", instance_id)
18664

18765

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
client_id = os.environ["AURA_API_CLIENT_ID"]
15+
client_secret = os.environ["AURA_API_CLIENT_SECRET"]
16+
tenant_id = os.environ.get("TENANT_ID")
17+
aura_api = AuraApiCI(client_id=client_id, client_secret=client_secret, tenant_id=tenant_id)
18+
19+
MAX_INT = 1000000
20+
instance_name = f"ci-build-{sys.argv[1]}" if len(sys.argv) > 1 else "ci-instance-" + str(rd.randint(0, MAX_INT))
21+
22+
create_result = aura_api.create_instance(instance_name, memory="1GB", type="professional-db")
23+
instance_id = create_result["id"]
24+
logging.info("Creation of database accepted")
25+
26+
try:
27+
aura_api.check_running(instance_id)
28+
logging.info("Database %s up and running", instance_id)
29+
30+
uri = (create_result["connection_url"],)
31+
username = (create_result["username"],)
32+
password = (create_result["password"],)
33+
34+
cmd = f"AURA_DB_ADDRESS={uri} AURA_DB_USER={username} AURA_DB_PW={password} tox -e jupyter-notebook-session-ci"
35+
36+
if os.system(cmd) != 0:
37+
raise Exception("Failed to run notebooks")
38+
39+
finally:
40+
aura_api.teardown_instance(instance_id)
41+
logging.info("Teardown of instance %s successful", instance_id)
42+
43+
44+
if __name__ == "__main__":
45+
main()

0 commit comments

Comments
 (0)