Skip to content

Commit

Permalink
[plugins/k8s][feat] Make KubernetesService a load balancer (#1638)
Browse files Browse the repository at this point in the history
  • Loading branch information
meln1k committed Jun 6, 2023
1 parent 6933866 commit 769f3db
Show file tree
Hide file tree
Showing 5 changed files with 2,541 additions and 5 deletions.
21 changes: 20 additions & 1 deletion plugins/k8s/resoto_plugin_k8s/resources.py
Expand Up @@ -1044,11 +1044,12 @@ class KubernetesServiceSpec:


@define(eq=False, slots=False)
class KubernetesService(KubernetesResource):
class KubernetesService(KubernetesResource, BaseLoadBalancer):
kind: ClassVar[str] = "kubernetes_service"
mapping: ClassVar[Dict[str, Bender]] = KubernetesResource.mapping | {
"service_status": S("status") >> Bend(KubernetesServiceStatus.mapping),
"service_spec": S("spec") >> Bend(KubernetesServiceSpec.mapping),
"public_ip_address": S("spec", "externalIPs", 0),
}
reference_kinds: ClassVar[ModelReference] = {
"successors": {
Expand All @@ -1061,10 +1062,28 @@ class KubernetesService(KubernetesResource):

def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None:
super().connect_in_graph(builder, source)
resolved_backends = set()

pods = [
((key, val), pod)
for pod in builder.graph.nodes
if isinstance(pod, KubernetesPod)
for key, val in pod.labels.items()
]
pods_by_labels = defaultdict(list)
for (key, val), pod in pods:
pods_by_labels[(key, val)].append(pod)

selector = bend(S("spec", "selector"), source)
if selector:
builder.add_edges_from_selector(self, EdgeType.default, selector, KubernetesPod)

for key, value in selector.items():
for pod in pods_by_labels.get((key, value), []):
resolved_backends.add(pod.name or pod.id)

self.backends = list(resolved_backends)


# endregion

Expand Down
2,509 changes: 2,508 additions & 1 deletion plugins/k8s/test/files/api_v1_services.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion plugins/k8s/test/resources_test.py
Expand Up @@ -105,7 +105,10 @@ def test_Ingress(json_file: Json) -> None:

@pytest.mark.json_file("api_v1_services.json")
def test_service(json_file: Json) -> None:
round_trip(KubernetesService, json_file)
services, _ = round_trip(KubernetesService, json_file)
service = services[0][0]
assert service.backends == []
assert service.public_ip_address == "198.51.100.32"


@pytest.mark.json_file("apis_storage.k8s.io_v1_csidrivers.json")
Expand Down
4 changes: 2 additions & 2 deletions resotolib/resotolib/json_bender.py
Expand Up @@ -93,7 +93,7 @@ class S(Bender):
Retrieve a value from a JSON object under given path.
"""

def __init__(self, *path: str, default: Optional[Any] = None):
def __init__(self, *path: Union[str, int], default: Optional[Any] = None):
if not path:
raise ValueError("No path given")
self._path = path
Expand All @@ -104,7 +104,7 @@ def execute(self, source: Any) -> Any:
for key in self._path:
source = source[key]
return source
except (KeyError, TypeError):
except (KeyError, TypeError, IndexError):
return self._default


Expand Down
7 changes: 7 additions & 0 deletions resotolib/test/json_bender_test.py
Expand Up @@ -80,3 +80,10 @@ def test_map_dict() -> None:
assert bend(MapDict(value_bender=F(lambda x: x + 1)), src) == {"a": 2, "b": 3}
assert bend(MapDict(key_bender=F(lambda x: x + "b")), src) == {"ab": 1, "bb": 2}
assert bend(MapDict(key_bender=F(lambda x: x + "b"), value_bender=F(lambda x: x + 1)), src) == {"ab": 2, "bb": 3}


def test_array_traversal() -> None:
src = [{"a": 1}]

assert bend(S(0, "a"), src) == 1
assert bend(S(42, "a"), src) is None

0 comments on commit 769f3db

Please sign in to comment.