Console API
JuiceFS Web Console provides API for all file system interactions that a user can perform through a web browser, e.g. create file systems, browse file system, check the trash directory or configure client ACL.
Since the Web Console does not provide file system modification capabilities, you cannot do that with the APIs as well. If you need to programmatically modify your file systems, use S3 Gateway.
This chapter only introduces the API authentication process, check the API schema to learn the specifics.
Usage
Code example
import base64
import hashlib
import hmac
import json
import time
from typing import Dict, List, Literal, Optional, Union
from urllib.parse import quote, urlencode, urlsplit
import requests
access_key = 'ac7418402ce0ce838ba87eb3a6be72af313cd7028e18007799c0d5651c326925'
secret_key = '5f0c5a5d51515947788fa7b8244acebe166aedd9de28b26ef716888a613c3d92'
def sign(
secret_key: str,
timestamp: int,
method: Literal['GET', 'POST', 'PATCH', 'PUT', 'DELETE'],
path: str,
headers: Dict[str, str],
query_params: Optional[Dict[str, Union[str, List[str]]]],
body: Optional[bytes],
) -> str:
sorted_headers = []
for h in ['Host']:
v = headers.get(h, '')
if not v:
raise ValueError(f'header {h} is required')
sorted_headers.append(f'{h.lower()}:{v}')
sorted_headers = '\n'.join(sorted_headers)
sorted_qs = ''
if query_params:
sorted_qs = sorted(query_params.items(), key=lambda item: item[0])
for i, (k, values) in enumerate(sorted_qs):
if not isinstance(values, list):
values = [values]
sorted_qs[i] = (k, sorted(values))
sorted_qs = urlencode(sorted_qs, doseq=True, quote_via=quote)
payload_hash = hashlib.sha256(body).hexdigest() if body else ''
parts = [str(timestamp), method, path, sorted_headers, sorted_qs, payload_hash]
data = '\n'.join(parts)
signature = hmac.new(
secret_key.encode('utf-8'), data.encode('utf-8'), hashlib.sha256
).hexdigest()
return signature
def request(
method: Literal['GET', 'POST', 'PATCH', 'PUT', 'DELETE'],
url: str,
query_params: Optional[dict] = None,
data: Optional[dict] = None,
):
timestamp = int(time.time())
parts = urlsplit(url)
path = parts.path
headers = {'Host': parts.netloc}
req = requests.Request(method, url, params=query_params, json=data).prepare()
body = req.body or b''
print(f'secret_key: {secret_key[:10]}...{secret_key[-10:]}')
print(f'timestamp: {timestamp}')
print(f'method: {method}')
print(f'path: {path}')
print(f'headers: {headers}')
print(f'query_params: {query_params if query_params else "empty"}')
print(f'body: {body if body else "empty"}')
signature = sign(secret_key, timestamp, method, path, headers, query_params, body)
print(f'signature: {signature}')
auth = {
'access_key': access_key,
'timestamp': timestamp,
'signature': signature,
'version': 1,
}
token = base64.b64encode(json.dumps(auth).encode()).decode()
print(f'token: {token}')
req.headers['Authorization'] = token
resp = requests.Session().send(req)
print()
print(json.dumps(resp.json(), indent=2))
# API schema doc: https://juicefs.com/api/v1/docs
API_URL = 'http://localhost:8080/api/v1'
def get_volumes():
url = f'{API_URL}/volumes'
request('GET', url)
def delete_volume():
url = f'{API_URL}/volumes/1'
request('DELETE', url)
def get_volume_exports():
url = f'{API_URL}/volumes/1/exports'
request('GET', url)
def create_volume_export():
url = f'{API_URL}/volumes/1/exports'
request(
'POST',
url,
data={
'desc': 'for mount',
'iprange': '192.168.0.1/24',
'apionly': False,
'readonly': False,
'appendonly': False,
},
)
def update_volume_export():
url = f'{API_URL}/volumes/1/exports/1'
request('PUT', url, data={'desc': 'for mount', 'iprange': '192.168.100.1/24'})
def get_volume_quotas():
url = f'{API_URL}/volumes/1/quotas'
request('GET', url)
def create_volume_quota():
url = f'{API_URL}/volumes/1/quotas'
request(
'POST',
url,
data={'path': '/path/to/subdir', 'inodes': 1 << 20, 'size': 1 << 30},
)
def update_volume_quota():
url = f'{API_URL}/volumes/1/quotas/9'
request('PUT', url, data={'path': '/foo', 'size': 10 << 30})