mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-03-29 16:48:41 +08:00
395 lines
11 KiB
Python
395 lines
11 KiB
Python
#!/usr/bin/env python2
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
This code is public domain.
|
|
Used as a demo for Bockstack.
|
|
"""
|
|
|
|
import requests
|
|
import urllib
|
|
import os
|
|
import sys
|
|
|
|
# need to pip install these
|
|
import blockstack_client as bsk
|
|
import jsontokens
|
|
import keylib
|
|
|
|
# application domain name (identifies the specific data store)
|
|
APPLICATION_NAME = 'files.blockstack.org'
|
|
|
|
# list of API families
|
|
APPLICATION_METHODS = [
|
|
'store_read',
|
|
'store_write',
|
|
'store_admin',
|
|
]
|
|
|
|
# ECDSA private key (different each time)
|
|
APPLICATION_KEY = keylib.ECPrivateKey().to_hex()
|
|
|
|
def bsk_get_url( path, port=6270 ):
|
|
"""
|
|
Make a URL to the local blockstack node
|
|
"""
|
|
return 'http://localhost:{}{}'.format(port, path)
|
|
|
|
|
|
def bsk_auth_headers( session ):
|
|
"""
|
|
Make authorization headers from a session token
|
|
"""
|
|
return {'Authorization': 'bearer {}'.format(session)}
|
|
|
|
|
|
def bsk_get_session( api_password, port=6270 ):
|
|
"""
|
|
Connect to the local blockstack node.
|
|
Get back a session.
|
|
|
|
Return the session (a JWT string) on success
|
|
Raise on error
|
|
"""
|
|
|
|
# will call `GET http://localhost:{port}/v1/auth?authRequest={auth JWT}`
|
|
# will get back a session JWT
|
|
|
|
# request permission to access the API
|
|
auth_request = {
|
|
'app_domain': APPLICATION_NAME,
|
|
'methods': APPLICATION_METHODS,
|
|
}
|
|
|
|
# authentication: basic {password}
|
|
headers = {
|
|
'Authorization': 'basic {}'.format(api_password)
|
|
}
|
|
|
|
# make the authentication token
|
|
signer = jsontokens.TokenSigner()
|
|
auth_token = signer.sign(auth_request, APPLICATION_KEY)
|
|
|
|
# ask for a session token
|
|
url = bsk_get_url('/v1/auth?authRequest={}'.format(auth_token), port=port)
|
|
req = requests.get(url, headers=headers )
|
|
|
|
if req.status_code == 200:
|
|
# good to go!
|
|
# expect {'token': ses token} JSON response
|
|
payload = req.json()
|
|
session = payload['token']
|
|
return session
|
|
|
|
else:
|
|
# whoops!
|
|
raise Exception("HTTP status {} from Blockstack on {}".format(req.status_code, url))
|
|
|
|
|
|
def bsk_make_datastore( session, drivers=None, port=6270 ):
|
|
"""
|
|
Make our data store. The data store will be specific
|
|
to this application's name (APPLICATION_NAME).
|
|
|
|
This method is idempotent. If the data store already exists,
|
|
then no action will be taken (except to query the ID)
|
|
|
|
Return the datastore ID on success (even if it already exists).
|
|
Raise on error
|
|
"""
|
|
|
|
# create a datastore with `POST http://localhost:{port}/v1/stores?drivers={drivers}`
|
|
# get back {'status': True, 'app_user_id': datastore ID}
|
|
|
|
# does this data store already exist?
|
|
auth_headers = bsk_auth_headers(session)
|
|
url = bsk_get_url('/v1/stores/{}'.format(APPLICATION_NAME), port=port)
|
|
req = requests.get(url, headers=auth_headers)
|
|
|
|
if req.status_code == 200:
|
|
# the data store already exists. Give back it's ID
|
|
datastore_info = req.json()
|
|
datastore_id = datastore_info['datastore_id']
|
|
return datastore_id
|
|
|
|
elif req.status_code != 404:
|
|
# some other error
|
|
raise Exception("HTTP status {} from Blockstack on {}".format(req.status_code, url))
|
|
|
|
# doesn't exist yet. Go create it.
|
|
url = bsk_get_url('/v1/stores', port=port)
|
|
|
|
if drivers is not None:
|
|
# include requested drivers
|
|
url += '?drivers={}'.format( urllib.quote(','.join(drivers)) )
|
|
|
|
req = requests.post(url, headers=auth_headers)
|
|
|
|
if req.status_code == 200:
|
|
# succeeded!
|
|
# get back the ID
|
|
datastore_res = req.json()
|
|
datastore_id = datastore_res['datastore_id']
|
|
return datastore_id
|
|
|
|
else:
|
|
# something broke!
|
|
raise Exception("HTTP status {} from Blockstack on {}".format(req.status_code, url))
|
|
|
|
|
|
def bsk_delete_datastore( session, port=6270 ):
|
|
"""
|
|
Delete our data store.
|
|
|
|
Return True on success
|
|
Raise on error
|
|
"""
|
|
|
|
# delete datastore with `DELETE http://localhost:{port}/v1/stores`
|
|
|
|
auth_headers = bsk_auth_headers(session)
|
|
url = bsk_get_url('/v1/stores', port=port)
|
|
req = requests.delete(url, headers=auth_headers)
|
|
|
|
if req.status_code == 200:
|
|
# success!
|
|
return True
|
|
|
|
else:
|
|
raise Exception("HTTP status {} from Blockstack on {}".format(req.status_code, url))
|
|
|
|
|
|
def bsk_put_file( session, datastore_id, local_path, bsk_path, port=6270 ):
|
|
"""
|
|
Put the file data pointed to by {local_path} into
|
|
{bsk_path}
|
|
|
|
Return True on success
|
|
Raise on error
|
|
"""
|
|
|
|
# put the file with `POST http://localhost:{port}/v1/stores/{store ID}/files?path={bsk_path}`
|
|
# get back {'status': True}
|
|
|
|
# get data from disk
|
|
with open(local_path, 'r') as f:
|
|
data = f.read()
|
|
|
|
# issue the request to store
|
|
auth_headers = bsk_auth_headers(session)
|
|
url = bsk_get_url('/v1/stores/{}/files?path={}'.format(datastore_id, urllib.quote(bsk_path)), port=port)
|
|
req = requests.post(url, headers=auth_headers, data=data)
|
|
|
|
if req.status_code == 200:
|
|
# success!
|
|
return True
|
|
|
|
else:
|
|
raise Exception("HTTP status {} from Blockstack on {}".format(req.status_code, url))
|
|
|
|
|
|
def bsk_get_file( session, datastore_id, bsk_path, port=6270, file=sys.stdout ):
|
|
"""
|
|
Get the file data pointed to by {bsk_path} from the data store.
|
|
Write it to stdout by default (override with file=)
|
|
|
|
Return True on success
|
|
Raise on error
|
|
"""
|
|
|
|
# get the file with `GET http://localhost:{port}/v1/stores/{store ID}/files?path={bsk_path}`
|
|
# get back raw data (application/octet-stream)
|
|
|
|
# issue the request to fetch
|
|
auth_headers = bsk_auth_headers(session)
|
|
url = bsk_get_url('/v1/stores/{}/files?path={}'.format(datastore_id, urllib.quote(bsk_path)), port=port)
|
|
req = requests.get(url, headers=auth_headers)
|
|
|
|
if req.status_code == 200:
|
|
# success!
|
|
print >> file, req.content
|
|
return True
|
|
|
|
else:
|
|
raise Exception("HTTP status {} from Blockstack on {}".format(req.status_code, url))
|
|
|
|
|
|
def bsk_delete_file( session, datastore_id, bsk_path, port=6270 ):
|
|
"""
|
|
Delete the file data pointed to by {bsk_path} from the data store.
|
|
|
|
Return True on success
|
|
Raise on error
|
|
"""
|
|
|
|
# delete with `DELETE http://localhost:{port}/v1/stores/{store ID}/files?path={bsk_path)`
|
|
# get back {'status': True}
|
|
|
|
# issue the request
|
|
auth_headers = bsk_auth_headers(session)
|
|
url = bsk_get_url('/v1/stores/{}/files?path={}'.format(datastore_id, urllib.quote(bsk_path)), port=port)
|
|
req = requests.delete(url, headers=auth_headers)
|
|
|
|
if req.status_code == 200:
|
|
# success!
|
|
return True
|
|
|
|
else:
|
|
raise Exception("HTTP status {} from Blockstack on {}".format(req.status_code, url))
|
|
|
|
|
|
def bsk_mkdir( session, datastore_id, bsk_path, port=6270 ):
|
|
"""
|
|
Make a directory at {bsk_path} in the given data store
|
|
|
|
Return True on success
|
|
Raise on error
|
|
"""
|
|
|
|
# make the directory with `POST http://localhost:{port}/v1/stores/{store ID}/directories?path={bsk_path}`
|
|
# get back {'status': True} (http 200)
|
|
|
|
# issue the request to fetch
|
|
auth_headers = bsk_auth_headers(session)
|
|
url = bsk_get_url('/v1/stores/{}/directories?path={}'.format(datastore_id, urllib.quote(bsk_path)), port=port)
|
|
req = requests.post(url, headers=auth_headers)
|
|
|
|
if req.status_code == 200:
|
|
# success!
|
|
return True
|
|
|
|
else:
|
|
raise Exception("HTTP status {} from Blockstack on {}".format(req.status_code, url))
|
|
|
|
|
|
def bsk_listdir( session, datastore_id, bsk_path, port=6270, file=sys.stdout ):
|
|
"""
|
|
List a directory at {bsk_path} in the given data store.
|
|
Write the listing to stdout.
|
|
|
|
Return True on success
|
|
Raise on error
|
|
"""
|
|
|
|
# list the directory with `GET http://localhost:{port}/v1/stores/{store ID}/directories?path={bsk_path}`
|
|
# get back the structured inode of the directory.
|
|
# iterate through the inode's children and print their names and types (i.e. append '/' to directories)
|
|
|
|
# issue the request to fetch
|
|
auth_headers = bsk_auth_headers(session)
|
|
url = bsk_get_url('/v1/stores/{}/directories?path={}'.format(datastore_id, urllib.quote(bsk_path)), port=port)
|
|
req = requests.get(url, headers=auth_headers)
|
|
|
|
if req.status_code == 200:
|
|
# success!
|
|
dir_listing = req.json()
|
|
|
|
# extract names and types
|
|
names = []
|
|
for name in dir_listing.keys():
|
|
dirent = dir_listing[name]
|
|
if dirent['type'] == 2: # this is a file
|
|
name += '/'
|
|
|
|
names.append(name)
|
|
|
|
names.sort()
|
|
print >> file, '\n'.join(names)
|
|
return True
|
|
|
|
else:
|
|
raise Exception("HTTP status {} from Blockstack on {}".format(req.status_code, url))
|
|
|
|
|
|
def bsk_rmdir( session, datastore_id, bsk_path, port=6270 ):
|
|
"""
|
|
Remove a directory at {bsk_path} in the given data store.
|
|
|
|
Return True on success
|
|
Raise on error
|
|
"""
|
|
|
|
# delete the directory with `DELETE http://localhost:{port}/v1/stores/{store ID}/directories?path={bsk_path}`
|
|
|
|
# issue the request
|
|
auth_headers = bsk_auth_headers(session)
|
|
url = bsk_get_url('/v1/stores/{}/directories?path={}'.format(datastore_id, urllib.quote(bsk_path)), port=port)
|
|
req = requests.delete(url, headers=auth_headers)
|
|
|
|
if req.status_code == 200:
|
|
# success!
|
|
return True
|
|
|
|
else:
|
|
raise Exception("HTTP status {} from Blockstack on {}".format(req.status_code, url))
|
|
|
|
|
|
def bsk_stat( session, datastore_id, bsk_path, port=6270 ):
|
|
"""
|
|
Stat a file or directory
|
|
|
|
Return the inode structure on success
|
|
Raise on error
|
|
"""
|
|
|
|
# send `GET http://localhost:{port}/v1/stores/{store ID}/inodes?path={bsk_path`
|
|
|
|
# issue the request
|
|
auth_headers = bsk_auth_headers(session)
|
|
url = bsk_get_url('/v1/stores/{}/inodes?path={}'.format(datastore_id, urllib.quote(bsk_path)), port=port)
|
|
req = requests.get(url, headers=auth_headers)
|
|
|
|
if req.status_code == 200:
|
|
# success!
|
|
return req.json()
|
|
|
|
else:
|
|
raise Exception('HTTP status {} from Blockstack on {}'.format(req.status_code, url))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# unit tests!
|
|
import StringIO
|
|
|
|
api_password = sys.argv[1]
|
|
ses = bsk_get_session(api_password)
|
|
assert ses, "Failed to authentcate"
|
|
|
|
datastore_id = bsk_make_datastore(ses, ['disk'])
|
|
assert datastore_id, "Failed to make datastore"
|
|
|
|
# load something
|
|
with open("/tmp/.footest", 'w') as f:
|
|
f.write("hello world\x00\x01\x02\x03\x04")
|
|
|
|
res = bsk_put_file(ses, datastore_id, "/tmp/.footest", "/foo")
|
|
assert res, "Failed to put file"
|
|
|
|
# get it back
|
|
sb = StringIO.StringIO()
|
|
res = bsk_get_file(ses, datastore_id, "/foo", file=sb)
|
|
assert res, "Failed to get file"
|
|
assert sb.getvalue() == "hello world\x00\x01\x02\x03\x04\n", "Got wrong data ({})".format(sb.getvalue())
|
|
|
|
# make a directory
|
|
res = bsk_mkdir(ses, datastore_id, "/bar")
|
|
assert res, "Failed to mkdir"
|
|
|
|
# list directory
|
|
sb = StringIO.StringIO()
|
|
res = bsk_listdir(ses, datastore_id, "/", file=sb)
|
|
assert res, "Failed to listdir"
|
|
assert sb.getvalue() == "bar/\nfoo\n", "Got wrong dir ({})".format(sb.getvalue())
|
|
|
|
# delete file
|
|
res = bsk_delete_file(ses, datastore_id, '/foo')
|
|
assert res
|
|
|
|
# delete directory
|
|
res = bsk_rmdir(ses, datastore_id, '/bar')
|
|
assert res
|
|
|
|
# delete datastore
|
|
res = bsk_delete_datastore(ses)
|
|
assert res
|
|
|