Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add router management functions to SDK #155

Merged
Merged
Show file tree
Hide file tree
Changes from 75 commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
3f7f61d
Refactor swagger/openapi specs for sdk
deadlycoconuts Dec 24, 2021
a553730
Add autogenerated openapi classes for routers
deadlycoconuts Dec 24, 2021
a69c676
Rewrote OpenAPI spec for ExperimentConfig to avoid using oneOf
deadlycoconuts Jan 4, 2022
7bb5c51
Remove dependency of ExperimentConfig schema on ExperimentEngineType
deadlycoconuts Jan 5, 2022
ff99859
Add additional fields for enricher component
deadlycoconuts Jan 5, 2022
ec6f708
Add additional nullable fields to OpenAPI spec
deadlycoconuts Jan 5, 2022
39c312d
Reference ensembler, event and router id to common id schema
deadlycoconuts Jan 6, 2022
a44fa55
Rename Ensembler in OpenAPI spec for routers to RouterEnsemblerConfig
deadlycoconuts Jan 6, 2022
a8fd945
Reorganise router related tests
deadlycoconuts Jan 7, 2022
d0dbfce
Refactor route tests to utilise predefined fixtures
deadlycoconuts Jan 10, 2022
97cff4c
Add resource_request sdk classes and tests
deadlycoconuts Jan 10, 2022
e105658
Add RouterEnsemblerConfig sdk and classes
deadlycoconuts Jan 11, 2022
25a5e9a
Move common router components to separate file
deadlycoconuts Jan 11, 2022
158204a
Refactor RouterEnsemblerConfig to use common schemas
deadlycoconuts Jan 11, 2022
8797e2f
Add Enricher sdk classes and tests
deadlycoconuts Jan 11, 2022
b7d9a89
Add ExperimentEngine sdk class
deadlycoconuts Jan 12, 2022
dc64ddc
Add fix to enforce project_id in ExperimentConfig to be read as int
deadlycoconuts Jan 12, 2022
6379eb4
Refactor regex checks for cpu and memory requests
deadlycoconuts Jan 12, 2022
46d4175
Add create router tests
deadlycoconuts Jan 12, 2022
909c4c5
Refactor string checks and add docstrings
deadlycoconuts Jan 12, 2022
ee11286
Rewrite router test case
deadlycoconuts Jan 12, 2022
13ddbbd
Modify min/max replica comparison check to allow equality
deadlycoconuts Jan 12, 2022
82bdd6d
Refactor swagger/openapi specs for sdk
deadlycoconuts Dec 24, 2021
bf41bab
Add autogenerated openapi classes for routers
deadlycoconuts Dec 24, 2021
caefe14
Rewrote OpenAPI spec for ExperimentConfig to avoid using oneOf
deadlycoconuts Jan 4, 2022
c8032e9
Remove dependency of ExperimentConfig schema on ExperimentEngineType
deadlycoconuts Jan 5, 2022
02d0476
Add additional fields for enricher component
deadlycoconuts Jan 5, 2022
a47431c
Add additional nullable fields to OpenAPI spec
deadlycoconuts Jan 5, 2022
9ad6135
Reference ensembler, event and router id to common id schema
deadlycoconuts Jan 6, 2022
ebaf83c
Rename Ensembler in OpenAPI spec for routers to RouterEnsemblerConfig
deadlycoconuts Jan 6, 2022
a125da5
Refactor route tests to utilise predefined fixtures
deadlycoconuts Jan 10, 2022
3838ee2
Add resource_request sdk classes and tests
deadlycoconuts Jan 10, 2022
56df7be
Add RouterEnsemblerConfig sdk and classes
deadlycoconuts Jan 11, 2022
ae4a169
Move common router components to separate file
deadlycoconuts Jan 11, 2022
911b861
Refactor RouterEnsemblerConfig to use common schemas
deadlycoconuts Jan 11, 2022
22e0d89
Add Enricher sdk classes and tests
deadlycoconuts Jan 11, 2022
c63a636
Add ExperimentEngine sdk class
deadlycoconuts Jan 12, 2022
fb398ae
Add fix to enforce project_id in ExperimentConfig to be read as int
deadlycoconuts Jan 12, 2022
786d4f6
Refactor regex checks for cpu and memory requests
deadlycoconuts Jan 12, 2022
138e7a3
Add create router tests
deadlycoconuts Jan 12, 2022
36f6f65
Refactor string checks and add docstrings
deadlycoconuts Jan 12, 2022
937e09e
Rewrite router test case
deadlycoconuts Jan 12, 2022
bcb6452
Modify min/max replica comparison check to allow equality
deadlycoconuts Jan 12, 2022
902b471
Refactor Enricher, LogConfig, Route, RouterConfig, RouterEnsemblerCon…
deadlycoconuts Jan 13, 2022
730c457
Remove regex validation from SDK and move them to OpenAPI spec
deadlycoconuts Jan 14, 2022
d608ce7
Reinsert missing semi-colon in js file
deadlycoconuts Jan 14, 2022
4c3c7b9
Correct regex strings in OpenApi spec
deadlycoconuts Jan 14, 2022
b21bee2
Rearrange modules in SDK
deadlycoconuts Jan 14, 2022
59e5efd
Add all router API endpoints and reformat response schema for delete …
deadlycoconuts Jan 13, 2022
9543d37
Add delete router method to router sdk class
deadlycoconuts Jan 13, 2022
0d7387a
Add SDK method for getting router by router_id
deadlycoconuts Jan 14, 2022
4cf9717
Add SDK method for updating router config
deadlycoconuts Jan 17, 2022
f5f7a7d
Add deploy and undeploy router SDK functions
deadlycoconuts Jan 17, 2022
1da7036
Add tests for create/delete/deploy/undeploy router SDK methods
deadlycoconuts Jan 17, 2022
e73674f
Add tests for undeploy router SDK method
deadlycoconuts Jan 17, 2022
fffa0cd
Add get/delete router version SDK methods
deadlycoconuts Jan 18, 2022
09dc23f
Add tests for get/delete router version SDK methods
deadlycoconuts Jan 18, 2022
af97ecb
Add deploy version/get events router SDK methods
deadlycoconuts Jan 18, 2022
7864981
Add list router versions SDK method
deadlycoconuts Jan 18, 2022
7d6c331
Reorganise router module
deadlycoconuts Jan 18, 2022
2c70ab2
Add sample file to demonstrate the use of the router class in the SDK
deadlycoconuts Jan 18, 2022
8c0c5b8
Add docstrings to RouterVersion
deadlycoconuts Jan 19, 2022
d95dffa
Replace print with logging statements and fix comments
deadlycoconuts Jan 19, 2022
64cde0e
Regroup import statements
deadlycoconuts Jan 19, 2022
d982e35
Add global numbers to implementation of to_dict
deadlycoconuts Jan 19, 2022
93f0acb
Add wait methods for routers to sample; move location of RouterStatus
deadlycoconuts Jan 19, 2022
8f1d9da
Replace kwargs with empty dict when created OpenAPI ExperimentConfig
deadlycoconuts Jan 20, 2022
01a0936
Refactor log_config fixture
deadlycoconuts Jan 20, 2022
0867468
Implement logging and tests for blocking functions
deadlycoconuts Jan 20, 2022
5b391a2
Add additional debugging statements for sample
deadlycoconuts Jan 20, 2022
85de070
Refactor blocking functions to take floats
deadlycoconuts Jan 20, 2022
b0152bf
Add dataclass implementation to config classes
deadlycoconuts Jan 20, 2022
109cedc
Remove extra comma in class definition
deadlycoconuts Jan 20, 2022
e81483d
Add additional sleep duration to ensure router dependencies are undep…
deadlycoconuts Jan 20, 2022
158ff88
Add support for experiment plugin configs
deadlycoconuts Jan 21, 2022
a63795c
Remove plugin_config from SDK and OpenAPI spec
deadlycoconuts Jan 21, 2022
082bfa0
Remove plugin_config from autogenerated classes
deadlycoconuts Jan 21, 2022
8e6ea9b
Fix update method of Router class to ensure all attributes are updated
deadlycoconuts Jan 24, 2022
6f3f1e5
Replace images with generic images from Docker Hub
deadlycoconuts Jan 24, 2022
c22c380
Use generic images and names for tests
deadlycoconuts Jan 24, 2022
38f7c2f
Add additional sample to demonstrate creating a new router from an ex…
deadlycoconuts Jan 24, 2022
99193e5
Remove redundant imports
deadlycoconuts Jan 24, 2022
225f256
Wrap sleep statements into router blocking functions
deadlycoconuts Jan 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions api/api/openapi-sdk.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,17 @@ paths:
# R O U T E R S
"/projects/{project_id}/routers":
$ref: "specs/routers.yaml#/paths/~1projects~1{project_id}~1routers"
"/projects/{project_id}/routers/{router_id}":
$ref: "specs/routers.yaml#/paths/~1projects~1{project_id}~1routers~1{router_id}"
"/projects/{project_id}/routers/{router_id}/deploy":
$ref: "specs/routers.yaml#/paths/~1projects~1{project_id}~1routers~1{router_id}~1deploy"
"/projects/{project_id}/routers/{router_id}/undeploy":
$ref: "specs/routers.yaml#/paths/~1projects~1{project_id}~1routers~1{router_id}~1undeploy"
"/projects/{project_id}/routers/{router_id}/versions":
$ref: "specs/routers.yaml#/paths/~1projects~1{project_id}~1routers~1{router_id}~1versions"
"/projects/{project_id}/routers/{router_id}/versions/{version}":
$ref: "specs/routers.yaml#/paths/~1projects~1{project_id}~1routers~1{router_id}~1versions~1{version}"
"/projects/{project_id}/routers/{router_id}/versions/{version}/deploy":
$ref: "specs/routers.yaml#/paths/~1projects~1{project_id}~1routers~1{router_id}~1versions~1{version}~1deploy"
"/projects/{project_id}/routers/{router_id}/events":
$ref: "specs/routers.yaml#/paths/~1projects~1{project_id}~1routers~1{router_id}~1events"
6 changes: 6 additions & 0 deletions api/api/specs/experiment-engines.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,12 @@ components:
$ref: "#/components/schemas/ExperimentEngineType"
config:
type: "object"
plugin_config:
type: "object"
nullable: true
properties:
image:
type: "string"

