Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ac9a9ab

Browse files
committedJul 2, 2024
feat: Knative deployment support
Signed-off-by: Abhishek Kumar <abhishek22512@gmail.com>
1 parent 0b8d777 commit ac9a9ab

File tree

9 files changed

+677
-1
lines changed

9 files changed

+677
-1
lines changed
 

‎config/systems.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,5 +234,46 @@
234234
}
235235
}
236236
}
237+
},
238+
"knative": {
239+
"languages": {
240+
"python": {
241+
"base_images": {
242+
"3.9": "python:3.9-slim",
243+
"3.10": "python:3.10-slim"
244+
},
245+
"images": [
246+
"build",
247+
"run"
248+
],
249+
"username": "docker_user",
250+
"deployment": {
251+
"files": [
252+
"handler.py",
253+
"storage.py"
254+
],
255+
"packages": {
256+
"parliament-functions": "0.1.0"
257+
}
258+
}
259+
},
260+
"nodejs": {
261+
"base_images": {
262+
"latest": "node:latest"
263+
},
264+
"images": [
265+
"build",
266+
"run"
267+
],
268+
"username": "docker_user",
269+
"deployment": {
270+
"files": [
271+
"handler.js",
272+
"storage.js"
273+
],
274+
"packages": []
275+
}
276+
}
277+
}
237278
}
238279
}

