Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: wasi platform build #810

Closed
wants to merge 12 commits into from
66 changes: 66 additions & 0 deletions .github/workflows/wasi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Integration test for wasi
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️❤️❤️


on:
pull_request:
branches: ["main"]

jobs:
wasi:
strategy:
fail-fast: false
matrix:
platform:
- ubuntu-latest
name: wasi
runs-on: ${{ matrix.platform }}
hown3d marked this conversation as resolved.
Show resolved Hide resolved

steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: 1.18
check-latest: true
- name: setup tinygo
shell: bash
env:
TINYGO_VERSION: 0.23.0
run: |
wget https://github.com/tinygo-org/tinygo/releases/download/v"${TINYGO_VERSION}"/tinygo_${TINYGO_VERSION}_amd64.deb
sudo dpkg -i tinygo_${TINYGO_VERSION}_amd64.deb
- name: create crun wasmedge binary
shell: bash
run: |
echo -e "Installing WasmEdge"
if [ -f install.sh ]; then
rm -f install.sh
fi
curl -L -o install.sh -q https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh
sudo chmod a+x install.sh
sudo bash ./install.sh --path="/usr/local"
rm -rf install.sh

echo -e "Building and installing crun"
sudo apt install -y make git gcc build-essential pkgconf libtool libsystemd-dev \
libprotobuf-c-dev libcap-dev libseccomp-dev libyajl-dev \
go-md2man libtool autoconf python3 automake

git clone https://github.com/containers/crun /tmp/crun
cd /tmp/crun
./autogen.sh
./configure --with-wasmedge
make

- name: Build and run ko container
shell: bash
run: |
set -euxo pipefail
export KO_DOCKER_REPO=$(uuidgen |tr "[:upper:]" "[:lower:]")
# Build and run the test/wasi binary, which should print "hello from wasi"
testimg=$(go run ./ build ./test/wasi/ --platform="wasm/wasi")

# run a nested podman container to use annotations (could be removed once docker is able to specify annotations on containers)
docker run --priviliged \
-v /tmp/crun/crun:/usr/bin/crun \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to just run crun directly, instead of having to run it in a container?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's easier to run everything inside a container, because automatically creating OCI bundles is kinda clunky. This approach makes it a bit easier.

quay.io/containers/podman:v4 \
podman run --runtime=/usr/bin/crun $testimg \
| grep "hello from wasi"
150 changes: 150 additions & 0 deletions internal/ociconv/ociconv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// from https://github.com/google/go-containerregistry/blob/53739b507dcc56b7ff29ee17982d6c5179b77aaa/pkg/v1/mutate/oci.go

package ociconv

import (
"fmt"

v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/match"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/tarball"
"github.com/google/go-containerregistry/pkg/v1/types"
)

// Drops docker specific properties
// See: https://github.com/opencontainers/image-spec/blob/main/config.md
func toOCIV1Config(config v1.Config) v1.Config {
return v1.Config{
User: config.User,
ExposedPorts: config.ExposedPorts,
Env: config.Env,
Entrypoint: config.Entrypoint,
Cmd: config.Cmd,
Volumes: config.Volumes,
WorkingDir: config.WorkingDir,
Labels: config.Labels,
StopSignal: config.StopSignal,
}
}

func toOCIV1ConfigFile(cf *v1.ConfigFile) *v1.ConfigFile {
return &v1.ConfigFile{
Created: cf.Created,
Author: cf.Author,
Architecture: cf.Architecture,
OS: cf.OS,
OSVersion: cf.OSVersion,
History: cf.History,
RootFS: cf.RootFS,
Config: toOCIV1Config(cf.Config),
}
}

// OCIImage mutates the provided v1.Image to be OCI compliant v1.Image
// Check image-spec to see which properties are ported and which are dropped.
// https://github.com/opencontainers/image-spec/blob/main/config.md
func OCIImage(base v1.Image) (v1.Image, error) {
// Get original manifest
m, err := base.Manifest()
if err != nil {
return nil, err
}
// Convert config
cfg, err := base.ConfigFile()
if err != nil {
return nil, err
}
cfg = toOCIV1ConfigFile(cfg)

layers, err := base.Layers()
if err != nil {
return nil, err
}

newLayers := []v1.Layer{}

for _, layer := range layers {
mediaType, err := layer.MediaType()
if err != nil {
return nil, err
}
switch mediaType {
case types.DockerLayer:
reader, err := layer.Compressed()
if err != nil {
return nil, fmt.Errorf("getting layer: %w", err)
}
layer, err = tarball.LayerFromReader(reader, tarball.WithMediaType(types.OCILayer))
if err != nil {
return nil, fmt.Errorf("building layer: %w", err)
}
case types.DockerUncompressedLayer:
reader, err := layer.Uncompressed()
if err != nil {
return nil, fmt.Errorf("getting layer: %w", err)
}
layer, err = tarball.LayerFromReader(reader, tarball.WithMediaType(types.OCIUncompressedLayer))
if err != nil {
return nil, fmt.Errorf("building layer: %w", err)
}
}
newLayers = append(newLayers, layer)
}

base, err = mutate.AppendLayers(empty.Image, newLayers...)
if err != nil {
return nil, err
}

base = mutate.MediaType(base, types.OCIManifestSchema1)
base = mutate.ConfigMediaType(base, types.OCIConfigJSON)
base = mutate.Annotations(base, m.Annotations).(v1.Image)
base, err = mutate.ConfigFile(base, cfg)
if err != nil {
return nil, err
}
return base, nil
}

// OCIImageIndex mutates the provided v1.ImageIndex to be OCI compliant v1.ImageIndex
func OCIImageIndex(base v1.ImageIndex) (v1.ImageIndex, error) {
hown3d marked this conversation as resolved.
Show resolved Hide resolved
base = mutate.IndexMediaType(base, types.OCIImageIndex)
mn, err := base.IndexManifest()
if err != nil {
return nil, err
}

removals := []v1.Hash{}
addendums := []mutate.IndexAddendum{}

for _, manifest := range mn.Manifests {
if !manifest.MediaType.IsImage() {
// it is not an image, leave it as is
continue
}
img, err := base.Image(manifest.Digest)
if err != nil {
return nil, err
}
img, err = OCIImage(img)
if err != nil {
return nil, err
}
mt, err := img.MediaType()
if err != nil {
return nil, err
}
removals = append(removals, manifest.Digest)
addendums = append(addendums, mutate.IndexAddendum{Add: img, Descriptor: v1.Descriptor{
URLs: manifest.URLs,
MediaType: mt,
Annotations: manifest.Annotations,
Platform: manifest.Platform,
}})
}
base = mutate.RemoveManifests(base, match.Digests(removals...))
base = mutate.AppendManifests(base, addendums...)
return base, nil
}
Loading