# Enums
ExperimentEngineType:
Expand Down
13 changes: 8 additions & 5 deletions api/api/specs/routers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ paths:
schema:
type: "object"
properties:
router_id:
id:
<<: *id
400:
description: "Invalid project_id or router_id"
Expand Down Expand Up @@ -320,7 +320,7 @@ paths:
<<: *id
required: true
responses:
200:
202:
description: "OK"
content:
application/json:
Expand Down Expand Up @@ -401,9 +401,12 @@ paths:
content:
application/json:
schema:
type: "array"
items:
$ref: "#/components/schemas/Event"
type: "object"
properties:
events:
type: "array"
items:
$ref: "#/components/schemas/Event"
400:
description: "Invalid project_id or router_id"
404:
Expand Down
320 changes: 320 additions & 0 deletions sdk/samples/router/general.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
import time
import turing
import turing.batch
import turing.batch.config
import turing.router.config.router_config
from turing.router.router import RouterStatus
from turing.router.config.route import Route
from turing.router.config.router_config import RouterConfig
from turing.router.config.resource_request import ResourceRequest
from turing.router.config.log_config import LogConfig, ResultLoggerType
from turing.router.config.traffic_rule import TrafficRule, HeaderTrafficRuleCondition, PayloadTrafficRuleCondition
from turing.router.config.enricher import Enricher
from turing.router.config.router_ensembler_config import DockerRouterEnsemblerConfig
from turing.router.config.common.env_var import EnvVar
from turing.router.config.experiment_config import ExperimentConfig


