gluerpy 1.0.25__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gluer/__init__.py +0 -0
- gluer/client.py +204 -0
- gluer/client_redis.py +269 -0
- gluer/lib/__init__.py +0 -0
- gluer/lib/acl.py +148 -0
- gluer/lib/redis_lib.py +69 -0
- gluerpy-1.0.25.dist-info/METADATA +18 -0
- gluerpy-1.0.25.dist-info/RECORD +10 -0
- gluerpy-1.0.25.dist-info/WHEEL +5 -0
- gluerpy-1.0.25.dist-info/top_level.txt +1 -0
gluer/__init__.py
ADDED
|
File without changes
|
gluer/client.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import traceback
|
|
3
|
+
import time
|
|
4
|
+
import json
|
|
5
|
+
import uuid
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
import rel
|
|
9
|
+
import websocket
|
|
10
|
+
import importlib
|
|
11
|
+
from websockets.sync.client import connect
|
|
12
|
+
|
|
13
|
+
from mako.lookup import TemplateLookup
|
|
14
|
+
from mako.template import Template
|
|
15
|
+
|
|
16
|
+
project = "sm"
|
|
17
|
+
websocket_url = "ws://localhost:9001/server"
|
|
18
|
+
session_id = str(uuid.uuid4())
|
|
19
|
+
heartbeat_interval = 5
|
|
20
|
+
retry_delay = 5
|
|
21
|
+
ws = None
|
|
22
|
+
|
|
23
|
+
imported_files = {}
|
|
24
|
+
methods_map = {}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def set_project(p):
|
|
28
|
+
global project
|
|
29
|
+
project = p
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def set_websocket_url(url):
|
|
33
|
+
global websocket_url
|
|
34
|
+
websocket_url = url
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# async def start():
|
|
38
|
+
# print("Start")
|
|
39
|
+
# sub_server()
|
|
40
|
+
# await asyncio.get_event_loop().run_until_complete(start_ws())
|
|
41
|
+
# await asyncio.to_thread(queue())
|
|
42
|
+
|
|
43
|
+
def build_header_from_message(job):
|
|
44
|
+
smh = job["smh"].split(":")
|
|
45
|
+
return {"d": smh[0], "session_id": smh[1].split('>'), "project": smh[2], "plugin": smh[3] or "", "action": smh[4] or ""}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def build_message_from_header(header):
|
|
49
|
+
return f'{header["d"]}:{">".join(header["session_id"])}>{session_id}:{header["project"]}:{header["plugin"] or ""}:{header["action"] or ""}'
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def on_message(wsapp, message):
|
|
53
|
+
# print("on_message")
|
|
54
|
+
# print(message)
|
|
55
|
+
job = json.loads(message)
|
|
56
|
+
header = build_header_from_message(job)
|
|
57
|
+
job["smh"] = build_message_from_header(header)
|
|
58
|
+
# print("header")
|
|
59
|
+
# print(header)
|
|
60
|
+
try:
|
|
61
|
+
mtd = None
|
|
62
|
+
if header["plugin"] in methods_map and header["action"] in methods_map[header["plugin"]]:
|
|
63
|
+
mtd = methods_map[header["plugin"]][header["action"]]
|
|
64
|
+
elif header["plugin"] in imported_files:
|
|
65
|
+
if hasattr(imported_files[header["plugin"]], header["action"]):
|
|
66
|
+
mtd = getattr(
|
|
67
|
+
imported_files[header["plugin"]], header["action"])
|
|
68
|
+
if mtd:
|
|
69
|
+
ret = None
|
|
70
|
+
if "data" in job:
|
|
71
|
+
ret = mtd(job["data"])
|
|
72
|
+
else:
|
|
73
|
+
ret = mtd(job)
|
|
74
|
+
# print("Pre Process")
|
|
75
|
+
process_dict(ret)
|
|
76
|
+
# print(ret)
|
|
77
|
+
# print("Post Process")
|
|
78
|
+
job["data"] = ret
|
|
79
|
+
# if "htmx" in job:
|
|
80
|
+
# # print("HTMX found")
|
|
81
|
+
# try:
|
|
82
|
+
# act_template = lookup.get_template(
|
|
83
|
+
# f'{header["plugin"]}/{header["action"]}.mako')
|
|
84
|
+
# # print("act_template")
|
|
85
|
+
# # print(act_template)
|
|
86
|
+
# if act_template:
|
|
87
|
+
# # print("Template Found")
|
|
88
|
+
# # print(ret)
|
|
89
|
+
# # print(job["data"])
|
|
90
|
+
# job["data"] = act_template.render(data=ret)
|
|
91
|
+
# # print(job["data"])
|
|
92
|
+
# except Exception as error:
|
|
93
|
+
# print(error)
|
|
94
|
+
else:
|
|
95
|
+
job["data"] = {
|
|
96
|
+
"error": f"no server actions found for {header['plugin']}:{header['action']}"
|
|
97
|
+
}
|
|
98
|
+
except Exception as error:
|
|
99
|
+
print("ERROR")
|
|
100
|
+
print(traceback.format_exc())
|
|
101
|
+
# print(error)
|
|
102
|
+
error_handler(job, error)
|
|
103
|
+
# if "ws" in job:
|
|
104
|
+
|
|
105
|
+
job["smh"] = "<" + job["smh"][1:]
|
|
106
|
+
# print("Sending job back")
|
|
107
|
+
# print(job["data"])
|
|
108
|
+
# job["d"] = "<"
|
|
109
|
+
wsapp.send(json.dumps(job))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def on_open(ws):
|
|
113
|
+
print("Opened")
|
|
114
|
+
job = f'{{"smh":"+:{session_id}:{project}","channel":"{session_id},{project}"}}'
|
|
115
|
+
ws.send(job)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def on_error(ws, error):
|
|
119
|
+
print("Error")
|
|
120
|
+
print(ws)
|
|
121
|
+
print(error)
|
|
122
|
+
# job = f'{{"smh":"+:{session_id}:{project}","channel":"{session_id},{project}"}}'
|
|
123
|
+
# ws.send(job)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def start():
|
|
127
|
+
global ws
|
|
128
|
+
# with connect(websocket_url) as websocket:
|
|
129
|
+
# ws = websocket
|
|
130
|
+
# message = websocket.recv()
|
|
131
|
+
# print(f"Received: {message}")
|
|
132
|
+
# websocket.enableTrace(false)
|
|
133
|
+
ws = websocket.WebSocketApp(
|
|
134
|
+
websocket_url, on_message=on_message, on_open=on_open, on_error=on_error)
|
|
135
|
+
ws.run_forever(dispatcher=rel, ping_interval=60, ping_timeout=10, reconnect=5,
|
|
136
|
+
ping_payload="This is an optional ping payload")
|
|
137
|
+
|
|
138
|
+
rel.dispatch()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
lookup = None
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def import_directories(path):
|
|
145
|
+
global lookup
|
|
146
|
+
files = os.listdir(path)
|
|
147
|
+
# print(files)
|
|
148
|
+
sys.path.append(path)
|
|
149
|
+
for file in files:
|
|
150
|
+
if file.endswith(".py"):
|
|
151
|
+
try:
|
|
152
|
+
file = file[:-3]
|
|
153
|
+
imported_files.setdefault(file, importlib.import_module(file))
|
|
154
|
+
print(f"Imported {file}")
|
|
155
|
+
except ImportError as err:
|
|
156
|
+
print('Error:', err)
|
|
157
|
+
lookup = TemplateLookup(directories=[f'{path}/templates'])
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def add_method(plugin, action, fn):
|
|
161
|
+
print("Adding method", plugin, action)
|
|
162
|
+
methods_map.setdefault(plugin, {})
|
|
163
|
+
methods_map[plugin].setdefault(action, fn)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def error_handler(job, error):
|
|
167
|
+
job["data"] = {"error": {
|
|
168
|
+
"message": error.content[0]['message'], "code": error.content[0]['errorCode']}}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def set_error_handler(fn):
|
|
172
|
+
global error_handler
|
|
173
|
+
error_handler = fn
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def process_dict(data):
|
|
177
|
+
for key, value in data.items():
|
|
178
|
+
# Check if the value has a 'model_dump' method
|
|
179
|
+
if hasattr(value, "model_dump") and callable(getattr(value, "model_dump")):
|
|
180
|
+
# Call model_dump() if it's available and callable
|
|
181
|
+
data[key] = value.model_dump()
|
|
182
|
+
elif isinstance(value, dict):
|
|
183
|
+
# If the value is a nested dictionary, recursively process it
|
|
184
|
+
process_dict(value)
|
|
185
|
+
elif isinstance(value, list):
|
|
186
|
+
# If the value is a list, process each item
|
|
187
|
+
for i, item in enumerate(value):
|
|
188
|
+
if hasattr(item, "model_dump") and callable(getattr(item, "model_dump")):
|
|
189
|
+
value[i] = item.model_dump()
|
|
190
|
+
elif isinstance(item, dict):
|
|
191
|
+
process_dict(item)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
if __name__ == '__main__':
|
|
195
|
+
print('start')
|
|
196
|
+
# redis.set_user("Teste")
|
|
197
|
+
# try:
|
|
198
|
+
# # loop = asyncio.get_event_loop()
|
|
199
|
+
# # loop.run_until_complete(start())
|
|
200
|
+
# asyncio.run(start())
|
|
201
|
+
# except Exception as error:
|
|
202
|
+
# print(error)
|
|
203
|
+
# traceback.print_exc()
|
|
204
|
+
# # asyncio.run(start())
|
gluer/client_redis.py
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import asyncio
|
|
3
|
+
import threading
|
|
4
|
+
import traceback
|
|
5
|
+
import time
|
|
6
|
+
import json
|
|
7
|
+
import uuid
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
import importlib
|
|
11
|
+
import redis
|
|
12
|
+
|
|
13
|
+
import gluer.lib.redis_lib as redis_lib
|
|
14
|
+
import gluer.lib.acl as acl
|
|
15
|
+
|
|
16
|
+
project = "sm"
|
|
17
|
+
session_id = str(uuid.uuid4())
|
|
18
|
+
heartbeat_interval = 5
|
|
19
|
+
retry_delay = 5
|
|
20
|
+
|
|
21
|
+
imported_files = {}
|
|
22
|
+
methods_map = {}
|
|
23
|
+
|
|
24
|
+
# ssh -R 80:localhost:9001 serveo.net
|
|
25
|
+
# autossh -M 0 -R gluer.serveo.net:80:localhost:9001 serveo.net
|
|
26
|
+
# autossh -M 0 -R gluer:80:localhost:9001 serveo.net
|
|
27
|
+
# ssh -R gluer.serveo.net:443:localhost:9001 serveo.net
|
|
28
|
+
# ssh -R 443:localhost:9001 serveo.net
|
|
29
|
+
|
|
30
|
+
# def import_directories():
|
|
31
|
+
# files = os.listdir('./plugins')
|
|
32
|
+
# # print(files)
|
|
33
|
+
# sys.path.append('./plugins')
|
|
34
|
+
# for file in files:
|
|
35
|
+
# if file.endswith(".py"):
|
|
36
|
+
# try:
|
|
37
|
+
# file = file[:-3]
|
|
38
|
+
# imported_files.setdefault(file, importlib.import_module(file))
|
|
39
|
+
# except ImportError as err:
|
|
40
|
+
# print('Error:', err)
|
|
41
|
+
# # print(imported_files)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def set_project(p):
|
|
45
|
+
global project
|
|
46
|
+
project = p
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def set_user(u):
|
|
50
|
+
redis_lib.set_redis_user(u)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def set_password(p):
|
|
54
|
+
redis_lib.set_redis_password(p)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def set_redis_url(url):
|
|
58
|
+
redis_lib.set_redis_url(url)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def set_redis_api_key(key):
|
|
62
|
+
acl.set_api_key(key)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def set_redis_api_secret(secret):
|
|
66
|
+
acl.set_api_secret(secret)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
async def start():
|
|
70
|
+
# print("Start")
|
|
71
|
+
sub_server()
|
|
72
|
+
await asyncio.to_thread(queue())
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def import_directories(path):
|
|
76
|
+
files = os.listdir(path)
|
|
77
|
+
# print(files)
|
|
78
|
+
sys.path.append(path)
|
|
79
|
+
for file in files:
|
|
80
|
+
if file.endswith(".py"):
|
|
81
|
+
try:
|
|
82
|
+
file = file[:-3]
|
|
83
|
+
imported_files.setdefault(file, importlib.import_module(file))
|
|
84
|
+
print(f"Imported {file}")
|
|
85
|
+
except ImportError as err:
|
|
86
|
+
print('Error:', err)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def sub_server():
|
|
90
|
+
print("Sub Server")
|
|
91
|
+
# r = await redis.from_url("redis://admin:V!6xU8Kf*sQqJS@redis-10264.c277.us-east-1-3.ec2.redns.redis-cloud.com:10264")
|
|
92
|
+
|
|
93
|
+
# acl.create_acl("test2", "test123")
|
|
94
|
+
ev = {
|
|
95
|
+
"channel": f'{project}:bl',
|
|
96
|
+
"action": "server_up",
|
|
97
|
+
"created": time.time_ns()
|
|
98
|
+
}
|
|
99
|
+
# get_redis_connection().hset(
|
|
100
|
+
# f'pp1:servers:{session_id}', mapping=session_data)
|
|
101
|
+
# get_redis_connection().expire(
|
|
102
|
+
# f'pp1:servers:{session_id}', heartbeat_interval)
|
|
103
|
+
# ev = {
|
|
104
|
+
# "channel": f'{project}:bl',
|
|
105
|
+
# "action": "server_up",
|
|
106
|
+
# "created": time.time_ns()
|
|
107
|
+
# }
|
|
108
|
+
redis_lib.get_redis_connection().publish(f'{project}:bl', json.dumps(ev))
|
|
109
|
+
pubsub = redis_lib.get_redis_connection().pubsub()
|
|
110
|
+
subscribe_channel(pubsub, f'{project}:br')
|
|
111
|
+
|
|
112
|
+
# update_redis_hash()
|
|
113
|
+
# Run the update_redis_hash function in a separate thread
|
|
114
|
+
interval_thread = threading.Thread(target=update_redis_hash)
|
|
115
|
+
interval_thread.daemon = True # Daemon thread will exit when the main program exits
|
|
116
|
+
interval_thread.start()
|
|
117
|
+
|
|
118
|
+
# while True:
|
|
119
|
+
# try:
|
|
120
|
+
# # Listen for messages
|
|
121
|
+
# for message in pubsub.listen():
|
|
122
|
+
# if message['type'] == 'message':
|
|
123
|
+
# print(f"Message received: {message['data']}")
|
|
124
|
+
|
|
125
|
+
# except Exception as e:
|
|
126
|
+
# time.sleep(5)
|
|
127
|
+
# pubsub = get_redis_connection().pubsub()
|
|
128
|
+
# subscribe_channel(pubsub, f'{project}:br')
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def update_redis_hash():
|
|
132
|
+
while True:
|
|
133
|
+
session_data = {
|
|
134
|
+
"type": "plugin",
|
|
135
|
+
"sessionId": session_id,
|
|
136
|
+
"project": project
|
|
137
|
+
}
|
|
138
|
+
try:
|
|
139
|
+
redis_lib.get_redis_connection().hset(
|
|
140
|
+
f'pp1:servers:{session_id}', mapping=session_data)
|
|
141
|
+
redis_lib.get_redis_connection().expire(
|
|
142
|
+
f'pp1:servers:{session_id}', heartbeat_interval)
|
|
143
|
+
# print("Ping Redis", session_id)
|
|
144
|
+
time.sleep(heartbeat_interval-1)
|
|
145
|
+
except (redis.exceptions.ConnectionError, redis.exceptions.TimeoutError) as e:
|
|
146
|
+
print(e)
|
|
147
|
+
except Exception as e:
|
|
148
|
+
print(e)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def message_handler(message):
|
|
152
|
+
print("message_handler")
|
|
153
|
+
print(message.get("data"))
|
|
154
|
+
job = json.loads(message.get("data"))
|
|
155
|
+
job["connected"] = True
|
|
156
|
+
if "action" in job and job["action"] == "ping":
|
|
157
|
+
print("PING")
|
|
158
|
+
if "ws" in job:
|
|
159
|
+
redis_lib.get_redis_connection().rpush(
|
|
160
|
+
job["ws"], json.dumps(job))
|
|
161
|
+
redis_lib.get_redis_connection().expire(
|
|
162
|
+
job["ws"], 60)
|
|
163
|
+
# redis_lib.get_redis_connection().publish(f'{project}:bl', json.dumps(job))
|
|
164
|
+
|
|
165
|
+
elif "plugin" in job and job["plugin"] in imported_files:
|
|
166
|
+
if hasattr(imported_files[job["plugin"]], job["action"]):
|
|
167
|
+
mtd = getattr(
|
|
168
|
+
imported_files[job["plugin"]], job["action"])
|
|
169
|
+
ret = mtd(job["data"])
|
|
170
|
+
job["data"] = ret
|
|
171
|
+
print('job with data')
|
|
172
|
+
print(job)
|
|
173
|
+
if "ws" in job:
|
|
174
|
+
redis_lib.get_redis_connection().rpush(
|
|
175
|
+
job["ws"], json.dumps(job))
|
|
176
|
+
redis_lib.get_redis_connection().expire(
|
|
177
|
+
job["ws"], 60)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def subscribe_channel(pubsub, channel):
|
|
181
|
+
print("Subscribe", channel)
|
|
182
|
+
# pubsub.subscribe(channel)
|
|
183
|
+
pubsub.subscribe(**{channel: message_handler})
|
|
184
|
+
pubsub.run_in_thread(sleep_time=0.001)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def add_method(plugin, action, fn):
|
|
188
|
+
print("Adding method", plugin, action)
|
|
189
|
+
methods_map.setdefault(plugin, {})
|
|
190
|
+
methods_map[plugin].setdefault(action, fn)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def error_handler(job, error):
|
|
194
|
+
job["data"] = {"error": {
|
|
195
|
+
"message": error.content[0]['message'], "code": error.content[0]['errorCode']}}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def set_error_handler(fn):
|
|
199
|
+
global error_handler
|
|
200
|
+
error_handler = fn
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def queue():
|
|
204
|
+
# print('Queue')
|
|
205
|
+
# r = await redis.from_url("redis://admin:V!6xU8Kf*sQqJS@redis-10264.c277.us-east-1-3.ec2.redns.redis-cloud.com:10264")
|
|
206
|
+
while True:
|
|
207
|
+
# print(f'{project}:r')
|
|
208
|
+
try:
|
|
209
|
+
val = redis_lib.get_redis_connection().blpop(f'{project}:r', 5)
|
|
210
|
+
if val:
|
|
211
|
+
job = json.loads(val[1])
|
|
212
|
+
try:
|
|
213
|
+
mtd = None
|
|
214
|
+
if job["plugin"] in methods_map and job["action"] in methods_map[job["plugin"]]:
|
|
215
|
+
mtd = methods_map[job["plugin"]][job["action"]]
|
|
216
|
+
elif job["plugin"] in imported_files:
|
|
217
|
+
if hasattr(imported_files[job["plugin"]], job["action"]):
|
|
218
|
+
mtd = getattr(
|
|
219
|
+
imported_files[job["plugin"]], job["action"])
|
|
220
|
+
if mtd:
|
|
221
|
+
ret = None
|
|
222
|
+
if "data" in job:
|
|
223
|
+
ret = mtd(job["data"])
|
|
224
|
+
else:
|
|
225
|
+
ret = mtd(job)
|
|
226
|
+
job["data"] = ret
|
|
227
|
+
else:
|
|
228
|
+
job["data"] = {
|
|
229
|
+
"error": f"no server actions found for {job['plugin']}:{job['action']}"
|
|
230
|
+
}
|
|
231
|
+
except Exception as error:
|
|
232
|
+
print("ERROR")
|
|
233
|
+
print(traceback.format_exc())
|
|
234
|
+
# print(error)
|
|
235
|
+
error_handler(job, error)
|
|
236
|
+
if "ws" in job:
|
|
237
|
+
redis_lib.get_redis_connection().rpush(
|
|
238
|
+
job["ws"], json.dumps(job))
|
|
239
|
+
redis_lib.get_redis_connection().expire(
|
|
240
|
+
job["ws"], 60)
|
|
241
|
+
except (redis.exceptions.ConnectionError, redis.exceptions.TimeoutError) as e:
|
|
242
|
+
print("Redis connection lost. Reconnecting...")
|
|
243
|
+
print(e)
|
|
244
|
+
time.sleep(retry_delay)
|
|
245
|
+
|
|
246
|
+
except redis.exceptions.RedisError as e:
|
|
247
|
+
# Catch other general Redis-related exceptions
|
|
248
|
+
print("General Redis error")
|
|
249
|
+
print(e)
|
|
250
|
+
# traceback.print_exc()
|
|
251
|
+
|
|
252
|
+
except Exception as error:
|
|
253
|
+
print("Unexpected error on brpop queue")
|
|
254
|
+
print(error)
|
|
255
|
+
|
|
256
|
+
def queue_ws():
|
|
257
|
+
print("Queue WS")
|
|
258
|
+
|
|
259
|
+
if __name__ == '__main__':
|
|
260
|
+
print('start')
|
|
261
|
+
# redis.set_user("Teste")
|
|
262
|
+
# try:
|
|
263
|
+
# # loop = asyncio.get_event_loop()
|
|
264
|
+
# # loop.run_until_complete(start())
|
|
265
|
+
# asyncio.run(start())
|
|
266
|
+
# except Exception as error:
|
|
267
|
+
# print(error)
|
|
268
|
+
# traceback.print_exc()
|
|
269
|
+
# # asyncio.run(start())
|
gluer/lib/__init__.py
ADDED
|
File without changes
|
gluer/lib/acl.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import time
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
API_KEY = os.getenv('REDIS_API_KEY',None)
|
|
6
|
+
API_SECRET_KEY = os.getenv('REDIS_API_SECRET',None)
|
|
7
|
+
|
|
8
|
+
headers = {
|
|
9
|
+
"x-api-key": "Ay9adtha5prx8za3413t4v9y5vptrvst1aulowgz6r30uz2oa7",
|
|
10
|
+
"x-api-secret-key": "S3ctl9u1zjg5cbcyma38bgnbrq6q2ia6pud7lwvaonramcpanq9",
|
|
11
|
+
"Content-Type": "application/json"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def set_api_key(key):
|
|
16
|
+
API_KEY = key
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def set_api_secret(secret):
|
|
20
|
+
API_SECRET_KEY = secret
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def check_task_status(task_url):
|
|
24
|
+
while True:
|
|
25
|
+
response = requests.get(task_url, headers=headers)
|
|
26
|
+
if response.status_code == 200:
|
|
27
|
+
task_status = response.json()
|
|
28
|
+
status = task_status.get('status')
|
|
29
|
+
|
|
30
|
+
if status == 'processing-completed':
|
|
31
|
+
return task_status
|
|
32
|
+
elif status in ['received', 'processing', 'processing-in-progress']:
|
|
33
|
+
print(f"Task is still processing: {status}")
|
|
34
|
+
time.sleep(5) # Non-blocking sleep
|
|
35
|
+
elif status == 'processing-error':
|
|
36
|
+
raise Exception(
|
|
37
|
+
f"Task failed with status: {status}. Error details: {task_status.get('response', {}).get('error', {}).get('description', 'No details provided')}")
|
|
38
|
+
else:
|
|
39
|
+
raise Exception(f"Unexpected task status: {status}")
|
|
40
|
+
else:
|
|
41
|
+
response.raise_for_status()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def create_acl(name, password):
|
|
45
|
+
rule = create_rule(name)
|
|
46
|
+
role = create_role(name)
|
|
47
|
+
user = create_user(name, password)
|
|
48
|
+
print("ACL Created")
|
|
49
|
+
print(rule)
|
|
50
|
+
print(role)
|
|
51
|
+
print(user)
|
|
52
|
+
return {"rule": rule, "role": role, "user": user}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def delete_acl(name):
|
|
56
|
+
delete_user(name)
|
|
57
|
+
# create_rule(name)
|
|
58
|
+
# create_role(name)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def create_rule(name):
|
|
62
|
+
print('Create Rule')
|
|
63
|
+
print(name)
|
|
64
|
+
data = {
|
|
65
|
+
"name": name,
|
|
66
|
+
"redisRule": f'+hset +unlink +pubsub +subscribe +brpop +blpop +rpush +lpush ~{name}* +pubsub ~pp1:* ~ws:*'
|
|
67
|
+
}
|
|
68
|
+
print(data)
|
|
69
|
+
response = requests.post(
|
|
70
|
+
'https://api.redislabs.com/v1/acl/redisRules', json=data, headers=headers)
|
|
71
|
+
response.raise_for_status()
|
|
72
|
+
data = response.json()
|
|
73
|
+
print(data)
|
|
74
|
+
if "links" in data and len(data["links"]) > 0:
|
|
75
|
+
status = check_task_status(data["links"][0]["href"])
|
|
76
|
+
print(status)
|
|
77
|
+
return status.get('response').get('resourceId')
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def create_role(name):
|
|
81
|
+
data = {
|
|
82
|
+
"name": name,
|
|
83
|
+
"redisRules": [
|
|
84
|
+
{
|
|
85
|
+
"ruleName": name,
|
|
86
|
+
"databases": [
|
|
87
|
+
{
|
|
88
|
+
"subscriptionId": 2350147,
|
|
89
|
+
"databaseId": 12364997
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
response = requests.post(
|
|
96
|
+
'https://api.redislabs.com/v1/acl/roles', json=data, headers=headers)
|
|
97
|
+
response.raise_for_status()
|
|
98
|
+
data = response.json()
|
|
99
|
+
print(data)
|
|
100
|
+
if "links" in data and len(data["links"]) > 0:
|
|
101
|
+
status = check_task_status(data["links"][0]["href"])
|
|
102
|
+
print(status)
|
|
103
|
+
return status.get('response').get('resourceId')
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def create_user(name, password):
|
|
107
|
+
if not password:
|
|
108
|
+
passowrd = generate_random_password()
|
|
109
|
+
data = {
|
|
110
|
+
"name": name,
|
|
111
|
+
"role": name,
|
|
112
|
+
"password": password
|
|
113
|
+
}
|
|
114
|
+
response = requests.post(
|
|
115
|
+
'https://api.redislabs.com/v1/acl/users', json=data, headers=headers)
|
|
116
|
+
response.raise_for_status()
|
|
117
|
+
data = response.json()
|
|
118
|
+
print(data)
|
|
119
|
+
if "links" in data and len(data["links"]) > 0:
|
|
120
|
+
status = check_task_status(data["links"][0]["href"])
|
|
121
|
+
print(status)
|
|
122
|
+
return status.get('response').get('resourceId')
|
|
123
|
+
# return data
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def delete_user(name):
|
|
127
|
+
data = {
|
|
128
|
+
"name": name,
|
|
129
|
+
"role": name,
|
|
130
|
+
"password": password
|
|
131
|
+
}
|
|
132
|
+
response = requests.post(
|
|
133
|
+
'https://api.redislabs.com/v1/acl/users', json=data, headers=headers)
|
|
134
|
+
response.raise_for_status()
|
|
135
|
+
data = response.json()
|
|
136
|
+
print(data)
|
|
137
|
+
if "links" in data and len(data["links"]) > 0:
|
|
138
|
+
status = check_task_status(data["links"][0]["href"])
|
|
139
|
+
print(status)
|
|
140
|
+
return data
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def generate_random_password(length=12):
|
|
144
|
+
# Define the character sets to use
|
|
145
|
+
characters = string.ascii_letters + string.digits + '!@$'
|
|
146
|
+
# Generate a random password
|
|
147
|
+
password = ''.join(random.choice(characters) for _ in range(length))
|
|
148
|
+
return password
|
gluer/lib/redis_lib.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# redis_connection.py
|
|
2
|
+
import redis
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
redis_client = None
|
|
6
|
+
redis_pubsub = None
|
|
7
|
+
|
|
8
|
+
redis_url = os.getenv('REDIS_URL', None)
|
|
9
|
+
redis_user = os.getenv('REDIS_USER', None)
|
|
10
|
+
redis_password = os.getenv('REDIS_PASSWORD', None)
|
|
11
|
+
redis_host = os.getenv(
|
|
12
|
+
'REDIS_HOST', "redis-10264.c277.us-east-1-3.ec2.redns.redis-cloud.com")
|
|
13
|
+
redis_port = os.getenv('REDIS_PORT', "10264")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def set_redis_url(u):
|
|
17
|
+
global redis_url
|
|
18
|
+
redis_url = u
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def set_redis_user(u):
|
|
22
|
+
global redis_user
|
|
23
|
+
redis_user = u
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def set_redis_password(p):
|
|
27
|
+
global redis_password
|
|
28
|
+
redis_password = p
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
events = {}
|
|
32
|
+
|
|
33
|
+
# pool = None
|
|
34
|
+
# r = redis.Redis(connection_pool=pool)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_url():
|
|
38
|
+
global redis_url, redis_user, redis_password
|
|
39
|
+
if redis_url:
|
|
40
|
+
return redis_url
|
|
41
|
+
elif redis_user and redis_password:
|
|
42
|
+
return f"redis://{redis_user}:{redis_password}@{redis_host}:{redis_port}"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_redis_connection():
|
|
46
|
+
global redis_client, redis_url
|
|
47
|
+
if redis_client is None:
|
|
48
|
+
redis_client = redis.from_url(redis_url,
|
|
49
|
+
decode_responses=True)
|
|
50
|
+
return redis_client
|
|
51
|
+
# if pool is None:
|
|
52
|
+
# pool = redis.ConnectionPool.from_url(get_url())
|
|
53
|
+
# return redis.Redis(connection_pool=pool, decode_responses=True)
|
|
54
|
+
|
|
55
|
+
# if redis_client is None:
|
|
56
|
+
# if redis_user and redis_password:
|
|
57
|
+
# redis_url = f"redis://{redis_user}:{redis_password}@redis-10264.c277.us-east-1-3.ec2.redns.redis-cloud.com:10264"
|
|
58
|
+
# redis_client = redis.from_url(redis_url,
|
|
59
|
+
# decode_responses=True)
|
|
60
|
+
# # redis_client = redis.Redis(
|
|
61
|
+
# # host='localhost', port=6379, decode_responses=True)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_redis_pubsub():
|
|
65
|
+
global redis_pubsub
|
|
66
|
+
if redis_pubsub is None:
|
|
67
|
+
redis_pubsub = get_redis_connection().pubsub()
|
|
68
|
+
redis_pubsub.run_in_thread(sleep_time=0.001)
|
|
69
|
+
return redis_pubsub
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: gluerpy
|
|
3
|
+
Version: 1.0.25
|
|
4
|
+
Summary: Python library to connect to gluer services
|
|
5
|
+
Home-page: https://www.gluer.io
|
|
6
|
+
Author: Thiago Magro
|
|
7
|
+
Author-email: thiago.magro@gmail.com
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Platform: UNKNOWN
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.6
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: redis
|
|
16
|
+
|
|
17
|
+
Gluer Library for Python
|
|
18
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
gluer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
gluer/client.py,sha256=KwyP0beFHwp-Fja3FILXl3G5GHWLp55eTf-jArCyq4w,6109
|
|
3
|
+
gluer/client_redis.py,sha256=P1x24i9it9p7H0mNAp-8ZfiuohDvCItneSJiQeoLwSU,8355
|
|
4
|
+
gluer/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
gluer/lib/acl.py,sha256=7laiX-NqpzqTb3LXlZkk6LBGQPBPir_6vxnmupyx_D4,4342
|
|
6
|
+
gluer/lib/redis_lib.py,sha256=hjPGkacuiJLHYrEj-5rg3rMcAm83lp63BPetvrlY964,1891
|
|
7
|
+
gluerpy-1.0.25.dist-info/METADATA,sha256=iFcYy2C3YurZyFYJtzUvy-g84PT62AoJwWUHUrBPPC0,487
|
|
8
|
+
gluerpy-1.0.25.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
|
|
9
|
+
gluerpy-1.0.25.dist-info/top_level.txt,sha256=V1vaafqCPsG50XMIBcZxsIHrkXQK6EWMM_8DFVbvbH8,6
|
|
10
|
+
gluerpy-1.0.25.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gluer
|