Skip to content

Commit

Permalink
Change API to the proposed API
Browse files Browse the repository at this point in the history
  • Loading branch information
baijum committed Jul 4, 2021
1 parent 966230a commit 751417e
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 162 deletions.
74 changes: 73 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,74 @@
# pyservicebinding
Kubernetes Service Binding Library for Python Applications
![PyPI - Downloads](https://img.shields.io/pypi/dm/pyservicebinding)
![Release](https://img.shields.io/pypi/v/pyservicebinding)
![Supported Python Versions](https://img.shields.io/pypi/pyversions/pyservicebinding)
[![Build](https://github.com/baijum/pyservicebinding/actions/workflows/python-package.yml/badge.svg?branch=main)](https://github.com/baijum/pyservicebinding/actions/workflows/python-package.yml)
> Kubernetes Service Binding Library for Python Applications
This is a Python module to retrieve bindings from a file-system created through
an implementation of [Service Binding Specification for
Kubernetes](https://github.com/k8s-service-bindings/spec).

You can install this package using pip:

```bash
pip install pyservicebinding
```


The `ServiceBinding` object can be instantiated like this:
```python
from pyservicebinding import binding
try:
sb = binding.ServiceBinding()
except binding.ServiceBindingRootMissingError as msg:
# log the error message and retry/exit
print("SERVICE_BINDING_ROOT env var not set")
```

To get bindings for a specific `type`, say `postgres`:

```python
bindings_list = sb.bindings("postgres")
```

To get bindings for a specific `type`, say `mysql`, and `provider`, say `mariadb`:

```python
bindings_list = sb.bindings("mysql", "mariadb")
```

To get all bindings irrespective of the `type` and `provider`:

```python
bindings_list = sb.all_bindings("mariadb")
```

This is the complete API of the module:
```python

class ServiceBindingRootMissingError(Exception):
pass


class ServiceBinding:

def __init__(self):
"""
- raise ServiceBindingRootMissingError if SERVICE_BINDING_ROOT env var not set
"""

def all_bindings(self) -> list[dict[str, str]]:
"""Get all bindings as a list of dictionaries
- return empty list if no bindings found
"""

def bindings(self, _type: str, provider: typing.Optional[str] = None) -> list[dict[str, str]]:
"""Get filtered bindings as a list of dictionaries
- return empty dictionary if no binding found
- filter the result with the given _type input
- if provider input is given, filter bindings using the given type and provider
"""
```
4 changes: 2 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[metadata]
name = pyservicebinding
version = 0.1.1
version = 0.2.0
author = Baiju Muthukadan
author_email = baiju.m.mail@gmail.com
description = Kubernetes Service Binding Library for Python Applications
Expand All @@ -14,7 +14,7 @@ url = https://github.com/baijum/pyservicebinding
project_urls =
Bug Tracker = https://github.com/baijum/pyservicebinding/issues
classifiers =
Programming Language :: Python :: 3
Programming Language :: Python :: 3.9
License :: OSI Approved :: Apache Software License
Operating System :: OS Independent

Expand Down
111 changes: 53 additions & 58 deletions src/pyservicebinding/binding.py
Original file line number Diff line number Diff line change
@@ -1,78 +1,73 @@
import os
import typing

class ServiceBindingRootMissingError(KeyError):
class ServiceBindingRootMissingError(Exception):
pass

class DuplicateEntryError(KeyError):
pass

def all_bindings() -> list[dict[str, str]]:
"""Get all bindings as a list of dictionaries

- return empty list if no bindings found
- raise ServiceBindingRootMissingError if SERVICE_BINDING_ROOT env var not set
"""
class ServiceBinding:

try:
root = os.environ["SERVICE_BINDING_ROOT"]
except KeyError as msg:
raise ServiceBindingRootMissingError(msg)
def __init__(self):
try:
self.root = os.environ["SERVICE_BINDING_ROOT"]
except KeyError as msg:
raise ServiceBindingRootMissingError(msg)

l = []
for dirname in os.listdir(root):
b = {}
for filename in os.listdir(os.path.join(root, dirname)):
b[filename] = open(os.path.join(root, dirname, filename)).read().strip()

l.append(b)
def all_bindings(self) -> list[dict[str, str]]:
"""Get all bindings as a list of dictionaries
return l
- return empty list if no bindings found
"""
root = self.root
l = []
for dirname in os.listdir(root):
b = {}
for filename in os.listdir(os.path.join(root, dirname)):
b[filename] = open(os.path.join(root, dirname, filename)).read().strip()

def get_binding(_type: str, provider: typing.Optional[str] = None) -> dict[str, str]:
"""Get binding as a dictionary for a given type and optional provider
l.append(b)

- return empty dictionary if no binding found
- raise DuplicateEntryError if duplicate entry found
- raise ServiceBindingRootMissingError if SERVICE_BINDING_ROOT env var not set
"""
return l

try:
root = os.environ["SERVICE_BINDING_ROOT"]
except KeyError as msg:
raise ServiceBindingRootMissingError(msg)
def bindings(self, _type: str, provider: typing.Optional[str] = None) -> list[dict[str, str]]:
"""Get filtered bindings as a list of dictionaries
b = {}
dupcheck = []
if provider:
for dirname in os.listdir(root):
typepath = os.path.join(root, dirname, "type")
providerpath = os.path.join(root, dirname, "provider")
if os.path.exists(typepath):
typevalue = open(typepath).read().strip()
if typevalue != _type:
continue
if os.path.exists(providerpath):
providervalue = open(providerpath).read().strip()
if providervalue != provider:
- return empty dictionary if no binding found
- filter the result with the given _type input
- if provider input is given, filter bindings using the given type and provider
"""
root = self.root
l = []
b = {}
if provider:
for dirname in os.listdir(root):
typepath = os.path.join(root, dirname, "type")
providerpath = os.path.join(root, dirname, "provider")
if os.path.exists(typepath):
typevalue = open(typepath).read().strip()
if typevalue != _type:
continue
if os.path.exists(providerpath):
providervalue = open(providerpath).read().strip()
if providervalue != provider:
continue

for filename in os.listdir(os.path.join(root, dirname)):
b[filename] = open(os.path.join(root, dirname, filename)).read().strip()

l.append(b)
else:
for dirname in os.listdir(root):
typepath = os.path.join(root, dirname, "type")
if os.path.exists(typepath):
typevalue = open(typepath).read().strip()
if typevalue != _type:
continue
dupcheck.append(typevalue + ":" + providervalue)

for filename in os.listdir(os.path.join(root, dirname)):
b[filename] = open(os.path.join(root, dirname, filename)).read().strip()
else:
for dirname in os.listdir(root):
typepath = os.path.join(root, dirname, "type")
if os.path.exists(typepath):
typevalue = open(typepath).read().strip()
if typevalue != _type:
continue
dupcheck.append(typevalue)

for filename in os.listdir(os.path.join(root, dirname)):
b[filename] = open(os.path.join(root, dirname, filename)).read().strip()

if len(dupcheck) > 1 and all(x==dupcheck[0] for x in dupcheck):
raise DuplicateEntryError(dupcheck)
l.append(b)

return b
return l
115 changes: 14 additions & 101 deletions src/pyservicebinding/binding_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

from pyservicebinding import binding

def test_get_binding(tmpdir, monkeypatch):

def test_bindings(tmpdir, monkeypatch):
bindings_dir = tmpdir.mkdir("bindings")
sb1 = tmpdir.join("bindings").mkdir("sb1")
_type = sb1.join("type")
Expand All @@ -26,111 +27,21 @@ def test_get_binding(tmpdir, monkeypatch):

monkeypatch.setenv("SERVICE_BINDING_ROOT", str(bindings_dir))

b = binding.get_binding("mysql")
sb = binding.ServiceBinding()
l = sb.bindings("mysql")
b = l[0]
assert b["username"] == "john"
assert b["password"] == "L&ia6W@n7epi18a"
assert b["url"] == "mysql://192.168.94.102:3306/school"

b = binding.get_binding("neo4j")
l = sb.bindings("neo4j")
b = l[0]
assert b["username"] == "jane"
assert b["password"] == "o4%bGt#D8v2i0ja"
assert b["uri"] == "neo4j://192.168.94.103:7687/cr"

def test_get_binding_with_duplicate_entry_error(tmpdir, monkeypatch):
bindings_dir = tmpdir.mkdir("bindings")
sb1 = tmpdir.join("bindings").mkdir("sb1")
_type = sb1.join("type")
_type.write("mysql")
username = sb1.join("username")
username.write("john")

sb2 = tmpdir.join("bindings").mkdir("sb2")
_type = sb2.join("type")
_type.write("mysql")
username = sb2.join("username")
username.write("jane")

monkeypatch.setenv("SERVICE_BINDING_ROOT", str(bindings_dir))

with pytest.raises(binding.DuplicateEntryError):
binding.get_binding("mysql")

def test_get_binding_with_duplicate_entry_error_different_provider(tmpdir, monkeypatch):
bindings_dir = tmpdir.mkdir("bindings")
sb1 = tmpdir.join("bindings").mkdir("sb1")
_type = sb1.join("type")
_type.write("mysql")
username = sb1.join("username")
username.write("john")

sb2 = tmpdir.join("bindings").mkdir("sb2")
_type = sb2.join("type")
_type.write("mysql")
provider = sb2.join("provider")
provider.write("mariadb")
username = sb2.join("username")
username.write("jane")

monkeypatch.setenv("SERVICE_BINDING_ROOT", str(bindings_dir))

with pytest.raises(binding.DuplicateEntryError):
binding.get_binding("mysql")

def test_get_binding_without_duplicate_entry_error_different_provider(tmpdir, monkeypatch):
bindings_dir = tmpdir.mkdir("bindings")
sb1 = tmpdir.join("bindings").mkdir("sb1")
_type = sb1.join("type")
_type.write("mysql")
username = sb1.join("username")
username.write("john")

sb2 = tmpdir.join("bindings").mkdir("sb2")
_type = sb2.join("type")
_type.write("mysql")
provider = sb2.join("provider")
provider.write("mariadb")
username = sb2.join("username")
username.write("jane")

monkeypatch.setenv("SERVICE_BINDING_ROOT", str(bindings_dir))

b = binding.get_binding("mysql", "mariadb")
assert b["username"] == "jane"


def test_get_binding_with_duplicate_entry_error_same_provider(tmpdir, monkeypatch):
bindings_dir = tmpdir.mkdir("bindings")
sb1 = tmpdir.join("bindings").mkdir("sb1")
_type = sb1.join("type")
_type.write("mysql")
provider = sb1.join("provider")
provider.write("mariadb")
username = sb1.join("username")
username.write("john")

sb2 = tmpdir.join("bindings").mkdir("sb2")
_type = sb2.join("type")
_type.write("mysql")
provider = sb2.join("provider")
provider.write("mariadb")
username = sb2.join("username")
username.write("jane")

monkeypatch.setenv("SERVICE_BINDING_ROOT", str(bindings_dir))

with pytest.raises(binding.DuplicateEntryError):
binding.get_binding("mysql")


def test_get_binding_missing_service_binding_root(tmpdir):
sb1 = tmpdir.mkdir("bindings").mkdir("sb1")
_type = sb1.join("type")
_type.write("mysql")
username = sb1.join("username")
username.write("john")

with pytest.raises(binding.ServiceBindingRootMissingError):
binding.get_binding("mysql")
l = sb.bindings("non-existing")
assert len(l) == 0


def test_all_bindings(tmpdir, monkeypatch):
Expand All @@ -157,7 +68,8 @@ def test_all_bindings(tmpdir, monkeypatch):

monkeypatch.setenv("SERVICE_BINDING_ROOT", str(bindings_dir))

l = binding.all_bindings()
sb = binding.ServiceBinding()
l = sb.all_bindings()
assert len(l) == 2
count = 0
for b in l:
Expand All @@ -175,12 +87,13 @@ def test_all_bindings(tmpdir, monkeypatch):

assert len(l) == 2

def test_all_bindings_missing_service_binding_root(tmpdir):

def test_missing_service_binding_root(tmpdir):
sb1 = tmpdir.mkdir("bindings").mkdir("sb1")
_type = sb1.join("type")
_type.write("mysql")
username = sb1.join("username")
username.write("john")

with pytest.raises(binding.ServiceBindingRootMissingError):
binding.all_bindings()
sb = binding.ServiceBinding()

0 comments on commit 751417e

Please sign in to comment.