Skip to content

Commit 13642f1

Browse files
committed
Initial commit
0 parents  commit 13642f1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+3983
-0
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*.swp
2+
*.py[cod]
3+
4+
dist/
5+
*.egg-info/

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 Opalstack
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
# Opalstack API Library
2+
3+
This is the official Python wrapper for the [Opalstack API](https://my.opalstack.com/api/v1/doc/).
4+
5+
The **opalstack** library is designed to streamline common CRUD *(Create/Read/Update/Delete)* operations. By default, all methods wait for objects to become ready or deleted before returning, allowing you to interact synchronously with a fundamentally asynchronous API.
6+
7+
This library is maintained in tandem with the Opalstack API. This way, your code is able to preserve compatibility with future API changes by simply updating the library instead of having to make additional changes to your code (we hope!).
8+
9+
## Installation
10+
11+
These examples assume that you have an active [Opalstack](https://www.opalstack.com/ "Opalstack") account. If you don't, then give it a try with our 14-day free trial. We think you'll like what you see.
12+
13+
Once logged in, obtain your API token from https://my.opalstack.com/tokens/ .
14+
15+
Then, to install the **opalstack** library using **pypi**:
16+
```bash
17+
pip3 install opalstack
18+
```
19+
20+
This is a pure-python library, so if you just want to try without installing, you can do:
21+
```bash
22+
mkdir -p $HOME/src
23+
cd $HOME/src
24+
git clone 'https://github.com/opalstack/opalstack-python.git'
25+
export PYTHONPATH="$PYTHONPATH:$HOME/src/opalstack-python/src"
26+
python3 -c 'import opalstack'
27+
```
28+
Note that the library does depend on [requests](https://pypi.org/project/requests/ "requests"), so you will need to install that first.
29+
30+
The library is [MIT-licensed](https://github.com/opalstack/opalstack-python/blob/master/LICENSE "MIT-licensed"), so feel free to embed it in your project if needed.
31+
## Examples
32+
33+
#### List web servers
34+
```python
35+
import opalstack
36+
opalapi = opalstack.Api(token='0123456789abcdef0123456789abcdef01234567')
37+
38+
# List all web servers on the account.
39+
#
40+
web_servers = opalapi.servers.list_all()['web_servers']
41+
42+
from pprint import pprint
43+
pprint(web_servers)
44+
45+
46+
# Get UUID of web server on the account with hostname 'opal1.opalstack.com'.
47+
# Be sure to replace 'opal1.opalstack.com' with the hostname of a server you have access to.
48+
#
49+
web_server = [
50+
web_server
51+
for web_server in opalapi.servers.list_all()['web_servers']
52+
if web_server['hostname'] == 'opal1.opalstack.com'
53+
][0]
54+
print(web_server)
55+
56+
# A cleaner way to do this makes use of the filt or filt_one utility functions from opalstack.util
57+
#
58+
# >>> help(filt)
59+
#
60+
# filt(items, keymap, sep='.')
61+
# Filters a list of dicts by given keymap.
62+
# By default, periods represent nesting (configurable by passing `sep`).
63+
#
64+
# For example:
65+
# items = [
66+
# {'name': 'foo', 'server': {'id': 1234, 'hostname': 'host1'}, 'loc': 4},
67+
# {'name': 'bar', 'server': {'id': 2345, 'hostname': 'host2'}, 'loc': 3},
68+
# {'name': 'baz', 'server': {'id': 3456, 'hostname': 'host3'}, 'loc': 4},
69+
# ]
70+
# filt(items, {'loc': 4}) # Returns [foo, baz]
71+
# filt(items, {'loc': 4, 'server.hostname': 'host1'}) # Returns [foo]
72+
# filt(items, {'name': 'bar', 'server.hostname': 'host2'}) # Returns [bar]
73+
# filt(items, {'name': 'bar', 'server.hostname': 'host3'}) # Returns []
74+
#
75+
# filt_one() is like filt(), but returns a single unique result instead of a list.
76+
#
77+
from opalstack.util import filt, filt_one
78+
web_server = filt_one(opalapi.servers.list_all()['web_servers'], {'hostname': 'opal1.opalstack.com'})
79+
print(web_server)
80+
```
81+
82+
#### Create OSUser (Shell User)
83+
```python
84+
import opalstack
85+
from opalstack.util import filt, filt_one
86+
opalapi = opalstack.Api(token='0123456789abcdef0123456789abcdef01234567')
87+
88+
# Choose the server to create on. This example uses 'opal1.opalstack.com'.
89+
# Be sure to replace 'opal1.opalstack.com' with the hostname of a server you have access to.
90+
#
91+
web_server = filt_one(opalapi.servers.list_all()['web_servers'], {'hostname': 'opal1.opalstack.com'})
92+
93+
osusers_to_create = [{
94+
'name': 'mytestuser1234',
95+
'server': web_server['id'],
96+
}]
97+
created_osusers = opalapi.osusers.create(osusers_to_create)
98+
created_osuser = created_osusers[0]
99+
100+
print(created_osuser['id'])
101+
print(created_osuser['name'])
102+
print(created_osuser['default_password'])
103+
```
104+
105+
#### List OSUsers (Shell Users)
106+
```python
107+
import opalstack
108+
from opalstack.util import filt, filt_one
109+
opalapi = opalstack.Api(token='0123456789abcdef0123456789abcdef01234567')
110+
111+
# Get all existing osusers.
112+
#
113+
osusers = opalapi.osusers.list_all()
114+
pprint(osusers)
115+
116+
first_osuser = osusers[0]
117+
pprint(first_osuser['server'])
118+
119+
120+
# Get all existing osusers, but embed the 'server' field with a dict instead of a UUID.
121+
#
122+
osusers = opalapi.osusers.list_all(embed=['server'])
123+
pprint(osusers)
124+
125+
first_osuser = osusers[0]
126+
pprint(first_osuser['server'])
127+
128+
129+
# Retrieve one OSUser by UUID
130+
#
131+
osuser_id = first_osuser['id']
132+
retrieved_osuser = opalapi.osusers.read(osuser_id)
133+
pprint(retrieved_osuser)
134+
135+
136+
# Retrieve one OSUser by UUID, embedding 'server' dict
137+
#
138+
osuser_id = first_osuser['id']
139+
retrieved_osuser = opalapi.osusers.read(osuser_id, embed=['server'])
140+
pprint(retrieved_osuser)
141+
142+
143+
# Get all existing osusers which are on server 'opal1.opalstack.com'.
144+
# Be sure to replace 'opal1.opalstack.com' with the hostname of a server you have access to.
145+
#
146+
osusers = filt(opalapi.osusers.list_all(embed=['server']), {'server.hostname': 'opal1.opalstack.com'})
147+
pprint(osusers)
148+
149+
150+
# Get one osuser on server 'opal1.opalstack.com' named 'mytestuser1234'.
151+
# Be sure to replace 'opal1.opalstack.com' with the hostname of a server you have access to.
152+
# Be sure to replace 'mytestuser1234' with the name of an osuser you have.
153+
#
154+
osuser = filt_one(opalapi.osusers.list_all(embed=['server']), {'server.hostname': 'opal1.opalstack.com', 'name': 'mytestuser1234'})
155+
pprint(osuser)
156+
```
157+
158+
#### Delete OSUsers (Shell Users)
159+
```python
160+
import opalstack
161+
from opalstack.util import one, filt, filt_one
162+
opalapi = opalstack.Api(token='0123456789abcdef0123456789abcdef01234567')
163+
164+
# Delete the osuser on server 'opal1.opalstack.com' named 'mytestuser1234'.
165+
# Be sure to replace 'opal1.opalstack.com' with the hostname of a server you have access to.
166+
# Be sure to replace 'mytestuser1234' with the name of an osuser you want to delete.
167+
#
168+
osuser = filt_one(opalapi.osusers.list_all(embed=['server']), {'server.hostname': 'opal1.opalstack.com', 'name': 'mytestuser1234'})
169+
osusers_to_delete = [osuser]
170+
opalapi.osusers.delete(osusers_to_delete)
171+
```
172+
173+
174+
#### Create, Update, and Delete a Domain, OSUSer, App, and Site
175+
```python
176+
import opalstack
177+
from opalstack.util import filt, filt_one
178+
opalapi = opalstack.Api(token='0123456789abcdef0123456789abcdef01234567')
179+
180+
# Retrieve the "opalstacked" gift domain.
181+
# Be sure to replace 'myusername' with your account username.
182+
#
183+
opalstacked_domain = filt_one(opalapi.domains.list_all(), {'name': 'myusername.opalstacked.com'})
184+
pprint(opalstacked_domain)
185+
186+
187+
# Create a new "mytestdomain" subdomain under the opalstacked gift domain.
188+
# Be sure to replace 'mytestdomain' with the name of a subdomain you want to create.
189+
#
190+
# This also demonstrates the opalstack.util one() function:
191+
#
192+
# That is, this:
193+
# created_domain = one(opalapi.domains.create(domains_to_create))
194+
#
195+
# Is equivalent to:
196+
# created_domains = opalapi.domains.create(domains_to_create)
197+
# assert len(created_domains) == 1
198+
# created_domain = created_domains[0]
199+
#
200+
opalstacked_domain_name = opalstacked_domain['name']
201+
testdomain_name = f'mytestdomain.{opalstacked_domain_name}'
202+
domains_to_create = [{
203+
'name': f'mytestdomain.{opalstacked_domain_name}',
204+
}]
205+
created_domain = one(opalapi.domains.create(domains_to_create))
206+
207+
208+
# Choose the server to create on. This example uses 'opal1.opalstack.com'.
209+
# Be sure to replace 'opal1.opalstack.com' with the hostname of a server you have access to.
210+
#
211+
web_server = filt_one(opalapi.servers.list_all()['web_servers'], {'hostname': 'opal1.opalstack.com'})
212+
213+
214+
# Create a new 'mytestuser2345' osuser on the chosen web server.
215+
# Be sure to replace 'mytestuser2345' with the name of an osuser you want to create.
216+
#
217+
osusers_to_create = [{
218+
'name': 'mytestuser2345',
219+
'server': web_server['id'],
220+
}]
221+
created_osuser = one(opalapi.osusers.create(osusers_to_create))
222+
223+
224+
# Create a new 'mytestwp' Wordpress app under the created osuser.
225+
# Be sure to replace 'mytestwp' with the name of an app you want to create.
226+
#
227+
# The App type represents the underlying type of the application:
228+
# 'STA': Static Only
229+
# 'NPF': Nginx/PHP-FPM
230+
# 'APA': Apache/PHP-CGI
231+
# 'CUS': Proxied port
232+
# 'SLS': Symbolic link, Static only
233+
# 'SLP': Symbolic link, Apache/PHP-CGI
234+
# 'SLN': Symbolic link, Nginx/PHP-FPM
235+
# 'SVN': Subversion
236+
# 'DAV': WebDAV
237+
#
238+
# The 'installer_url' points to an install script,
239+
# usually the raw content of a script somewhere under https://github.com/opalstack/installers.
240+
# The field is optional (omit to create an empty app).
241+
#
242+
apps_to_create = [{
243+
'name': 'mytestwp',
244+
'osuser': created_osuser['id'],
245+
'type': 'APA',
246+
'installer_url': 'https://raw.githubusercontent.com/opalstack/installers/master/core/wordpress/install.sh'
247+
}]
248+
created_app = one(opalapi.apps.create(apps_to_create))
249+
250+
251+
# Create a new 'mytestsite' site to mount the created app onto the created domain.
252+
# Be sure to replace 'mytestsite' with the name of a site you want to create.
253+
#
254+
# In order to create a site, we first need to choose the IP address to use.
255+
# This is because a server may have multiple IPs.
256+
#
257+
# We will use the primary IP address for server 'opal1.opalstacked.com'.
258+
# Be sure to replace 'opal1.opalstack.com' with the hostname of a server you have access to.
259+
#
260+
webserver_primary_ip = filt_one(
261+
opalapi.ips.list_all(embed=['server']), {'server.hostname': 'opal1.opalstack.com', 'primary': True}
262+
)
263+
264+
sites_to_create = [{
265+
'name': 'mytestsite',
266+
'ip4': webserver_primary_ip['id'],
267+
'domains': [created_domain['id']],
268+
'routes': [{'app': created_app['id'], 'uri': '/'}],
269+
}]
270+
created_site = one(opalapi.sites.create(sites_to_create))
271+
272+
273+
# Wait a couple of minutes for everything to take effect.
274+
# Trying too soon could cause an invalid DNS cache, which will take longer to refresh.
275+
#
276+
import time
277+
time.sleep(120.0)
278+
279+
import requests
280+
url = f'http://{created_domain["name"]}/'
281+
resp = requests.get(url)
282+
assert resp.status_code == 200
283+
assert 'wordpress' in str(resp.content).lower()
284+
print(f'Assuming there were no AsserionErrors, your site is now live at {url}')
285+
286+
287+
# Update the created site, renaming it to 'mytestsite2'
288+
#
289+
# Only provided fields are updated. Omitted fields remain as-is.
290+
#
291+
sites_to_update = [{
292+
'id': created_site['id'],
293+
'name': 'mytestsite2',
294+
}]
295+
updated_site = one(opalapi.sites.update(sites_to_update))
296+
297+
298+
# Delete the created site, app, osuser, and domain
299+
#
300+
opalapi.sites.delete([created_site])
301+
opalapi.apps.delete([created_app])
302+
opalapi.osusers.delete([created_osuser])
303+
opalapi.domains.delete([created_domain])
304+
```

0 commit comments

Comments
 (0)