func NewSpec(rootfs string, cmd string, args ...string) *specs.Spec {
	proc := NewSpecProcess(cmd, args...)
	return &specs.Spec{
		Version: specs.Version,
		Linux: &specs.Linux{
			Namespaces: []specs.LinuxNamespace{
				specs.LinuxNamespace{Type: specs.PIDNamespace},
				specs.LinuxNamespace{Type: specs.MountNamespace},
				specs.LinuxNamespace{Type: specs.IPCNamespace},
				specs.LinuxNamespace{Type: specs.UTSNamespace},
				specs.LinuxNamespace{Type: specs.CgroupNamespace},
				specs.LinuxNamespace{Type: specs.NetworkNamespace},
			},
			Devices: EssentialDevices,
			Resources: &specs.LinuxResources{
				Devices: EssentialDevicesAllow,
			},
		},
		Mounts: []specs.Mount{
			specs.Mount{Destination: "/proc", Source: "proc", Type: "proc",
				Options: []string{"rw", "nosuid", "nodev", "noexec", "relatime"},
			},
			specs.Mount{Destination: "/dev", Source: "tmpfs", Type: "tmpfs",
				Options: []string{"rw", "nosuid", "noexec", "relatime", "dev"},
			},
		},
		Process: proc,
		Root:    &specs.Root{Path: rootfs},
	}
}
NewSpec returns a minimal spec.Spec instance, which is
required to run the given process within a container
using the given rootfs.
NOTE /proc and /dev folders must be present within the given rootfs.
package specki
import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
	"github.com/opencontainers/runtime-spec/specs-go"
)
func UnmapContainerID(id uint32, idmaps []specs.LinuxIDMapping) uint32 {
	for _, idmap := range idmaps {
		if idmap.Size < 1 {
			continue
		}
		maxID := idmap.ContainerID + idmap.Size - 1
		if (id >= idmap.ContainerID) && (id <= maxID) {
			offset := id - idmap.ContainerID
			hostid := idmap.HostID + offset
			return hostid
		}
	}
	return id
}
func RunHooks(ctx context.Context, state *specs.State, hooks []specs.Hook, continueOnError bool) error {
	if len(hooks) == 0 {
		return nil
	}
	stateJSON, err := json.Marshal(state)
	if err != nil {
		return fmt.Errorf("failed to serialize spec state: %w", err)
	}
	for i, h := range hooks {
		fmt.Printf("running hook[%d] path:%s\n", i, h.Path)
		err := RunHook(ctx, stateJSON, h)
		if err != nil {
			fmt.Printf("hook[%d] failed: %s\n", i, err)
			if !continueOnError {
				return err
			}
		}
	}
	return nil
}
func RunHook(ctx context.Context, stateJSON []byte, hook specs.Hook) error {
	if hook.Timeout != nil {
		hookCtx, cancel := context.WithTimeout(ctx, time.Second*time.Duration(*hook.Timeout))
		defer cancel()
		ctx = hookCtx
	}
	cmd := exec.CommandContext(ctx, hook.Path, hook.Args...)
	cmd.Env = hook.Env
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	in, err := cmd.StdinPipe()
	if err != nil {
		return fmt.Errorf("failed to get stdin pipe: %w", err)
	}
	if err := cmd.Start(); err != nil {
		return err
	}
	if _, err := io.Copy(in, bytes.NewReader(stateJSON)); err != nil {
		return err
	}
	in.Close()
	return cmd.Wait()
}
func DecodeJSONFile(filename string, v interface{}) error {
	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	err = json.NewDecoder(f).Decode(v)
	if err != nil {
		f.Close()
		return fmt.Errorf("failed to decode JSON from %s: %w", filename, err)
	}
	err = f.Close()
	if err != nil {
		return fmt.Errorf("failed to close %s: %w", filename, err)
	}
	return nil
}
func EncodeJSONFile(filename string, v interface{}, flags int, perm os.FileMode) error {
	f, err := os.OpenFile(filename, os.O_RDWR|flags, perm)
	if err != nil {
		return err
	}
	enc := json.NewEncoder(f)
	err = enc.Encode(v)
	if err != nil {
		f.Close()
		return fmt.Errorf("failed to encode JSON to %s: %w", filename, err)
	}
	err = f.Close()
	if err != nil {
		return fmt.Errorf("failed to close %s: %w", filename, err)
	}
	err = os.Chmod(filename, perm)
	if err != nil {
		return fmt.Errorf("failed to 'chmod %o %s': %w", perm, filename, err)
	}
	return nil
}
func int64p(v int64) *int64 {
	return &v
}
func modep(m os.FileMode) *os.FileMode {
	return &m
}
var (
	EssentialDevices = []specs.LinuxDevice{
		specs.LinuxDevice{Type: "c", Major: 1, Minor: 3, FileMode: modep(0666), Path: "/dev/null"},
		specs.LinuxDevice{Type: "c", Major: 1, Minor: 5, FileMode: modep(0666), Path: "/dev/zero"},
		specs.LinuxDevice{Type: "c", Major: 1, Minor: 7, FileMode: modep(0666), Path: "/dev/full"},
		specs.LinuxDevice{Type: "c", Major: 1, Minor: 8, FileMode: modep(0666), Path: "/dev/random"},
		specs.LinuxDevice{Type: "c", Major: 1, Minor: 9, FileMode: modep(0666), Path: "/dev/urandom"},
		specs.LinuxDevice{Type: "c", Major: 5, Minor: 0, FileMode: modep(0666), Path: "/dev/tty"},
	}
	EssentialDevicesAllow = []specs.LinuxDeviceCgroup{
		specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: int64p(1), Minor: int64p(3), Access: "rwm"},  
		specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: int64p(1), Minor: int64p(5), Access: "rwm"},  
		specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: int64p(1), Minor: int64p(7), Access: "rwm"},  
		specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: int64p(1), Minor: int64p(8), Access: "rwm"},  
		specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: int64p(1), Minor: int64p(9), Access: "rwm"},  
		specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: int64p(5), Minor: int64p(0), Access: "rwm"},  
		specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: int64p(5), Minor: int64p(2), Access: "rwm"},  
		specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: int64p(88), Access: "rwm"},                   
	}
)
func AllowEssentialDevices(spec *specs.Spec) error {
	for _, dev := range EssentialDevices {
		exist, err := IsDeviceEnabled(spec, dev)
		if err != nil {
			return err
		}
		if !exist {
			spec.Linux.Devices = append(spec.Linux.Devices, dev)
		}
	}
	spec.Linux.Resources.Devices = append(spec.Linux.Resources.Devices, EssentialDevicesAllow...)
	return nil
}
func IsDeviceEnabled(spec *specs.Spec, dev specs.LinuxDevice) (bool, error) {
	for _, d := range spec.Linux.Devices {
		if d.Path == dev.Path {
			if d.Type != dev.Type {
				return false, fmt.Errorf("%s type mismatch (expected %s but was %s)", dev.Path, dev.Type, d.Type)
			}
			if d.Major != dev.Major {
				return false, fmt.Errorf("%s major number mismatch (expected %d but was %d)", dev.Path, dev.Major, d.Major)
			}
			if d.Minor != dev.Minor {
				return false, fmt.Errorf("%s major number mismatch (expected %d but was %d)", dev.Path, dev.Major, d.Major)
			}
			return true, nil
		}
	}
	return false, nil
}
func LoadSpecJSON(p string) (*specs.Spec, error) {
	spec := new(specs.Spec)
	err := DecodeJSONFile(p, spec)
	return spec, err
}
func LoadSpecProcessJSON(src string) (*specs.Process, error) {
	proc := new(specs.Process)
	err := DecodeJSONFile(src, proc)
	return proc, err
}
func LoadSpecStateJSON(filename string) (*specs.State, error) {
	state := new(specs.State)
	err := DecodeJSONFile(filename, state)
	return state, err
}