Skip to content

Commit 59831f9

Browse files
authored
Merge pull request #12 from openziti/ziti-hosting
Ziti hosting
2 parents 134f247 + 5c4d947 commit 59831f9

File tree

12 files changed

+320
-49
lines changed

12 files changed

+320
-49
lines changed

sample/README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ OpenZiti Python SDK in Action
1010

1111
- get yourself a Ziti identity from [ZEDS](https://zeds.openziti.org)
1212

13-
follow enrollment instructions from the site.
14-
these instructions assume that Ziti identity is stored in `id.json` file
15-
16-
_Enrollment with Python is coming soon_
13+
follow enrollment instructions from the site, or better yet enroll with openziti Python module
14+
```bash
15+
$ python -m openziti enroll --jwt=<enrollment token file> --identity=<identity file>
16+
```
17+
18+
the following instructions assume that Ziti identity is stored in `id.json` file
1719

1820

1921
- set `ZITI_IDENTITIES` environment variable to location of `id.json` file

sample/flask-of-ziti/helloFlazk.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Copyright (c) NetFoundry Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from flask import Flask
16+
import openziti
17+
import sys
18+
19+
app = Flask(__name__)
20+
bind_opts = {} # populated in main
21+
22+
23+
@openziti.zitify(bindings={
24+
('1.2.3.4', '18080'): bind_opts
25+
})
26+
def runApp():
27+
from waitress import serve
28+
serve(app,host='1.2.3.4',port=18080)
29+
30+
31+
@app.route('/')
32+
def hello_world(): # put application's code here
33+
return 'Have some Ziti!'
34+
35+
36+
if __name__ == '__main__':
37+
bind_opts['ztx'] = sys.argv[1]
38+
bind_opts['service'] = sys.argv[2]
39+
runApp()

sample/flask-of-ziti/requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
flask
2+
waitress
3+
openziti

sample/http-get.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import sys
2+
3+
import urllib3
4+
import openziti
5+
6+
# to run this sample
7+
# set ZITI_IDENTITIES environment variable to location of your Ziti identity file
8+
#
9+
# python http-get.py <url>
10+
# url should be the intercept address of a ziti service
11+
if __name__ == '__main__':
12+
openziti.monkeypatch()
13+
http = urllib3.PoolManager()
14+
r = http.request('GET', sys.argv[1])
15+
print("{0} {1}".format(r.status, r.reason))
16+
print(r.data.decode('utf-8'))
17+

sample/ziti-echo-server.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Copyright (c) NetFoundry Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import sys
16+
import openziti
17+
18+
19+
def run(ziti_id, service):
20+
ztx = openziti.load(ziti_id)
21+
server = ztx.bind(service)
22+
server.listen()
23+
24+
while True:
25+
conn, peer = server.accept()
26+
print(f'processing incoming client[{peer}]')
27+
with conn:
28+
count = 0
29+
while True:
30+
data = conn.recv(1024)
31+
if not data:
32+
print(f'client finished after sending {count} bytes')
33+
break
34+
count += len(data)
35+
conn.sendall(data)
36+
37+
38+
if __name__ == '__main__':
39+
run(sys.argv[1], sys.argv[2])

sample/ziti-http-server.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Copyright (c) NetFoundry Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
import sys
15+
from http.server import BaseHTTPRequestHandler, HTTPServer
16+
import time
17+
18+
import openziti
19+
20+
hostName = "localhost"
21+
serverPort = 8080
22+
23+
cfg = dict(
24+
ztx=sys.argv[1],
25+
service=sys.argv[2]
26+
)
27+
openziti.monkeypatch(bindings={(hostName, serverPort): cfg})
28+
29+
30+
class MyServer(BaseHTTPRequestHandler):
31+
def do_GET(self):
32+
self.send_response(200)
33+
self.send_header("Content-type", "application/json")
34+
self.end_headers()
35+
msg = """{"msg": "Help! I was zitified!"}"""
36+
self.wfile.write(bytes(msg, "utf-8"))
37+
38+
39+
if __name__ == "__main__":
40+
webServer = HTTPServer((hostName, serverPort), MyServer)
41+
print("Server started http://%s:%s" % (hostName, serverPort))
42+
43+
try:
44+
webServer.serve_forever(poll_interval=600)
45+
except KeyboardInterrupt:
46+
pass
47+
48+
webServer.server_close()
49+
print("Server stopped.")

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,4 @@ tag_prefix = v
3838
parentdir_prefix = openziti-
3939

4040
[openziti]
41-
ziti_sdk_version = 0.28.4
41+
ziti_sdk_version = 0.28.7

src/openziti/__init__.py

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,14 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
import socket as sock
1615
from os import getenv
1716

18-
from . import _version, context, zitilib, zitisock
17+
from . import _version, context, zitilib, zitisock, decor
1918

2019
_ziti_identities = filter(lambda p: p != '',
2120
map(lambda s: s.strip(),
2221
(getenv('ZITI_IDENTITIES') or "").split(';')))
2322

24-
_id_map = {}
25-
26-
zitilib.init()
27-
2823
enroll = zitilib.enroll
2924
version = zitilib.version
3025
shutdown = zitilib.shutdown
@@ -35,30 +30,8 @@
3530
if identity != '':
3631
load(identity)
3732

38-
_patch_methods = {
39-
"create_connection": zitisock.create_ziti_connection,
40-
"getaddrinfo": zitisock.ziti_getaddrinfo
41-
}
42-
43-
44-
class MonkeyPatch():
45-
def __init__(self):
46-
self.orig_socket = sock.socket
47-
sock.socket = zitisock.ZitiSocket
48-
self.orig_methods = {m: sock.__dict__[m] for m, _ in
49-
_patch_methods.items()}
50-
for m_name, _ in _patch_methods.items():
51-
sock.__dict__[m_name] = _patch_methods[m_name]
52-
53-
def __enter__(self):
54-
return self
55-
56-
def __exit__(self, exc_type, exc_val, exc_tb):
57-
for m_name, _ in self.orig_methods.items():
58-
sock.__dict__[m_name] = self.orig_methods[m_name]
59-
60-
61-
monkeypatch = MonkeyPatch # pylint: disable=invalid-name
33+
monkeypatch = decor.MonkeyPatch # pylint: disable=invalid-name
34+
zitify = decor.zitify
6235

6336
__version__ = _version.get_versions()['version']
6437
del _version

src/openziti/context.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,42 @@
1414

1515
import socket
1616

17-
from . import zitilib
17+
from . import zitilib, zitisock
1818

1919

2020
class ZitiContext:
2121
# pylint: disable=too-few-public-methods
22-
def __init__(self, ctx_p):
23-
self._ctx = ctx_p
22+
def __init__(self, ctx):
23+
ztx = ctx
24+
if isinstance(ctx, str):
25+
ztx = zitilib.load(ctx)
26+
self._ctx = ztx
2427

2528
def connect(self, addr):
2629
# pylint: disable=invalid-name
2730
fd = zitilib.ziti_socket(socket.SOCK_STREAM)
28-
service = bytes(addr, encoding="utf-8")
29-
zitilib.ziti_connect(fd, self._ctx, service)
31+
if addr is str:
32+
zitilib.connect(fd, self._ctx, addr)
33+
elif addr is tuple:
34+
zitilib.connect_addr(fd, addr)
35+
else:
36+
raise RuntimeError(f'unsupported address {addr}')
3037
return socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0, fd)
3138

