Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions init.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ func init() {
logrus.SetFormatter(new(logrus.JSONFormatter))
logrus.Debug("child process in init()")

factory, _ := libcontainer.New("")
if err := factory.StartInitialization(); err != nil {
if err := libcontainer.StartInitialization(); err != nil {
// as the error is sent back to the parent there is no need to log
// or write it to stderr because the parent process will handle this
os.Exit(1)
Expand Down
21 changes: 5 additions & 16 deletions libcontainer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,15 @@ func init() {
if len(os.Args) > 1 && os.Args[1] == "init" {
runtime.GOMAXPROCS(1)
runtime.LockOSThread()
factory, _ := libcontainer.New("")
if err := factory.StartInitialization(); err != nil {
if err := libcontainer.StartInitialization(); err != nil {
logrus.Fatal(err)
}
panic("--this line should have never been executed, congratulations--")
}
}
```

Then to create a container you first have to initialize an instance of a factory
that will handle the creation and initialization for a container.

```go
factory, err := libcontainer.New("/var/lib/container")
if err != nil {
logrus.Fatal(err)
return
}
```

Once you have an instance of the factory created we can create a configuration
Then to create a container you first have to create a configuration
struct describing how the container is to be created. A sample would look similar to this:

```go
Expand Down Expand Up @@ -243,10 +231,11 @@ config := &configs.Config{
}
```

Once you have the configuration populated you can create a container:
Once you have the configuration populated you can create a container
with a specified ID under a specified state directory:

```go
container, err := factory.Create("container-id", config)
container, err := libcontainer.Create("/run/containers", "container-id", config)
if err != nil {
logrus.Fatal(err)
return
Expand Down
8 changes: 4 additions & 4 deletions libcontainer/cgroups/manager/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ func NewWithPaths(config *configs.Cgroup, paths map[string]string) (cgroups.Mana
return fs.NewManager(config, paths)
}

// getUnifiedPath is an implementation detail of libcontainer factory.
// Historically, it saves cgroup paths as per-subsystem path map (as returned
// by cm.GetPaths(""), but with v2 we only have one single unified path
// (with "" as a key).
// getUnifiedPath is an implementation detail of libcontainer.
// Historically, libcontainer.Create saves cgroup paths as per-subsystem path
// map (as returned by cm.GetPaths(""), but with v2 we only have one single
// unified path (with "" as a key).
//
// This function converts from that map to string (using "" as a key),
// and also checks that the map itself is sane.
Expand Down
30 changes: 0 additions & 30 deletions libcontainer/factory.go

This file was deleted.

96 changes: 33 additions & 63 deletions libcontainer/factory_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"strconv"

securejoin "github.com/cyphar/filepath-securejoin"
"github.com/moby/sys/mountinfo"
"golang.org/x/sys/unix"

"github.com/opencontainers/runc/libcontainer/cgroups/manager"
Expand All @@ -28,60 +27,30 @@ const (

var idRegex = regexp.MustCompile(`^[\w+-\.]+$`)

// TmpfsRoot is an option func to mount LinuxFactory.Root to tmpfs.
func TmpfsRoot(l *LinuxFactory) error {
mounted, err := mountinfo.Mounted(l.Root)
if err != nil {
return err
}
if !mounted {
if err := mount("tmpfs", l.Root, "", "tmpfs", 0, ""); err != nil {
return err
}
}
return nil
}

// New returns a linux based container factory based in the root directory and
// configures the factory with the provided option funcs.
func New(root string, options ...func(*LinuxFactory) error) (Factory, error) {
if root != "" {
if err := os.MkdirAll(root, 0o700); err != nil {
return nil, err
}
}
l := &LinuxFactory{
Root: root,
}

for _, opt := range options {
if opt == nil {
continue
}
if err := opt(l); err != nil {
return nil, err
}
}
return l, nil
}

// LinuxFactory implements the default factory interface for linux based systems.
type LinuxFactory struct {
// Root directory for the factory to store state.
Root string
}

func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, error) {
if l.Root == "" {
// Create creates a new container with the given id inside a given state
// directory (root), and returns a Container object.
//
// The root is a state directory which many containers can share. It can be
// used later to get the list of containers, or to get information about a
// particular container (see Load).
//
// The id must not be empty and consist of only the following characters:
// ASCII letters, digits, underscore, plus, minus, period. The id must be
// unique and non-existent for the given root path.
func Create(root, id string, config *configs.Config) (Container, error) {
if root == "" {
return nil, errors.New("root not set")
}
if err := l.validateID(id); err != nil {
if err := validateID(id); err != nil {
return nil, err
}
if err := validate.Validate(config); err != nil {
return nil, err
}
containerRoot, err := securejoin.SecureJoin(l.Root, id)
if err := os.MkdirAll(root, 0o700); err != nil {
return nil, err
}
containerRoot, err := securejoin.SecureJoin(root, id)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -125,7 +94,8 @@ func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, err
return nil, errors.New("container's cgroup unexpectedly frozen")
}

if err := os.MkdirAll(containerRoot, 0o711); err != nil {
// Parent directory is already created above, so Mkdir is enough.
if err := os.Mkdir(containerRoot, 0o711); err != nil {
return nil, err
}
c := &linuxContainer{
Expand All @@ -139,19 +109,22 @@ func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, err
return c, nil
}

func (l *LinuxFactory) Load(id string) (Container, error) {
if l.Root == "" {
// Load takes a path to the state directory (root) and an id of an existing
// container, and returns a Container object reconstructed from the saved
// state. This presents a read only view of the container.
func Load(root, id string) (Container, error) {
if root == "" {
return nil, errors.New("root not set")
}
// when load, we need to check id is valid or not.
if err := l.validateID(id); err != nil {
if err := validateID(id); err != nil {
return nil, err
}
containerRoot, err := securejoin.SecureJoin(l.Root, id)
containerRoot, err := securejoin.SecureJoin(root, id)
if err != nil {
return nil, err
}
state, err := l.loadState(containerRoot)
state, err := loadState(containerRoot)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -181,13 +154,10 @@ func (l *LinuxFactory) Load(id string) (Container, error) {
return c, nil
}

func (l *LinuxFactory) Type() string {
return "libcontainer"
}

// StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state
// This is a low level implementation detail of the reexec and should not be consumed externally
func (l *LinuxFactory) StartInitialization() (err error) {
// StartInitialization loads a container by opening the pipe fd from the parent
// to read the configuration and state. This is a low level implementation
// detail of the reexec and should not be consumed externally.
func StartInitialization() (err error) {
// Get the INITPIPE.
envInitPipe := os.Getenv("_LIBCONTAINER_INITPIPE")
pipefd, err := strconv.Atoi(envInitPipe)
Expand Down Expand Up @@ -269,7 +239,7 @@ func (l *LinuxFactory) StartInitialization() (err error) {
return i.Init()
}

func (l *LinuxFactory) loadState(root string) (*State, error) {
func loadState(root string) (*State, error) {
stateFilePath, err := securejoin.SecureJoin(root, stateFilename)
if err != nil {
return nil, err
Expand All @@ -289,7 +259,7 @@ func (l *LinuxFactory) loadState(root string) (*State, error) {
return state, nil
}

func (l *LinuxFactory) validateID(id string) error {
func validateID(id string) error {
if !idRegex.MatchString(id) || string(os.PathSeparator)+id != utils.CleanPath(string(os.PathSeparator)+id) {
return ErrInvalidID
}
Expand Down
85 changes: 3 additions & 82 deletions libcontainer/factory_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,89 +7,14 @@ import (
"reflect"
"testing"

"github.com/moby/sys/mountinfo"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/utils"
"github.com/opencontainers/runtime-spec/specs-go"

"golang.org/x/sys/unix"
)

func TestFactoryNew(t *testing.T) {
root := t.TempDir()
factory, err := New(root)
if err != nil {
t.Fatal(err)
}
if factory == nil {
t.Fatal("factory should not be nil")
}
lfactory, ok := factory.(*LinuxFactory)
if !ok {
t.Fatal("expected linux factory returned on linux based systems")
}
if lfactory.Root != root {
t.Fatalf("expected factory root to be %q but received %q", root, lfactory.Root)
}

if factory.Type() != "libcontainer" {
t.Fatalf("unexpected factory type: %q, expected %q", factory.Type(), "libcontainer")
}
}

func TestFactoryNewTmpfs(t *testing.T) {
root := t.TempDir()
factory, err := New(root, TmpfsRoot)
if err != nil {
t.Fatal(err)
}
if factory == nil {
t.Fatal("factory should not be nil")
}
lfactory, ok := factory.(*LinuxFactory)
if !ok {
t.Fatal("expected linux factory returned on linux based systems")
}
if lfactory.Root != root {
t.Fatalf("expected factory root to be %q but received %q", root, lfactory.Root)
}

if factory.Type() != "libcontainer" {
t.Fatalf("unexpected factory type: %q, expected %q", factory.Type(), "libcontainer")
}
mounted, err := mountinfo.Mounted(lfactory.Root)
if err != nil {
t.Fatal(err)
}
if !mounted {
t.Fatalf("Factory Root is not mounted")
}
mounts, err := mountinfo.GetMounts(mountinfo.SingleEntryFilter(lfactory.Root))
if err != nil {
t.Fatal(err)
}
if len(mounts) != 1 {
t.Fatalf("Factory Root is not listed in mounts list")
}
m := mounts[0]
if m.FSType != "tmpfs" {
t.Fatalf("FSType of root: %s, expected %s", m.FSType, "tmpfs")
}
if m.Source != "tmpfs" {
t.Fatalf("Source of root: %s, expected %s", m.Source, "tmpfs")
}
err = unix.Unmount(root, unix.MNT_DETACH)
if err != nil {
t.Error("failed to unmount root:", err)
}
}

func TestFactoryLoadNotExists(t *testing.T) {
factory, err := New(t.TempDir())
if err != nil {
t.Fatal(err)
}
_, err = factory.Load("nocontainer")
stateDir := t.TempDir()
_, err := Load(stateDir, "nocontainer")
if err == nil {
t.Fatal("expected nil error loading non-existing container")
}
Expand Down Expand Up @@ -135,11 +60,7 @@ func TestFactoryLoadContainer(t *testing.T) {
if err := marshal(filepath.Join(root, id, stateFilename), expectedState); err != nil {
t.Fatal(err)
}
factory, err := New(root)
if err != nil {
t.Fatal(err)
}
container, err := factory.Load(id)
container, err := Load(root, id)
if err != nil {
t.Fatal(err)
}
Expand Down
7 changes: 3 additions & 4 deletions libcontainer/integration/checkpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,9 @@ func testCheckpoint(t *testing.T, userns bool) {
}

config := newTemplateConfig(t, &tParam{userns: userns})
factory, err := libcontainer.New(t.TempDir())
ok(t, err)
stateDir := t.TempDir()

container, err := factory.Create("test", config)
container, err := libcontainer.Create(stateDir, "test", config)
ok(t, err)
defer destroyContainer(container)

Expand Down Expand Up @@ -143,7 +142,7 @@ func testCheckpoint(t *testing.T, userns bool) {
ok(t, err)

// reload the container
container, err = factory.Load("test")
container, err = libcontainer.Load(stateDir, "test")
ok(t, err)

restoreStdinR, restoreStdinW, err := os.Pipe()
Expand Down
6 changes: 1 addition & 5 deletions libcontainer/integration/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@ func init() {
}
runtime.GOMAXPROCS(1)
runtime.LockOSThread()
factory, err := libcontainer.New("")
if err != nil {
logrus.Fatalf("unable to initialize for container: %s", err)
}
if err := factory.StartInitialization(); err != nil {
if err := libcontainer.StartInitialization(); err != nil {
logrus.Fatal(err)
}
}
Expand Down
Loading