Skip to content

Commit

Permalink
add simple retry to requests (#40)
Browse files Browse the repository at this point in the history
* add simple retry to requests
Signed-off-by: vsoch <vsoch@users.noreply.github.com>
  • Loading branch information
vsoch committed Jan 23, 2023
1 parent 770bc06 commit ee26737
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 64 deletions.
1 change: 1 addition & 0 deletions app/library/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def check_auth(credentials: HTTPBasicCredentials = Depends(security)):
current_username_bytes, correct_username_bytes
)
current_password_bytes = credentials.password.encode("utf8")

correct_password_bytes = bytes(settings.flux_token.encode("utf8"))
is_correct_password = secrets.compare_digest(
current_password_bytes, correct_password_bytes
Expand Down
2 changes: 1 addition & 1 deletion app/routers/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ async def list_nodes():
rpc = flux.resource.list.resource_list(app.handle)
listing = rpc.get()
nodes = jsonable_encoder(
{"nodes": list({str(node) for node in listing.free.nodelist})}
{"nodes": list({str(node) for node in listing.up.nodelist})}
)
return JSONResponse(content=nodes, status_code=200)

Expand Down
1 change: 1 addition & 0 deletions clients/python/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and **Merged pull requests**. Critical items to know are:
The versions coincide with releases on pip. Only major versions will be released as tags on Github.

## [0.0.x](https://github.com/flux-framework/flux-restful-api/tree/main) (0.0.x)
- add simple retry to requests (0.0.16)
- support for adding option flags to submit (0.0.15)
- support for `is_launcher` parameter to indicate a launcher should be used instead (0.0.14)
- support for streaming job output (0.0.13)
Expand Down
64 changes: 55 additions & 9 deletions clients/python/flux_restful_client/main/client.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import os
import time
from copy import deepcopy

import flux_restful_client.main.schemas as schemas
import flux_restful_client.utils as utils
import jsonschema
import requests
from flux_restful_client.logger import logger
from requests.auth import HTTPBasicAuth

from .settings import Settings

Expand All @@ -23,6 +25,8 @@ def __init__(
quiet=False,
settings_file=None,
prefix="v1",
attempts=5,
timeout=2,
**kwargs,
):

Expand All @@ -36,13 +40,19 @@ def __init__(
self.headers = {}
self.quiet = quiet
self.prefix = prefix
if self.user and self.token:
self.set_basic_auth(self.user, self.token)
self.attempts = attempts
self.timeout = timeout
self.session = requests.Session()

def set_header(self, name, value):
self.headers.update({name: value})

def set_bearer_auth(self, token):
"""
Add a token directly to a request
"""
self.set_header("Authorization", "Bearer %s" % token)

def set_basic_auth(self, username, password):
"""
A wrapper to adding basic authentication to the Request
Expand All @@ -62,21 +72,57 @@ def reset(self):
self.headers = {}

def do_request(
self, endpoint, method="GET", data=None, headers=None, params=None, stream=False
self,
endpoint,
method="GET",
data=None,
headers=None,
params=None,
stream=False,
timeout=None,
attempts=None,
):
"""
Do a request. This is a wrapper around requests.
"""
attempts = self.attempts if attempts is None else attempts
timeout = self.timeout if timeout is None else timeout

# Always reset headers for new request.
self.reset()

basic = None
if self.user and self.token:
basic = HTTPBasicAuth(self.user, self.token)

headers = headers or self.headers
url = "%s/%s/%s" % (self.host, self.prefix, endpoint)

# Make the request and return to calling function, unless requires auth
response = self.session.request(
method, url, params=params, json=data, headers=headers, stream=stream
)
try:
response = self.session.request(
method,
url,
params=params,
json=data,
headers=headers,
stream=stream,
auth=basic,
)
except Exception as e:
if attempts > 0:
time.sleep(timeout)
return self.do_request(
endpoint,
method,
data,
headers,
params,
stream,
timeout=timeout * 2,
attempts=attempts - 1,
)
raise e

# A 401 response is a request for authentication
if response.status_code != 401:
Expand All @@ -99,8 +145,8 @@ def authenticate_request(self, originalResponse):
return False

# If we have a username and password, set basic auth automatically
if self.token and self.username:
self.set_basic_auth(self.username, self.token)
if self.token and self.user:
self.set_basic_auth(self.user, self.token)

headers = deepcopy(self.headers)
if "Authorization" not in headers:
Expand All @@ -112,6 +158,7 @@ def authenticate_request(self, originalResponse):
return False

# Prepare request to retry

h = utils.parse_auth_header(authHeaderRaw)
headers.update(
{
Expand Down Expand Up @@ -239,7 +286,6 @@ def submit(self, command, **kwargs):
data[optional] = kwargs[optional]

# Validate the data first.
print(data)
jsonschema.validate(data, schema=schemas.job_submit_schema)
result = self.do_request("jobs/submit", "POST", data=data)
if result.status_code == 404:
Expand Down
2 changes: 1 addition & 1 deletion clients/python/flux_restful_client/version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "0.0.15"
__version__ = "0.0.16"
AUTHOR = "Vanessa Sochat"
EMAIL = "vsoch@users.noreply.github.com"
NAME = "flux-restful-client"
Expand Down
134 changes: 82 additions & 52 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.abspath("."))))
from distutils.version import LooseVersion # noqa

import sphinx_material # noqa
import sphinx_immaterial # noqa
from recommonmark.transform import AutoStructify # noqa

FORCE_CLASSIC = os.environ.get("SPHINX_MATERIAL_FORCE_CLASSIC", False)
Expand All @@ -30,8 +30,6 @@
copyright = "2022, Flux RESTful API Developers"
author = "@vsoch"

# The full version, including alpha/beta/rc tags
release = LooseVersion(sphinx_material.__version__).vstring

# -- General configuration ---------------------------------------------------

Expand All @@ -48,14 +46,18 @@
"sphinx.ext.todo",
"sphinx.ext.mathjax",
"sphinx.ext.viewcode",
"sphinx_immaterial.theme_result",
"sphinx_immaterial.kbd_keys",
"sphinx_immaterial.apidoc.format_signatures",
"sphinx_immaterial.apidoc.json.domain",
"sphinx_immaterial.apidoc.python.apigen",
"sphinx_immaterial.graphviz",
"nbsphinx",
"sphinx_markdown_tables",
"sphinx_gallery.gen_gallery",
"sphinx_copybutton",
"sphinx_search.extension",
]


autosummary_generate = True
autoclass_content = "class"

Expand Down Expand Up @@ -102,71 +104,99 @@
# Allows us to add to the default template
templates_path = ["_templates"]

extensions.append("sphinx_material")
html_theme_path = sphinx_material.html_theme_path()
html_context = sphinx_material.get_html_context()
html_theme = "sphinx_material"
extensions.append("sphinx_immaterial")
html_theme = "sphinx_immaterial"
html_css_files = ["custom.css"]

# Custom sphinx material variables
theme_logo_icon = "images/oras.png"


# material theme options (see theme.conf for more information)
html_theme_options = {
"base_url": "http://flux-framework.github.io/flux-restful-api/",
"icon": {
"repo": "fontawesome/brands/github",
"edit": "material/file-edit-outline",
},
"repo_url": "https://github.com/flux-framework/flux-restful-api/",
"repo_name": "Flux RESTful API",
"html_minify": False,
"html_prettify": True,
"css_minify": False,
# https://fonts.google.com/icons?icon.query=cycle
"logo_icon": "cycle",
"repo_name": "Flux RESTFul API",
"repo_type": "github",
"globaltoc_depth": 2,
# red, pink, purple, deep-purple, indigo, blue, light-blue, cyan, teal, green, light-green, lime, yellow, amber, orange, deep-orange, brown, grey, blue-grey, and white.
"color_primary": "blue",
# red, pink, purple, deep-purple, indigo, blue, light-blue, cyan, teal, green, light-green, lime, yellow, amber, orange, and deep-orange.
"color_accent": "blue",
"touch_icon": "images/logo.png",
"theme_color": "#036291",
"master_doc": False,
"nav_links": [
"edit_uri": "blob/main/docs",
"globaltoc_collapse": True,
"features": [
"navigation.expand",
"navigation.tabs",
"toc.integrate",
"navigation.sections",
"navigation.instant",
"header.autohide",
"navigation.top",
"navigation.tracking",
"search.highlight",
"search.share",
"toc.follow",
"toc.sticky",
"content.tabs.link",
"announce.dismiss",
],
"palette": [
{
"media": "(prefers-color-scheme: light)",
"scheme": "default",
"primary": "blue",
"accent": "light-blue",
"toggle": {
"icon": "material/lightbulb-outline",
"name": "Switch to dark mode",
},
},
{
"href": "https://flux-framework.org/",
"internal": False,
"title": "Flux Framework",
"media": "(prefers-color-scheme: dark)",
"scheme": "slate",
"primary": "blue",
"accent": "light-blue",
"toggle": {
"icon": "material/lightbulb",
"name": "Switch to light mode",
},
},
],
# BEGIN: version_dropdown
"version_dropdown": False,
"version_info": [
{
"href": "https://github.com/flux-framework",
"internal": False,
"title": "Flux Framework on GitHub",
"version": "https://sphinx-immaterial.rtfd.io",
"title": "ReadTheDocs",
"aliases": [],
},
{
"href": "https://github.com/flux-framework/flux-restful-api",
"internal": False,
"title": "Flux RESTful API on GitHub",
"version": "https://jbms.github.io/sphinx-immaterial",
"title": "Github Pages",
"aliases": [],
},
],
"heroes": {
"index": "Flux RESTful API",
"customization": "Flux RESTful API",
},
# Include the version dropdown top right? (e.g., if we use readthedocs)
"version_dropdown": False,
# Format of this is dict with [label,path]
# Since we are rendering on gh-pages without readthedocs, we don't
# have versions
# "version_json": "_static/versions.json",
# "version_info": {
# "Release": "https://online-ml.github.io/viz/",
# "Development": "https://online-ml.github.io/viz/devel/",
# "Release (rel)": "/viz/",
# "Development (rel)": "/viz/devel/",
# },
# Do NOT strip these classes from tables!
"table_classes": ["plain"],
# END: version_dropdown
"toc_title_is_page_title": True,
# BEGIN: social icons
"social": [
{
"icon": "fontawesome/brands/github",
"link": "https://github.com/flux-framework/flux-restful-api",
"name": "Flux RESTFul API on GitHub",
},
{
"icon": "material/chart-donut-variant",
"link": "https://flux-framework.org/",
"name": "Flux Framework",
},
],
# END: social icons
}

todo_include_todos = True
sphinx_immaterial_icon_path = html_static_path
sphinx_immaterial_bundle_source_maps = True

if FORCE_CLASSIC:
print("!!!!!!!!! Forcing classic !!!!!!!!!!!")
html_theme = "classic"
Expand Down
2 changes: 1 addition & 1 deletion docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sphinx_material
sphinx_immaterial
numpy>=1.16
pandas
matplotlib
Expand Down

0 comments on commit ee26737

Please sign in to comment.