39+
def bind(self, service, sock=None):
40+
if sock is None:
41+
sock = zitisock.ZitiSocket(type=socket.SOCK_STREAM)
42+
zitilib.bind(sock.fileno(), self._ctx, service)
43+
return sock
44+
3245

3346
def load_identity(path) -> ZitiContext:
3447
return ZitiContext(zitilib.load(path))
48+
49+
50+
def get_context(ztx) -> ZitiContext:
51+
if isinstance(ztx, ZitiContext):
52+
return ztx
53+
if isinstance(ztx, str):
54+
return ZitiContext(ztx)
55+
raise RuntimeError(f'{ztx} is not a Ziti Context or a path')

src/openziti/decor.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Copyright (c) NetFoundry Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
import socket as sock
15+
from . import zitisock
16+
17+
18+
_patch_methods = {
19+
"create_connection": zitisock.create_ziti_connection,
20+
"getaddrinfo": zitisock.ziti_getaddrinfo
21+
}
22+
23+
24+
def _patchedSocket(patch_opts):
25+
class patchedSocket(zitisock.ZitiSocket):
26+
def __init__(self, *args, **kwargs):
27+
super().__init__(*args, **dict(kwargs, opts=patch_opts))
28+
return patchedSocket
29+
30+
31+
class MonkeyPatch():
32+
def __init__(self, **kwargs):
33+
self.orig_socket = sock.socket
34+
sock.socket = _patchedSocket(kwargs)
35+
self.orig_methods = {m: sock.__dict__[m] for m, _ in
36+
_patch_methods.items()}
37+
for m_name, _ in _patch_methods.items():
38+
sock.__dict__[m_name] = _patch_methods[m_name]
39+
40+
def __enter__(self):
41+
return self
42+
43+
def __exit__(self, exc_type, exc_val, exc_tb):
44+
for m_name, _ in self.orig_methods.items():
45+
sock.__dict__[m_name] = self.orig_methods[m_name]
46+
47+
48+
49+
50+
def zitify(**zkwargs):
51+
def zitify_func(func):
52+
def zitified(*args, **kwargs):
53+
with MonkeyPatch(**zkwargs):
54+
func(*args, **kwargs)
55+
return zitified
56+
return zitify_func

0 commit comments

Comments
 (0)