Skip to content

Commit

Permalink
Using wasm-oci to download WASM from OCI registries
Browse files Browse the repository at this point in the history
  • Loading branch information
cardil committed Apr 17, 2023
1 parent 77ce55f commit 9dbea4c
Show file tree
Hide file tree
Showing 15 changed files with 205 additions and 426 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"java.compile.nullAnalysis.mode": "automatic"
}
38 changes: 37 additions & 1 deletion frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"adm-zip": "^0.5.10",
"colorette": "^2.0.19",
"react-scripts": "5.0.1",
"ts-node": "^10.9.1"
"ts-node": "^10.9.1",
"wasm-oci": "^0.1.3"
},
"dependencies": {
"@types/node": "^18.15.0",
Expand Down
161 changes: 124 additions & 37 deletions frontend/scripts/webjar.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,134 @@
import AdmZip from 'adm-zip'
import fs from 'fs'
import { green, yellow } from 'colorette'
import fs from 'fs/promises'
import os from 'os'
import { green, yellow, cyan, magenta, Color, blue, white } from 'colorette'
import { WasmRegistry, Image } from 'wasm-oci'

const coords = {
type Packager = (packer: Packer, log: Logger) => Promise<void>

interface Packer {
/**
* Adds a file from the disk to the archive.
* @param localPath Path to a file on disk.
* @param zipPath Path to a directory in the archive. Defaults to the empty
* string.
* @param zipName Name for the file.
* @param comment Comment to be attached to the file
*/
addLocalFile(localPath: string, zipPath?: string, zipName?: string, comment?: string): void
/**
* Adds a local directory and all its nested files and directories to the
* archive.
* @param localPath Path to a folder on disk.
* @param zipPath Path to a folder in the archive. Default: `""`.
* @param filter RegExp or Function if files match will be included.
*/
addLocalFolder(localPath: string, zipPath?: string, filter?: (path: string) => boolean): void
/**
* Allows you to create a entry (file or directory) in the zip file.
* If you want to create a directory the `entryName` must end in `"/"` and a `null`
* buffer should be provided.
* @param entryName Entry path.
* @param content Content to add to the entry; must be a 0-length buffer
* for a directory.
* @param comment Comment to add to the entry.
* @param attr Attribute to add to the entry.
*/
addFile(entryName: string, content: Buffer, comment?: string, attr?: number): void
}

interface Webjar {
group: string
artifact: string
version: string
color: Color,
build: Packager
}

const webjars: Webjar[] = [{
group: 'com.redhat.openshift.knative.showcase',
artifact: 'frontend',
version: 'main',
color: cyan,
build: async p => {
p.addLocalFolder('./build', 'META-INF/resources', (path) => {
return !path.includes('index.html')
})
p.addLocalFile('./build/index.html', 'META-INF/resources', 'home.html')
}
}, {
group: 'com.redhat.openshift.knative.showcase',
artifact: 'cloudevents-pp-wasm',
version: 'main',
color: magenta,
build: async (p, log) => {
const tmpDir = os.tmpdir()
const tmp = await fs.mkdtemp(`${tmpDir}/wasm-oci-`)
const reg = new WasmRegistry(tmp)
const image = Image.parse('quay.io/cardil/cloudevents-pretty-print@sha256:01b30983dda5eb42a8baefb523eb50d7d0e539fb10d7ab9498a2a59f35036afb')
log(`Pulling image: ${green(image.toString())}`)
const wasm = await reg.pull(image)
p.addFile('META-INF/cloudevents-pretty-print.wasm', await fs.readFile(wasm.file), 'Wasm')
await fs.rm(tmp, { recursive: true })
}
}]

type Logger = (message?: any, ...optionalParams: any[]) => void

function createLogger(name: string): Logger {
return (msg) => {
console.log(`[${name}] ${msg}`)
}
}

const jarDir = `${process.env.HOME}/.m2/repository/` +
`${coords.group.replace(/\./g, '/')}/` +
`${coords.artifact}/` + coords.version
const jarPath = `${jarDir}/${coords.artifact}-${coords.version}.jar`
const pomPath = `${jarDir}/${coords.artifact}-${coords.version}.pom`

const pom = `<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>${coords.group}</groupId>
<artifactId>${coords.artifact}</artifactId>
<version>${coords.version}</version>
<packaging>jar</packaging>
// Build by: forntend/scripts/webjar.ts script
async function buildWebjar(webjar: Webjar) {
const log = createLogger(webjar.color(webjar.artifact))
const jarDir = `${process.env.HOME}/.m2/repository/` +
`${webjar.group.replace(/\./g, '/')}/` +
`${webjar.artifact}/` + webjar.version
const jarPath = `${jarDir}/${webjar.artifact}-${webjar.version}.jar`
const pomPath = `${jarDir}/${webjar.artifact}-${webjar.version}.pom`

const pom = `<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>${webjar.group}</groupId>
<artifactId>${webjar.artifact}</artifactId>
<version>${webjar.version}</version>
<packaging>jar</packaging>
// Build by: forntend/scripts/webjar.ts script
</project>
`
const zip = new AdmZip()
await webjar.build(zip, log)
zip.addFile(`META-INF/maven/${webjar.group}/${webjar.artifact}/pom.xml`, Buffer.from(pom))
zip.writeZip(jarPath)
log(`Created webjar: ${yellow(jarPath)}`)
await fs.writeFile(pomPath, pom)
log(`Created webjar POM: ${yellow(pomPath)}`)
log('To use it, add following to your pom.xml file:\n' + blue(
`
<dependency>
<groupId>${white(webjar.group)}</groupId>
<artifactId>${white(webjar.artifact)}</artifactId>
<version>${white(webjar.version)}</version>
</dependency>
`))
}

async function build() {
const ps : Promise<void>[] = []
for (const webjar of webjars) {
ps.push(buildWebjar(webjar))
}
await Promise.all(ps)
}

const zip = new AdmZip()
zip.addLocalFolder('./build', 'META-INF/resources', (path) => {
return !path.includes('index.html')
})
zip.addLocalFile('./build/index.html', 'META-INF/resources', 'home.html')
zip.addFile(`META-INF/maven/${coords.group}/${coords.artifact}/pom.xml`, Buffer.from(pom))
zip.writeZip(jarPath)
console.log(`Created webjar: ${yellow(jarPath)}`)
fs.writeFileSync(pomPath, pom)
console.log(`Created webjar POM: ${yellow(pomPath)}`)
console.log('\nTo use it, add following to your pom.xml file:\n\n' + green(
`
<dependency>
<groupId>${coords.group}</groupId>
<artifactId>${coords.artifact}</artifactId>
<version>${coords.version}</version>
</dependency>
`))
build()
.catch(e => {
console.error(e)
process.exit(1)
})
.then(() => process.exit(0))
5 changes: 5 additions & 0 deletions quarkus/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@
<artifactId>frontend</artifactId>
<version>main</version>
</dependency>
<dependency>
<groupId>com.redhat.openshift.knative.showcase</groupId>
<artifactId>cloudevents-pp-wasm</artifactId>
<version>main</version>
</dependency>
<dependency>
<groupId>io.github.kawamuray.wasmtime</groupId>
<artifactId>wasmtime-java</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,17 @@
package com.redhat.openshift.knative.showcase.events;

import com.redhat.openshift.oci.registry.WasmDownloader;
import com.redhat.openshift.wasm.c.CString;
import io.cloudevents.CloudEvent;
import io.cloudevents.jackson.JsonFormat;
import io.quarkus.runtime.ShutdownEvent;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

@ApplicationScoped
class Presenter {

private final PrettyPrintWasm wasm;

@Inject
Presenter(WasmDownloader downloader) {
this.wasm = new PrettyPrintWasm(downloader);
}
private final PrettyPrintWasm wasm = new PrettyPrintWasm();

void onStop(@Observes ShutdownEvent ignored) {
wasm.close();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.redhat.openshift.knative.showcase.events;

import com.redhat.openshift.oci.registry.ContainerRegistry;
import com.redhat.openshift.oci.registry.WasmDownloader;
import com.redhat.openshift.wasm.c.CString;
import io.github.kawamuray.wasmtime.Engine;
import io.github.kawamuray.wasmtime.Func;
Expand All @@ -13,19 +11,17 @@
import io.github.kawamuray.wasmtime.wasi.WasiCtx;
import io.github.kawamuray.wasmtime.wasi.WasiCtxBuilder;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;

class PrettyPrintWasm implements AutoCloseable {
public static final String MODULE_NAME = "wasm";
private final WasiCtx wasi;
private final Store<Void> store;
private final Linker linker;
private final Engine engine;
private final WasmDownloader downloader;
private Module module;

PrettyPrintWasm(WasmDownloader downloader) {
this.downloader = downloader;
PrettyPrintWasm() {
this.wasi = new WasiCtxBuilder()
.inheritStdout()
.inheritStderr()
Expand All @@ -38,18 +34,36 @@ class PrettyPrintWasm implements AutoCloseable {
}

CString execute(CString input) {
Path wasm = ensureWasmModuleIsDownloaded();
try (var module = Module.fromFile(engine, wasm.toString())) {
if (!linker.modules(store).contains(MODULE_NAME)) {
linker.module(store, MODULE_NAME, module);
}
try(var mem = linker.get(store, MODULE_NAME, "memory").orElseThrow().memory()) {
return executeOnSharedMemory(input, mem);
}
loadWasmModule();
try(var mem = linker.get(store, MODULE_NAME, "memory").orElseThrow().memory()) {
return executeUsingSharedMemory(input, mem);
}
}

private CString executeOnSharedMemory(CString input, Memory mem) {
private Module loadWasmModule() {
if (module != null) {
return module;
}
byte[] wasm = loadWasmBinary();
module = Module.fromBinary(engine, wasm);
if (!linker.modules(store).contains(MODULE_NAME)) {
linker.module(store, MODULE_NAME, module);
}
return module;
}

private byte[] loadWasmBinary() {
var steam = PrettyPrintWasm.class.getResourceAsStream(
"/META-INF/cloudevents-pretty-print.wasm");
Objects.requireNonNull(steam, "cloudevents-pretty-print.wasm not found");
try {
return steam.readAllBytes();
} catch (Exception e) {
throw new RuntimeException("Failed to load cloudevents-pretty-print.wasm", e);
}
}

private CString executeUsingSharedMemory(CString input, Memory mem) {
var buf = mem.buffer(store);
var offset = 0;
input.writeOn(buf, offset);
Expand All @@ -65,20 +79,11 @@ private CString executeOnSharedMemory(CString input, Memory mem) {
}
}

private Path ensureWasmModuleIsDownloaded() {
var repo = new WasmDownloader.Repository("cardil/cloudevents-pretty-print");
var target = new WasmDownloader.Target(Path.of(
System.getProperty("java.io.tmpdir"), ContainerRegistry.USER_AGENT
));
var wasm = downloader.computeDownloadPath(repo, target);
if (!Files.exists(wasm)) {
downloader.download(repo, wasm);
}
return wasm;
}

@Override
public void close() {
if (module != null) {
module.close();
}
linker.close();
engine.close();
store.close();
Expand Down
Loading

0 comments on commit 9dbea4c

Please sign in to comment.