drown 0.2.0__tar.gz → 0.2.2__tar.gz
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.
- {drown-0.2.0/drown.egg-info → drown-0.2.2}/PKG-INFO +1 -1
- {drown-0.2.0 → drown-0.2.2}/drown/__init__.py +1 -1
- drown-0.2.2/drown/api.py +187 -0
- {drown-0.2.0 → drown-0.2.2}/drown/cli.py +2 -2
- {drown-0.2.0 → drown-0.2.2/drown.egg-info}/PKG-INFO +1 -1
- {drown-0.2.0 → drown-0.2.2}/setup.py +1 -1
- drown-0.2.0/drown/api.py +0 -156
- {drown-0.2.0 → drown-0.2.2}/LICENSE +0 -0
- {drown-0.2.0 → drown-0.2.2}/MANIFEST.in +0 -0
- {drown-0.2.0 → drown-0.2.2}/README.md +0 -0
- {drown-0.2.0 → drown-0.2.2}/drown/config.py +0 -0
- {drown-0.2.0 → drown-0.2.2}/drown.egg-info/SOURCES.txt +0 -0
- {drown-0.2.0 → drown-0.2.2}/drown.egg-info/dependency_links.txt +0 -0
- {drown-0.2.0 → drown-0.2.2}/drown.egg-info/entry_points.txt +0 -0
- {drown-0.2.0 → drown-0.2.2}/drown.egg-info/requires.txt +0 -0
- {drown-0.2.0 → drown-0.2.2}/drown.egg-info/top_level.txt +0 -0
- {drown-0.2.0 → drown-0.2.2}/requirements.txt +0 -0
- {drown-0.2.0 → drown-0.2.2}/setup.cfg +0 -0
drown-0.2.2/drown/api.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""API wrapper functions for Drown Platform."""
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
from drown.config import get_api_base
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _make_request(method, endpoint, token=None, data=None):
|
|
8
|
+
"""
|
|
9
|
+
Make an HTTP request to the API.
|
|
10
|
+
|
|
11
|
+
Returns: (success: bool, result: dict)
|
|
12
|
+
"""
|
|
13
|
+
api_base = get_api_base()
|
|
14
|
+
url = f"{api_base}{endpoint}"
|
|
15
|
+
|
|
16
|
+
headers = {}
|
|
17
|
+
if token:
|
|
18
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
if method == "GET":
|
|
22
|
+
response = requests.get(url, headers=headers, timeout=10)
|
|
23
|
+
elif method == "POST":
|
|
24
|
+
headers["Content-Type"] = "application/json"
|
|
25
|
+
response = requests.post(url, headers=headers, json=data, timeout=10)
|
|
26
|
+
else:
|
|
27
|
+
return False, {"error": f"Unsupported HTTP method: {method}"}
|
|
28
|
+
|
|
29
|
+
if response.status_code == 200:
|
|
30
|
+
try:
|
|
31
|
+
return True, response.json()
|
|
32
|
+
except ValueError:
|
|
33
|
+
return True, {"message": response.text}
|
|
34
|
+
|
|
35
|
+
elif response.status_code == 201:
|
|
36
|
+
try:
|
|
37
|
+
return True, response.json()
|
|
38
|
+
except ValueError:
|
|
39
|
+
return True, {"message": "Created successfully"}
|
|
40
|
+
|
|
41
|
+
elif response.status_code == 400:
|
|
42
|
+
try:
|
|
43
|
+
error_data = response.json()
|
|
44
|
+
error_msg = error_data.get("error", "Bad request")
|
|
45
|
+
except ValueError:
|
|
46
|
+
error_msg = "Bad request"
|
|
47
|
+
return False, {"error": error_msg}
|
|
48
|
+
|
|
49
|
+
elif response.status_code == 401:
|
|
50
|
+
return False, {
|
|
51
|
+
"error": "Invalid credentials. Please run 'drown login' again."
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
elif response.status_code == 403:
|
|
55
|
+
return False, {
|
|
56
|
+
"error": "Forbidden: You don't have access to this resource."
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
elif response.status_code == 404:
|
|
60
|
+
return False, {
|
|
61
|
+
"error": "Not found. The app or resource doesn't exist."
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
elif response.status_code >= 500:
|
|
65
|
+
return False, {
|
|
66
|
+
"error": "Server error. Please try again later."
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
else:
|
|
70
|
+
return False, {
|
|
71
|
+
"error": f"Unexpected response: {response.status_code}"
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
except requests.exceptions.ConnectionError:
|
|
75
|
+
return False, {
|
|
76
|
+
"error": f"Unable to connect to {api_base}. Please check your internet connection."
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
except requests.exceptions.Timeout:
|
|
80
|
+
return False, {
|
|
81
|
+
"error": "Request timed out. Please try again."
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
except requests.exceptions.RequestException as e:
|
|
85
|
+
return False, {
|
|
86
|
+
"error": f"Network error: {str(e)}"
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def login(username, password):
|
|
91
|
+
"""
|
|
92
|
+
Authenticate with the platform.
|
|
93
|
+
"""
|
|
94
|
+
return _make_request(
|
|
95
|
+
"POST",
|
|
96
|
+
"/api/auth/login",
|
|
97
|
+
data={
|
|
98
|
+
"username": username,
|
|
99
|
+
"password": password
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def get_apps(token):
|
|
105
|
+
"""
|
|
106
|
+
Get list of all apps.
|
|
107
|
+
"""
|
|
108
|
+
return _make_request(
|
|
109
|
+
"GET",
|
|
110
|
+
"/api/apps",
|
|
111
|
+
token=token
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def create_app(token, app_name):
|
|
116
|
+
"""
|
|
117
|
+
Create a new app.
|
|
118
|
+
"""
|
|
119
|
+
return _make_request(
|
|
120
|
+
"POST",
|
|
121
|
+
"/api/apps/create",
|
|
122
|
+
token=token,
|
|
123
|
+
data={
|
|
124
|
+
"name": app_name
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def scale_app(token, app_name, replicas):
|
|
130
|
+
"""
|
|
131
|
+
Scale an app.
|
|
132
|
+
"""
|
|
133
|
+
return _make_request(
|
|
134
|
+
"POST",
|
|
135
|
+
f"/api/apps/{app_name}/scale",
|
|
136
|
+
token=token,
|
|
137
|
+
data={
|
|
138
|
+
"replicas": replicas
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def get_logs(token, app_name):
|
|
144
|
+
"""
|
|
145
|
+
Get logs for an app.
|
|
146
|
+
"""
|
|
147
|
+
return _make_request(
|
|
148
|
+
"GET",
|
|
149
|
+
f"/api/apps/{app_name}/logs",
|
|
150
|
+
token=token
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def link_app(token, app_name):
|
|
155
|
+
"""
|
|
156
|
+
Link to an existing app.
|
|
157
|
+
"""
|
|
158
|
+
return _make_request(
|
|
159
|
+
"POST",
|
|
160
|
+
f"/api/apps/{app_name}/link",
|
|
161
|
+
token=token
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def get_metrics(token, app_name):
|
|
166
|
+
"""
|
|
167
|
+
Get metrics for an app.
|
|
168
|
+
"""
|
|
169
|
+
return _make_request(
|
|
170
|
+
"GET",
|
|
171
|
+
f"/api/apps/{app_name}/metrics",
|
|
172
|
+
token=token
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def register_ssh_key(token, public_key):
|
|
177
|
+
"""
|
|
178
|
+
Register an SSH public key with the platform.
|
|
179
|
+
"""
|
|
180
|
+
return _make_request(
|
|
181
|
+
"POST",
|
|
182
|
+
"/api/keys/register",
|
|
183
|
+
token=token,
|
|
184
|
+
data={
|
|
185
|
+
"public_key": public_key
|
|
186
|
+
}
|
|
187
|
+
)
|
|
@@ -6,11 +6,11 @@ import subprocess
|
|
|
6
6
|
import sys
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
|
-
from drown import api, config
|
|
9
|
+
from drown import api, config, __version__
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
@click.group()
|
|
13
|
-
@click.version_option(version=
|
|
13
|
+
@click.version_option(version=__version__, prog_name="drown")
|
|
14
14
|
def cli():
|
|
15
15
|
"""Drown Platform CLI - Manage your self-hosted PaaS."""
|
|
16
16
|
pass
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="drown",
|
|
8
|
-
version="0.2.
|
|
8
|
+
version="0.2.2",
|
|
9
9
|
author="Drown Platform",
|
|
10
10
|
author_email="platform@dr0wn.duckdns.org",
|
|
11
11
|
description="CLI tool for managing apps on Drown Platform - a self-hosted PaaS",
|
drown-0.2.0/drown/api.py
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
"""API wrapper functions for Drown Platform."""
|
|
2
|
-
|
|
3
|
-
import requests
|
|
4
|
-
from drown.config import get_api_base
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def _make_request(method, endpoint, token=None, data=None):
|
|
8
|
-
"""
|
|
9
|
-
Make an HTTP request to the API.
|
|
10
|
-
|
|
11
|
-
Returns: (success: bool, result: dict)
|
|
12
|
-
"""
|
|
13
|
-
api_base = get_api_base()
|
|
14
|
-
url = f"{api_base}{endpoint}"
|
|
15
|
-
|
|
16
|
-
headers = {}
|
|
17
|
-
if token:
|
|
18
|
-
headers["Authorization"] = f"Bearer {token}"
|
|
19
|
-
|
|
20
|
-
try:
|
|
21
|
-
if method == "GET":
|
|
22
|
-
response = requests.get(url, headers=headers, timeout=10)
|
|
23
|
-
elif method == "POST":
|
|
24
|
-
headers["Content-Type"] = "application/json"
|
|
25
|
-
response = requests.post(url, headers=headers, json=data, timeout=10)
|
|
26
|
-
else:
|
|
27
|
-
return False, {"error": f"Unsupported HTTP method: {method}"}
|
|
28
|
-
|
|
29
|
-
# Handle different status codes
|
|
30
|
-
if response.status_code == 200:
|
|
31
|
-
try:
|
|
32
|
-
return True, response.json()
|
|
33
|
-
except ValueError:
|
|
34
|
-
return True, {"message": response.text}
|
|
35
|
-
elif response.status_code == 201:
|
|
36
|
-
try:
|
|
37
|
-
return True, response.json()
|
|
38
|
-
except ValueError:
|
|
39
|
-
return True, {"message": "Created successfully"}
|
|
40
|
-
elif response.status_code == 401:
|
|
41
|
-
return False, {"error": "Invalid credentials. Please run 'drown login' again."}
|
|
42
|
-
elif response.status_code == 403:
|
|
43
|
-
return False, {"error": "Forbidden: You don't have access to this resource."}
|
|
44
|
-
elif response.status_code == 404:
|
|
45
|
-
return False, {"error": "Not found. The app or resource doesn't exist."}
|
|
46
|
-
elif response.status_code == 400:
|
|
47
|
-
try:
|
|
48
|
-
error_data = response.json()
|
|
49
|
-
error_msg = error_data.get("error", "Bad request")
|
|
50
|
-
except ValueError:
|
|
51
|
-
error_msg = "Bad request"
|
|
52
|
-
return False, {"error": error_msg}
|
|
53
|
-
elif response.status_code >= 500:
|
|
54
|
-
return False, {"error": "Server error. Please try again later."}
|
|
55
|
-
else:
|
|
56
|
-
return False, {"error": f"Unexpected response: {response.status_code}"}
|
|
57
|
-
|
|
58
|
-
except requests.exceptions.ConnectionError:
|
|
59
|
-
return False, {"error": f"Unable to connect to {api_base}. Please check your internet connection."}
|
|
60
|
-
except requests.exceptions.Timeout:
|
|
61
|
-
return False, {"error": "Request timed out. Please try again."}
|
|
62
|
-
except requests.exceptions.RequestException as e:
|
|
63
|
-
return False, {"error": f"Network error: {str(e)}"}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def login(username, password):
|
|
67
|
-
"""
|
|
68
|
-
Authenticate with the platform.
|
|
69
|
-
|
|
70
|
-
Returns: (success: bool, result: dict)
|
|
71
|
-
result contains: {"token": "...", "username": "..."} or {"error": "..."}
|
|
72
|
-
"""
|
|
73
|
-
return _make_request("POST", "/api/auth/login", data={
|
|
74
|
-
"username": username,
|
|
75
|
-
"password": password
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def get_apps(token):
|
|
80
|
-
"""
|
|
81
|
-
Get list of all apps.
|
|
82
|
-
|
|
83
|
-
Returns: (success: bool, result: dict)
|
|
84
|
-
result contains: {"apps": [...]} or {"error": "..."}
|
|
85
|
-
"""
|
|
86
|
-
return _make_request("GET", "/api/apps", token=token)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def create_app(token, app_name):
|
|
90
|
-
"""
|
|
91
|
-
Create a new app.
|
|
92
|
-
|
|
93
|
-
Returns: (success: bool, result: dict)
|
|
94
|
-
result contains: {"app": {...}, "domain": "...", "git_remote": "...", "push_instructions": "..."} or {"error": "..."}
|
|
95
|
-
"""
|
|
96
|
-
return _make_request("POST", "/api/apps/create", token=token, data={
|
|
97
|
-
"name": app_name
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def scale_app(token, app_name, replicas):
|
|
102
|
-
"""
|
|
103
|
-
Scale an app to specified replica count.
|
|
104
|
-
|
|
105
|
-
Returns: (success: bool, result: dict)
|
|
106
|
-
result contains: {"app": {...}, "replicas": ...} or {"error": "..."}
|
|
107
|
-
"""
|
|
108
|
-
return _make_request("POST", f"/api/apps/{app_name}/scale", token=token, data={
|
|
109
|
-
"replicas": replicas
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def get_logs(token, app_name):
|
|
114
|
-
"""
|
|
115
|
-
Get logs for an app.
|
|
116
|
-
|
|
117
|
-
Returns: (success: bool, result: dict)
|
|
118
|
-
result contains: {"app": "...", "logs": "..."} or {"error": "..."}
|
|
119
|
-
"""
|
|
120
|
-
return _make_request("GET", f"/api/apps/{app_name}/logs", token=token)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
def link_app(token, app_name):
|
|
124
|
-
"""
|
|
125
|
-
Link to an existing app (get git remote info).
|
|
126
|
-
|
|
127
|
-
Returns: (success: bool, result: dict)
|
|
128
|
-
result contains: {"app": {...}, "domain": "...", "git_remote": "...", "push_instructions": "..."} or {"error": "..."}
|
|
129
|
-
"""
|
|
130
|
-
return _make_request("POST", f"/api/apps/{app_name}/link", token=token)
|
|
131
|
-
"""
|
|
132
|
-
Get metrics for an app.
|
|
133
|
-
|
|
134
|
-
Returns: (success: bool, result: dict)
|
|
135
|
-
result contains: {"app": "...", "replicas": [...]} or {"error": "..."}
|
|
136
|
-
"""
|
|
137
|
-
return _make_request("GET", f"/api/apps/{app_name}/metrics", token=token)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
def get_metrics(token, app_name):
|
|
141
|
-
"""
|
|
142
|
-
Get metrics for an app.
|
|
143
|
-
|
|
144
|
-
Returns: (success: bool, result: dict)
|
|
145
|
-
result contains: {"app": "...", "replicas": [...]} or {"error": "..."}
|
|
146
|
-
"""
|
|
147
|
-
return _make_request("GET", f"/api/apps/{app_name}/metrics", token=token)
|
|
148
|
-
"""
|
|
149
|
-
Register an SSH public key with the platform.
|
|
150
|
-
|
|
151
|
-
Returns: (success: bool, result: dict)
|
|
152
|
-
result contains: {"message": "..."} or {"error": "..."}
|
|
153
|
-
"""
|
|
154
|
-
return _make_request("POST", "/api/keys/register", token=token, data={
|
|
155
|
-
"public_key": public_key
|
|
156
|
-
})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|