def main(turing_api: str, project: str):
# Initialize Turing client
turing.set_url(turing_api)
turing.set_project(project)

# Build a router config in order to create a router
# Create some routes
routes = [
Route(
id='meow',
endpoint='http://fox-says.meow',
timeout='20ms'
),
Route(
id='woof',
endpoint='http://fox-says.woof',
timeout='20ms'
),
Route(
id='baaa',
endpoint='http://fox-says.baa',
timeout='20ms'
),
Route(
id='oink',
endpoint='http://fox-says.oink',
timeout='20ms'
),
Route(
id='ring-ding-ding',
endpoint='http://fox-says.ring-ding-ding',
timeout='20ms'
)
]

# Create some traffic rules
rules = [
TrafficRule(
conditions=[
HeaderTrafficRuleCondition(
field='name',
values=['cat']
)
],
routes=[
'meow'
]
),
TrafficRule(
conditions=[
HeaderTrafficRuleCondition(
field='name',
values=['dog']
)
],
routes=[
'woof'
]
),
TrafficRule(
conditions=[
HeaderTrafficRuleCondition(
field='name',
values=['sheep']
)
],
routes=[
'baaa'
]
),
TrafficRule(
conditions=[
HeaderTrafficRuleCondition(
field='name',
values=['pig']
)
],
routes=[
'oink'
]
),
TrafficRule(
conditions=[
PayloadTrafficRuleCondition(
field='body',
values=['sus']
)
],
routes=[
'meow',
'woof',
'baaa',
'oink',
'ring-ding-ding'
]
)
]

