Skip to content

Commit 883aef7

Browse files
committed
libct/init: unify init, fix its error logic
This commit does two things: 1. Consolidate StartInitialization calling logic into Init(). 2. Fix init error handling logic. The main issues at hand are: - the "unable to convert _LIBCONTAINER_INITPIPE" error from StartInitialization is never shown; - errors from WriteSync and WriteJSON are never shown; - the StartInit calling code is triplicated; - using panic is questionable. Generally, our goals are: - if there's any error, do our best to show it; - but only show each error once; - simplify the code, unify init implementations. Signed-off-by: Kir Kolyshkin <[email protected]>
1 parent 789a73d commit 883aef7

File tree

4 files changed

+37
-49
lines changed

4 files changed

+37
-49
lines changed

init.go

+1-10
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package main
22

33
import (
44
"os"
5-
"runtime"
65

76
"github.com/opencontainers/runc/libcontainer"
87
_ "github.com/opencontainers/runc/libcontainer/nsenter"
@@ -12,14 +11,6 @@ func init() {
1211
if len(os.Args) > 1 && os.Args[1] == "init" {
1312
// This is the golang entry point for runc init, executed
1413
// before main() but after libcontainer/nsenter's nsexec().
15-
runtime.GOMAXPROCS(1)
16-
runtime.LockOSThread()
17-
18-
if err := libcontainer.StartInitialization(); err != nil {
19-
// as the error is sent back to the parent there is no need to log
20-
// or write it to stderr because the parent process will handle this
21-
os.Exit(1)
22-
}
23-
panic("libcontainer: container init failed to exec")
14+
libcontainer.Init()
2415
}
2516
}

libcontainer/README.md

+3-16
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,9 @@ function as the entry of "bootstrap".
2323
In addition to the go init function the early stage bootstrap is handled by importing
2424
[nsenter](https://github.com/opencontainers/runc/blob/master/libcontainer/nsenter/README.md).
2525

26-
```go
27-
import (
28-
_ "github.com/opencontainers/runc/libcontainer/nsenter"
29-
)
30-
31-
func init() {
32-
if len(os.Args) > 1 && os.Args[1] == "init" {
33-
runtime.GOMAXPROCS(1)
34-
runtime.LockOSThread()
35-
if err := libcontainer.StartInitialization(); err != nil {
36-
logrus.Fatal(err)
37-
}
38-
panic("--this line should have never been executed, congratulations--")
39-
}
40-
}
41-
```
26+
For details on how runc implements such "init", see
27+
[init.go](https://github.com/opencontainers/runc/blob/master/init.go)
28+
and [libcontainer/init_linux.go](https://github.com/opencontainers/runc/blob/master/libcontainer/init_linux.go).
4229

4330
Then to create a container you first have to create a configuration
4431
struct describing how the container is to be created. A sample would look similar to this:

libcontainer/init_linux.go

+31-8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"io"
99
"net"
1010
"os"
11+
"runtime"
1112
"runtime/debug"
1213
"strconv"
1314
"strings"
@@ -84,10 +85,30 @@ type initConfig struct {
8485
Cgroup2Path string `json:"cgroup2_path,omitempty"`
8586
}
8687

87-
// StartInitialization loads a container by opening the pipe fd from the parent
88-
// to read the configuration and state. This is a low level implementation
89-
// detail of the reexec and should not be consumed externally.
90-
func StartInitialization() (retErr error) {
88+
// Init is part of "runc init" implementation.
89+
func Init() {
90+
runtime.GOMAXPROCS(1)
91+
runtime.LockOSThread()
92+
93+
if err := startInitialization(); err != nil {
94+
// If the error is returned, it was not communicated
95+
// back to the parent (which is not a common case),
96+
// so print it to stderr here as a last resort.
97+
//
98+
// Do not use logrus as we are not sure if it has been
99+
// set up yet, but most important, if the parent is
100+
// alive (and its log forwarding is working).
101+
fmt.Fprintln(os.Stderr, err)
102+
}
103+
// Normally, StartInitialization() never returns, meaning
104+
// if we are here, it had failed.
105+
os.Exit(1)
106+
}
107+
108+
// Normally, this function does not return. If it returns, with or without an
109+
// error, it means the initialization has failed. If the error is returned,
110+
// it means the error can not be communicated back to the parent.
111+
func startInitialization() (retErr error) {
91112
// Get the INITPIPE.
92113
envInitPipe := os.Getenv("_LIBCONTAINER_INITPIPE")
93114
pipefd, err := strconv.Atoi(envInitPipe)
@@ -98,16 +119,18 @@ func StartInitialization() (retErr error) {
98119
defer pipe.Close()
99120

100121
defer func() {
101-
// We have an error during the initialization of the container's init,
102-
// send it back to the parent process in the form of an initError.
122+
// If this defer is ever called, this means initialization has failed.
123+
// Send the error back to the parent process in the form of an initError.
103124
if err := writeSync(pipe, procError); err != nil {
104-
fmt.Fprintln(os.Stderr, retErr)
125+
fmt.Fprintln(os.Stderr, err)
105126
return
106127
}
107128
if err := utils.WriteJSON(pipe, &initError{Message: retErr.Error()}); err != nil {
108-
fmt.Fprintln(os.Stderr, retErr)
129+
fmt.Fprintln(os.Stderr, err)
109130
return
110131
}
132+
// The error is sent, no need to also return it (or it will be reported twice).
133+
retErr = nil
111134
}()
112135

113136
// Set up logging. This is used rarely, and mostly for init debugging.

libcontainer/integration/init_test.go

+2-15
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package integration
22

33
import (
4-
"fmt"
54
"os"
6-
"runtime"
75
"testing"
86

97
"github.com/opencontainers/runc/libcontainer"
@@ -14,20 +12,9 @@ import (
1412

1513
// Same as ../../init.go but for libcontainer/integration.
1614
func init() {
17-
if len(os.Args) < 2 || os.Args[1] != "init" {
18-
return
15+
if len(os.Args) > 1 && os.Args[1] == "init" {
16+
libcontainer.Init()
1917
}
20-
// This is the golang entry point for runc init, executed
21-
// before TestMain() but after libcontainer/nsenter's nsexec().
22-
runtime.GOMAXPROCS(1)
23-
runtime.LockOSThread()
24-
if err := libcontainer.StartInitialization(); err != nil {
25-
// logrus is not initialized
26-
fmt.Fprintln(os.Stderr, err)
27-
}
28-
// Normally, StartInitialization() never returns, meaning
29-
// if we are here, it had failed.
30-
os.Exit(1)
3118
}
3219

3320
func TestMain(m *testing.M) {

0 commit comments

Comments
 (0)