containerd/go-runc

Go-runc example?

pwFoo opened this issue · 5 comments

pwFoo commented

Hi,

I'm new with Go, so it will be a stupid question... Sorry.

Could you provide a simple example how to run a container with go-runc?

Thanks!

I think go-runc is not so independent project to run container, may it is more suit to work with containerd, just my thought.

I don't know if i'll have time to turn this into docs, but some code I was playing with looks like:

package run

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"

	runc "github.com/containerd/go-runc"
	libpull "github.com/svendowideit/cirri/lib/pull"
	"github.com/svendowideit/cirri/options"

	"github.com/opencontainers/runc/libcontainer/specconv"
	"github.com/opencontainers/runtime-spec/specs-go"
	//"github.com/opencontainers/runtime-spec/specs-go"
)

func Run(image string) error {
	rootfsPath := options.GetImageRootfsDir(image)
	if _, err := os.Stat(rootfsPath); os.IsNotExist(err) {
		// pulls an image from a registry, and unpacks the layers into a rootfs
		if err = libpull.Pull(image); err != nil {
			return err
		}
	}

	// TODO: name should be generated? plus aliases?
	name := image
	bundleDir := filepath.Join(options.LocalCacheDir(), "containers", name)
	if err := os.MkdirAll(bundleDir, 0775); err != nil {
		return err
	}
	if err := os.Chdir(bundleDir); err != nil {
		return err
	}
	configJson := "config.json"
	if _, err := os.Stat(configJson); os.IsNotExist(err) {
		spec := specconv.Example()
		specconv.ToRootless(spec)
		// TODO: should also use the info in the manifest to determine default command/entrypoint etc
		spec.Root.Path = rootfsPath
		//if err := injectPRoot(spec); err != nil {
			//return err
		//}
		//save it..
		data, err := json.MarshalIndent(spec, "", "\t")
		if err != nil {
			return err
		}
		err = ioutil.WriteFile(configJson, data, 0666)
		if err != nil {
			return err
		}
	}

	cfg := &runc.Runc{
		//Command: "/home/sven/go/bin/runc",
		Root:  options.GetRuncDir(),
		Debug: true,
		//Rootless: false,
	}

	io, err := runc.NewSTDIO()
	if err != nil {
		fmt.Printf("ERROR: %s\n", err)
	}

	status, err := cfg.Run(context.Background(), "test", ".", &runc.CreateOpts{
		IO: io,
	})
	if err == nil {
		// exit with the container's exit status so any external supervisor is
		// notified of the exit with the correct exit status.
		fmt.Printf("Status == %d\n", status)
		os.Exit(status)
	}
	fmt.Printf("ERROR: %s", err)

	return nil
}

@estesp ^^^ is the kind of example that I'm talking about - I'm sure that I'm not using the library "in the right way" but the only way consumers of a library will know what was intended, is for those that wrote it to give working examples.

@SvenDowideit Am I understanding correctly that your example would not have a need for an actual runc binary to be installed on a system, or am I seeing this wrong?
I've been trying to use a couple of things in the container world "as a library (package)" and having finally made containerd to work in this way, I noticed that the moment I do not have runc installed I get the following error:

INFO[2021-11-15T00:05:39.074495020+01:00] containerd successfully booted in 0.035576s  
2021/11/15 00:05:44 Successfully pulled docker.io/theapemachine/term:latest image
2021/11/15 00:05:44 start failed: runtime "io.containerd.runc.v2" binary not installed "containerd-shim-runc-v2": file does not exist: unknown

I was writing a tool for a previous employer that would use whatever container tooling was installed - in this example, its using an existing runc binary (as that's what go-run's intent is)

its been a long time since i sighted that experiment - but I think I did end up adding some other runc based code only path - but this isn't it.