# Create an experiment config (
experiment_config = ExperimentConfig(
type="xp",
config={
'variables':
[
{'name': 'farm_id', 'field': 'farm_id', 'field_source': 'header'},
{'name': 'country_code', 'field': 'country', 'field_source': 'header'},
{'name': 'latitude', 'field': 'farm_lat', 'field_source': 'header'},
{'name': 'longitude', 'field': 'farm_long', 'field_source': 'header'}
],
'project_id': 102
}
)

# Create a resource request config for the router
resource_request = ResourceRequest(
min_replica=0,
max_replica=2,
cpu_request="500m",
memory_request="512Mi"
)

# Create a log config for the router
log_config = LogConfig(
result_logger_type=ResultLoggerType.NOP
)

# Create an enricher for the router
enricher = Enricher(
image="asia.gcr.io/gods-dev/echo:1.0.2",
resource_request=ResourceRequest(
min_replica=0,
max_replica=2,
cpu_request="500m",
memory_request="512Mi"
),
endpoint="/",
timeout="60ms",
port=8080,
env=[
EnvVar(
name="humans",
value="farmer-joe"
)
]
)

# Create an ensembler for the router
ensembler = DockerRouterEnsemblerConfig(
id=1,
image="asia.gcr.io/gods-dev/echo:1.0.2",
resource_request=ResourceRequest(
min_replica=1,
max_replica=3,
cpu_request="500m",
memory_request="512Mi"
),
endpoint="/echo",
timeout="60ms",
port=8080,
env=[],
)

# Create the RouterConfig instance
router_config = RouterConfig(
environment_name="id-dev",
name="what-does-the-fox-say",
routes=routes,
rules=rules,
default_route_id="test",
experiment_engine=experiment_config,
resource_request=resource_request,
timeout="100ms",
log_config=log_config,
enricher=enricher,
ensembler=ensembler
)

# 1. Create a new router using the RouterConfig object
new_router = turing.Router.create(router_config)
print(f"1. You have created a router with id: {new_router.id}")

# 2. List all routers
routers = turing.Router.list()
print(f"2. You have just retrieved a list of {len(routers)} routers:")
for r in routers:
if r.name == new_router.name:
my_router = r
print(r)

# Wait for the router to get deployed
try:
my_router.wait_for_status(RouterStatus.DEPLOYED)
except TimeoutError:
raise Exception(f"Turing API is taking too long for router {my_router.id} to get deployed.")

