Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/maif/wasm4s
A library to easily run WASM vms from inside your scala project
https://github.com/maif/wasm4s
integration scala wasm
Last synced: about 2 months ago
JSON representation
A library to easily run WASM vms from inside your scala project
- Host: GitHub
- URL: https://github.com/maif/wasm4s
- Owner: MAIF
- License: apache-2.0
- Created: 2023-11-21T09:14:30.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2024-04-11T19:30:51.000Z (9 months ago)
- Last Synced: 2024-04-12T00:21:13.536Z (9 months ago)
- Topics: integration, scala, wasm
- Language: Scala
- Homepage:
- Size: 2.26 MB
- Stars: 6
- Watchers: 6
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: readme.md
- Contributing: .github/CONTRIBUTING.md
- License: LICENSE
- Code of conduct: .github/CODE_OF_CONDUCT.md
- Security: SECURITY.md
Awesome Lists containing this project
README
# wasm4s
A library to easily run WASM vms from inside your scala project. The wasm vms can be pooled and can auto update themselves when needed. The wasm source can be a file, a base64 payload, an http response, and a [wasmo](https://github.com/maif/wasmo) plugin.
# dependency
first declare the dependency to wasm4s in your `build.sbt`
```scala
libraryDependencies += "fr.maif" %% "wasm4s" % "2.0.0" classifier "bundle"
```the dependency is quite big as it embed multiarch build of wasmtime and extism.
## how to use it in you project
Wasm4s is supposed to be used in an environment where you are going to store and retrieve configurations of wasm vms with callable functions. Wasm4s provides a trait called `WasmConfiguration` that you can extend to represent those vm configuration. You need to extend the `WasmConfiguration` trait with your own implementation in order to interact with wasm4s. Wasm4s provides a `BasicWasmConfiguration` implementation, but you can build your own. It doesn't matter if those configuration are just objects in memory or something that is stored durably in a datastore. Wasm4s provides an `InMemoryWasmConfigurationStore` type to store your configuration in memory.
Once you have types that extends `WasmConfiguration`, you'll need to create an integration context class that will provide access to the whole infrastructure needed by wasm4s
for instance, here is the otoroshi integration :
```scala
import io.otoroshi.wasm4s._class OtoroshiWasmIntegrationContext(env: Env) extends WasmIntegrationContext {
implicit val ec = env.otoroshiExecutionContext
implicit val ev = envval logger: Logger = Logger("otoroshi-wasm-integration")
val materializer: Materializer = env.otoroshiMaterializer
val executionContext: ExecutionContext = env.otoroshiExecutionContext
val wasmCacheTtl: Long = env.wasmCacheTtl
val wasmQueueBufferSize: Int = env.wasmQueueBufferSize
val wasmScriptCache: TrieMap[String, CacheableWasmScript] = new TrieMap[String, CacheableWasmScript]()
val wasmExecutor: ExecutionContext = ExecutionContext.fromExecutorService(
Executors.newWorkStealingPool(Math.max(32, (Runtime.getRuntime.availableProcessors * 4) + 1))
)override def url(path: String): WSRequest = env.Ws.url(path)
override def mtlsUrl(path: String, tlsConfig: TlsConfig): WSRequest = {
val cfg = NgTlsConfig.format.reads(tlsConfig.json).get.legacy
env.MtlsWs.url(path, cfg)
}override def wasmManagerSettings: Future[Option[WasmManagerSettings]] = env.datastores.globalConfigDataStore.latest().wasmManagerSettings.vfuture
override def wasmConfig(path: String): Option[WasmConfiguration] = env.proxyState.wasmPlugin(path).map(_.config)
override def wasmConfigs(): Seq[WasmConfiguration] = env.proxyState.allWasmPlugins().map(_.config)
override def hostFunctions(config: WasmConfiguration, pluginId: String): Array[HostFunction[_ <: HostUserData]] = {
HostFunctions.getFunctions(config.asInstanceOf[WasmConfig], pluginId, None)
}
}
```you can create one yourself like :
```scala
val testWasmConfigs: InMemoryWasmConfigurationStore[WasmConfiguration] = InMemoryWasmConfigurationStore(
"basic" -> BasicWasmConfiguration.fromWasiSource(WasmSource(WasmSourceKind.File, "./src/test/resources/basic.wasm")),
"opa" -> BasicWasmConfiguration.fromOpaSource(WasmSource(WasmSourceKind.File, "./src/test/resources/opa.wasm")),
)class FooWasmIntegrationContext(env: Env) extends WasmIntegrationContext {
val system = ActorSystem("foo-wasm")
val materializer: Materializer = Materializer(system)
val executionContext: ExecutionContext = system.dispatcher
val logger: Logger = Logger("foo-wasm")
val wasmCacheTtl: Long = 2000
val wasmQueueBufferSize: Int = 100
val wasmManagerSettings: Future[Option[WasmManagerSettings]] = Future.successful(None)
val wasmScriptCache: TrieMap[String, CacheableWasmScript] = new TrieMap[String, CacheableWasmScript]()
val wasmExecutor: ExecutionContext = ExecutionContext.fromExecutorService(
Executors.newWorkStealingPool(Math.max(32, (Runtime.getRuntime.availableProcessors * 4) + 1))
)
override def url(path: String): WSRequest = ??? // we do not provide http call right now ;)
override def mtlsUrl(path: String, tlsConfig: TlsConfig): WSRequest = ??? // we do not provide http call right now ;)
override def wasmConfig(path: String): Option[WasmConfiguration] = testWasmConfigs.wasmConfiguration(path)
override def wasmConfigs(): Seq[WasmConfiguration] = testWasmConfigs.wasmConfigurations()
override def hostFunctions(config: WasmConfiguration, pluginId: String): Array[HostFunction[_ <: HostUserData]] = Array.empty
}
```you can also use `DefaultWasmIntegrationContext` and `DefaultWasmIntegrationContextWithNoHttpClient` types that provides default settings for your `WasmIntegrationContext`.
then instanciate a wasm integration
```scala
val wasmIntegration = WasmIntegration(new FooWasmIntegrationContext(env))
```now you have to trigger jobs that will cache wasm stuff and clean vm. you can either do it manually or let the integration do it.
```scala
wasmIntegration.start()
Runtime.getRuntime().addShutdownHook(() => {
wasmIntegration.stop()
})
```now you can get a wasm vm through the wasm integration object and use it
```scala
wasmIntegration.withPooledVm(basicConfiguration) { vm =>
vm.callExtismFunction(
"execute",
Json.obj("message" -> "coucou").stringify
).map {
case Left(error) => println(s"error: ${error.prettify}")
case Right(out) => {
assertEquals(out, "{\"input\":{\"message\":\"coucou\"},\"message\":\"yo\"}")
println(s"output: ${out}")
}
}
}
```