‎sebs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def common_params(func):
8888
@click.option(
8989
"--deployment",
9090
default=None,
91-
type=click.Choice(["azure", "aws", "gcp", "local", "openwhisk"]),
91+
type=click.Choice(["azure", "aws", "gcp", "local", "openwhisk", "knative"]),
9292
help="Cloud deployment to use.",
9393
)
9494
@click.option(

‎sebs/faas/config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ def deserialize(config: dict, cache: Cache, handlers: LoggingHandlers) -> Config
204204
from sebs.openwhisk.config import OpenWhiskConfig
205205

206206
implementations["openwhisk"] = OpenWhiskConfig.deserialize
207+
if has_platform("knative"):
208+
from sebs.knative.config import KnativeConfig
209+
210+
implementations["knative"] = KnativeConfig.deserialize
207211
func = implementations.get(name)
208212
assert func, "Unknown config type!"
209213
return func(config[name] if name in config else config, cache, handlers)

‎sebs/knative/config.py

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
from sebs.cache import Cache
2+
from sebs.faas.config import Credentials, Resources, Config
3+
from sebs.utils import LoggingHandlers
4+
from sebs.storage.config import MinioConfig
5+
6+
from typing import cast, Optional
7+
8+
9+
class KnativeResources(Resources):
10+
def __init__(
11+
self,
12+
registry: Optional[str] = None,
13+
username: Optional[str] = None,
14+
password: Optional[str] = None,
15+
registry_updated: bool = False,
16+
):
17+
super().__init__(name="knative")
18+
self._docker_registry = registry if registry != "" else None
19+
self._docker_username = username if username != "" else None
20+
self._docker_password = password if password != "" else None
21+
self._registry_updated = registry_updated
22+
self._storage: Optional[MinioConfig] = None
23+
self._storage_updated = False
24+
25+
@staticmethod
26+
def typename() -> str:
27+
return "Knative.Resources"
28+
29+
@property
30+
def docker_registry(self) -> Optional[str]:
31+
return self._docker_registry
32+
33+
@property
34+
def docker_username(self) -> Optional[str]:
35+
return self._docker_username
36+
37+
@property
38+
def docker_password(self) -> Optional[str]:
39+
return self._docker_password
40+
41+
@property
42+
def storage_config(self) -> Optional[MinioConfig]:
43+
return self._storage
44+
45+
@property
46+
def storage_updated(self) -> bool:
47+
return self._storage_updated
48+
49+
@property
50+
def registry_updated(self) -> bool:
51+
return self._registry_updated
52+
53+
@staticmethod
54+
def initialize(res: Resources, dct: dict):
55+
ret = cast(KnativeResources, res)
56+
ret._docker_registry = dct["registry"]
57+
ret._docker_username = dct["username"]
58+
ret._docker_password = dct["password"]
59+
60+
@staticmethod
61+
def deserialize(config: dict, cache: Cache, handlers: LoggingHandlers) -> Resources:
62+
63+
cached_config = cache.get_config("knative")
64+
ret = KnativeResources()
65+
if cached_config:
66+
super(KnativeResources, KnativeResources).initialize(
67+
ret, cached_config["resources"]
68+
)
69+
70+
# Check for new config - overrides but check if it's different
71+
if "docker_registry" in config:
72+
73+
KnativeResources.initialize(ret, config["docker_registry"])
74+
ret.logging.info("Using user-provided Docker registry for Knative.")
75+
ret.logging_handlers = handlers
76+
77+
# check if there has been an update
78+
if not (
79+
cached_config
80+
and "resources" in cached_config
81+
and "docker" in cached_config["resources"]
82+
and cached_config["resources"]["docker"] == config["docker_registry"]
83+
):
84+
ret._registry_updated = True
85+
86+
# Load cached values
87+
elif (
88+
cached_config
89+
and "resources" in cached_config
90+
and "docker" in cached_config["resources"]
91+
):
92+
KnativeResources.initialize(ret, cached_config["resources"]["docker"])
93+
ret.logging_handlers = handlers
94+
ret.logging.info("Using cached Docker registry for Knative")
95+
else:
96+
ret = KnativeResources()
97+
ret.logging.info("Using default Docker registry for Knative.")
98+
ret.logging_handlers = handlers
99+
ret._registry_updated = True
100+
101+
# Check for new config
102+
if "storage" in config:
103+
ret._storage = MinioConfig.deserialize(config["storage"])
104+
ret.logging.info("Using user-provided configuration of storage for Knative.")
105+
106+
# check if there has been an update
107+
if not (
108+
cached_config
109+
and "resources" in cached_config
110+
and "storage" in cached_config["resources"]
111+
and cached_config["resources"]["storage"] == config["storage"]
112+
):
113+
ret.logging.info(
114+
"User-provided configuration is different from cached storage, "
115+
"we will update existing Knative actions."
116+
)
117+
ret._storage_updated = True
118+
119+
# Load cached values
120+
elif (
121+
cached_config
122+
and "resources" in cached_config
123+
and "storage" in cached_config["resources"]
124+
):
125+
ret._storage = MinioConfig.deserialize(cached_config["resources"]["storage"])
126+
ret.logging.info("Using cached configuration of storage for Knative.")
127+
128+
return ret
129+
130+
def update_cache(self, cache: Cache):
131+
super().update_cache(cache)
132+
cache.update_config(
133+
val=self.docker_registry, keys=["knative", "resources", "docker", "registry"]
134+
)
135+
cache.update_config(
136+
val=self.docker_username, keys=["knative", "resources", "docker", "username"]
137+
)
138+
cache.update_config(
139+
val=self.docker_password, keys=["knative", "resources", "docker", "password"]
140+
)
141+
if self._storage:
142+
self._storage.update_cache(["knative", "resources", "storage"], cache)
143+
144+
def serialize(self) -> dict:
145+
out: dict = {
146+
**super().serialize(),
147+
"docker_registry": self.docker_registry,
148+
"docker_username": self.docker_username,
149+
"docker_password": self.docker_password,
150+
}
151+
if self._storage:
152+
out = {**out, "storage": self._storage.serialize()}
153+
return out
154+
155+
156+
class KnativeConfig(Config):
157+
name: str
158+
cache: Cache
159+
160+
def __init__(self, config: dict, cache: Cache):
161+
super().__init__(name="knative")
162+
self._resources = KnativeResources()
163+
self.knative_exec = config["knativeExec"]
164+
self.cache = cache
165+
166+
@property
167+
def resources(self) -> KnativeResources:
168+
return self._resources
169+
170+
@staticmethod
171+
def initialize(cfg: Config, dct: dict):
172+
cfg._region = dct["region"]
173+
174+
def serialize(self) -> dict:
175+
return {
176+
"name": self._name,
177+
"region": self._region,
178+
"knativeExec": self.knative_exec,
179+
"resources": self._resources.serialize(),
180+
}
181+
182+
@staticmethod
183+
def deserialize(config: dict, cache: Cache, handlers: LoggingHandlers) -> Config:
184+
cached_config = cache.get_config("knative")
185+
resources = cast(
186+
KnativeResources, KnativeResources.deserialize(config, cache, handlers)
187+
)
188+
189+
res = KnativeConfig(config, cached_config)
190+
res.logging_handlers = handlers
191+
res._resources = resources
192+
return res
193+
194+
def update_cache(self, cache: Cache):
195+
cache.update_config(val=self.knative_exec, keys=["knative", "knativeExec"])
196+
self.resources.update_cache(cache)

‎sebs/knative/function.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
from __future__ import annotations
2+
3+
from typing import cast, Optional
4+
from dataclasses import dataclass
5+
6+
from sebs.benchmark import Benchmark
7+
from sebs.faas.function import Function, FunctionConfig, Runtime
8+
from sebs.storage.config import MinioConfig
9+
10+
@dataclass
11+
class KnativeFunctionConfig(FunctionConfig):
12+
docker_image: str = ""
13+
namespace: str = "default"
14+
storage: Optional[MinioConfig] = None
15+
url: str = ""
16+
17+
@staticmethod
18+
def deserialize(data: dict) -> KnativeFunctionConfig:
19+
keys = list(KnativeFunctionConfig.__dataclass_fields__.keys())
20+
data = {k: v for k, v in data.items() if k in keys}
21+
data["runtime"] = Runtime.deserialize(data["runtime"])
22+
data["storage"] = MinioConfig.deserialize(data["storage"])
23+
return KnativeFunctionConfig(**data)
24+
25+
def serialize(self) -> dict:
26+
return self.__dict__
27+
28+
@staticmethod
29+
def from_benchmark(benchmark: Benchmark) -> KnativeFunctionConfig:
30+
return super(KnativeFunctionConfig, KnativeFunctionConfig)._from_benchmark(
31+
benchmark, KnativeFunctionConfig
32+
)
33+
34+
class KnativeFunction(Function):
35+
def __init__(
36+
self, name: str, benchmark: str, code_package_hash: str, cfg: KnativeFunctionConfig
37+
):
38+
super().__init__(benchmark, name, code_package_hash, cfg)
39+
40+
@property
41+
def config(self) -> KnativeFunctionConfig:
42+
return cast(KnativeFunctionConfig, self._cfg)
43+
44+
@staticmethod
45+
def typename() -> str:
46+
return "Knative.Function"
47+
48+
def serialize(self) -> dict:
49+
return {**super().serialize(), "config": self._cfg.serialize()}
50+
51+
@staticmethod
52+
def deserialize(cached_config: dict) -> KnativeFunction:
53+
from sebs.faas.function import Trigger
54+
from sebs.knative.triggers import KnativeLibraryTrigger, KnativeHTTPTrigger
55+
56+
cfg = KnativeFunctionConfig.deserialize(cached_config["config"])
57+
ret = KnativeFunction(
58+
cached_config["name"], cached_config["benchmark"], cached_config["hash"], cfg
59+
)
60+
for trigger in cached_config["triggers"]:
61+
trigger_type = cast(
62+
Trigger,
63+
{"Library": KnativeLibraryTrigger, "HTTP": KnativeHTTPTrigger}.get(trigger["type"]),
64+
)
65+
assert trigger_type, "Unknown trigger type {}".format(trigger["type"])
66+
ret.add_trigger(trigger_type.deserialize(trigger))
67+
return ret

0 commit comments

Comments
 (0)
Please sign in to comment.