# 3. Get the router you just created using the router_id obtained
my_router = turing.Router.get(my_router.id)
print(f"3. You have retrieved the router with name: {my_router.name}")

# Access the router config from the returned Router object directly
my_router_config = my_router.config

# Modify something in the router config
my_router_config.routes.append(
Route(
id='fee-fi-fo-fum',
endpoint='http://fox-says.fee-fi-fo-fum',
timeout='20ms'
)
)

# 4. Update the router with the new router config
my_router.update(my_router_config)
print(f"4. You have just updated your router with a new config.")

# 5. List all the router config versions of your router
my_router_versions = my_router.list_versions()
print(f"5. You have just retrieved a list of {len(my_router_versions)} versions for your router:")
for ver in my_router_versions:
print(ver)

# Sort the versions returned by version number
my_router_versions.sort(key=lambda x: x.version)
# Get the version number of the first version returned
first_ver_no = my_router_versions[0].version
# Get the version number of the latest version returned
latest_ver_no = my_router_versions[-1].version

# Wait for the latest version to get deployed
try:
my_router.wait_for_version_status(RouterStatus.DEPLOYED, latest_ver_no)
except TimeoutError:
raise Exception(f"Turing API is taking too long for router {my_router.id} with version {latest_ver_no} to get "
f"deployed.")

# Wait for the dependencies of the first version to be fully undeployed
time.sleep(15)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is it for? Can it be done as: ?

my_router.wait_for_version_status(RouterStatus.UNDEPLOYED, first_ver_no)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this is also similar to the issue right below; I was kind of trying to test both 'potential sources' of error (either with something undeploying being undeployed completely/properly, or with something being deployed not being deployed completely/properly) in both places.


# 6. Deploy a specific router config version (the first one we created)
response = my_router.deploy_version(first_ver_no)
print(f"6. You have deployed version {response['version']} of router {response['router_id']}.")

# Wait for the first version to get deployed
try:
my_router.wait_for_version_status(RouterStatus.DEPLOYED, first_ver_no)
except TimeoutError:
raise Exception(f"Turing API is taking too long for router {my_router.id} with version {first_ver_no} to get "
f"deployed.")

# 7. Undeploy the current active router configuration
response = my_router.undeploy()
print(f"7. You have undeployed router {response['router_id']}.")

# Wait for the router to get undeployed
try:
my_router.wait_for_status(RouterStatus.UNDEPLOYED)
except TimeoutError:
raise Exception(f"Turing API is taking too long for router {my_router.id} to get undeployed.")

# Wait for the dependencies of the router to be fully undeployed
time.sleep(10)

# 8. Deploy the router's *current* configuration (notice how it still deploys the *first* version)
response = my_router.deploy()
print(f"8. You have deployed version {response['version']} of router {response['router_id']}.")

# Wait for the router to get deployed
try:
my_router.wait_for_status(RouterStatus.DEPLOYED)
except TimeoutError:
raise Exception(f"Turing API is taking too long for router {my_router.id} to get deployed.")

# Undeploy the router
response = my_router.undeploy()
print(f"You have undeployed router {response['router_id']}.")

# 9. Get a specific router version of the router
my_router_ver = my_router.get_version(first_ver_no)
print(f"9. You have just retrieved version {my_router_ver.version} of your router.")

# 10. Delete a specific router version of the router
response = my_router.delete_version(latest_ver_no)
print(f"10. You have deleted version {response['version']} of router {response['router_id']}.")

# 11. Get all deployment events associated with this router
events = my_router.get_events()
print(f"11. You have just retrieved a list of {len(events)} events for your router:")
for e in events:
print(e)

# 12. Delete this router (using its router_id)
deleted_router_id = turing.Router.delete(my_router.id)
print(f"12. You have just deleted the router with id: {deleted_router_id}")

# Check if the router still exists
for r in turing.Router.list():
if r.name == my_router_config.name:
raise Exception("Oh my, this router still exists!")


if __name__ == '__main__':
import fire
fire.Fire(main)
Loading