feat: artifact precompile pipeline (MVP)#5125
Conversation
| # -- Wasmtime version the worker links against. Must match the host's | ||
| # wasmtime version exactly — cwasm bytes are version-specific. | ||
| # No default; required when precompile.enabled is true. | ||
| wasmtimeVersion: "" |
There was a problem hiding this comment.
why not infer this from the installed wasmtime version?
| #[arg(long)] | ||
| image: String, | ||
|
|
||
| /// URL where the precompiled .cwasm bytes will be written |
There was a problem hiding this comment.
| /// URL where the precompiled .cwasm bytes will be written | |
| /// URL where the precompiled .cwasm bytes will be written | |
| /// | |
| /// Supported schemes: | |
| /// - file:///path/to/component.cwasm | |
| /// - nats://<bucket>/<key> (requires NATS_URL env var) |
| #[tokio::main] | ||
| async fn main() -> anyhow::Result<()> { | ||
| let args = Args::parse(); | ||
| let output_url = Url::parse(&args.output)?; |
There was a problem hiding this comment.
| let output_url = Url::parse(&args.output)?; | |
| let output_url = Url::parse(&args.output) | |
| .with_context(|| format!("invalid output URL '{}'", args.output))?; |
better to have a descriptive error
| ``` | ||
|
|
||
| For `nats://`, set `NATS_URL`. For pulling from a private registry, set `DOCKER_CONFIG` pointing at a directory containing a `config.json` (the same format `kubectl create secret docker-registry` produces). | ||
|
|
There was a problem hiding this comment.
Could we add a short section clarifying the semantics of nats:// outputs?
Right now it’s not obvious that:
NATS_URLconfigures the NATS server connectionnats://<bucket>/<key>identifies the JetStream object store destination
An example mapping of bucket/key from the URL would probably help too, since the scheme is behaving more like an object-store locator (s3://bucket/key I think) than a direct connection URL.
| operator: | ||
| precompile: | ||
| enabled: true | ||
| wasmtimeVersion: "43" |
There was a problem hiding this comment.
probably should match the host's version 44
| let mut config = wasmtime::Config::new(); | ||
| config.wasm_component_model(true); | ||
| let engine = wasmtime::Engine::new(&config)?; | ||
| let wasm = wat::parse_str("(component)")?; |
There was a problem hiding this comment.
I think we'd prefer using an actual component like the http-hello-world just so we can validate that the rest of the flow executes too.
| } | ||
| } | ||
|
|
||
| #[allow(unsafe_code)] |
There was a problem hiding this comment.
I know the deserialize has been pointed out in the slack threads, so it's more of a call back.
perhaps an upstream modification to wasmtime? Signing/Attestation or a checksum of sorts
| .map_err(|e| anyhow::anyhow!("Error setting up wasmtime engine: {e}"))?; | ||
| let cwasm = engine | ||
| .precompile_component(wasm_bytes) | ||
| .map_err(|e| anyhow::anyhow!("Error precompiling wasm component: {e}"))?; |
There was a problem hiding this comment.
As a compatibility step I think It makes sense to store some additional metadata like a hash https://docs.wasmtime.dev/api/wasmtime/struct.Engine.html#method.precompile_compatibility_hash
| url = %component.precompiled_url, | ||
| "using precompiled component" | ||
| ); | ||
| let bytes = match fetch(&component.precompiled_url).await { |
There was a problem hiding this comment.
Since cwasm bytes are only valid for compatible engine configs, this fetch path should probably return both the bytes and attached metadata (at least a stored precompile_compatibility_hash). Then, before setting is_precompiled: true, we can compare that metadata against the current host engine and probably fall back to pulling/compiling the original wasm when it does not match
See comment in precompiler.rs for the precompile hash context.
ref: https://docs.wasmtime.dev/api/wasmtime/struct.Engine.html#method.precompile_compatibility_hash
Summary
As discussed in #wasmcloud-precompile Slack thread.
Adds an opt-in precompile pipeline so a wasmCloud host can skip compilation at workload start and load a precompiled
.cwasmproduced by a separate worker. The pipeline is disabled unless the operator is started with--precompile-targetand--precompile-wasmtime-version.End-to-end flow
PrecompileReconcilerspawns a KubernetesJobperArtifactJobruns thewash-precompilebinary. This binary pulls the OCI image, compiles it to.cwasmand writes it to a configured store (nats://for production,file://for dev/test).Jobis finished, the operator records the results inArtifact.status.precompiledWorkloadDeploymentReconcilerwaits for the precompilation to be complete and sends thePrecompiledURLvia the workload proto to the wasmCloud Host..cwasmif it was passed asPrecompiledURL. Otherwise the host will use the existing pull + compile path.Open work
Artifact.spec.imagechange.cwasmLooking forward to your thoughts.
Mees