forked from redhat-developer/odo-init-image
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replace dumb-init with go init (redhat-developer#35)
* Moving to use go-init instead of dumb-init Signed-off-by: Mohammed Zeeshan Ahmed <mohammed.zee1000@gmail.com> * Moving go-init out of vendor and updating Dockerfile accordingly * Commenting out everything that has to do with dumb-init from Dockerfile * Removing dumb-init and any remaining references * Updating image to follow /go/src/github.com/pablo-ruth/go-init
- Loading branch information
1 parent
2db434d
commit 49d40b7
Showing
49 changed files
with
269 additions
and
2,225 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
# go-init | ||
|
||
[](https://travis-ci.org/pablo-ruth/go-init) | ||
|
||
**go-init** is a minimal init system with simple *lifecycle management* heavily inspired by [dumb-init](https://github.com/Yelp/dumb-init). | ||
|
||
It is designed to run as the first process (PID 1) inside a container. | ||
|
||
It is lightweight (less than 500KB after UPX compression) and statically linked so you don't need to install any dependency. | ||
|
||
## Download | ||
|
||
You can download the latest version on [releases page](https://github.com/pablo-ruth/go-init/releases) | ||
|
||
## Why you need an init system | ||
|
||
I can't explain it better than Yelp in *dumb-init* repo, so please [read their explanation](https://github.com/Yelp/dumb-init/blob/v1.2.0/README.md#why-you-need-an-init-system) | ||
|
||
Summary: | ||
- Proper signal forwarding | ||
- Orphaned zombies reaping | ||
|
||
## Why another minimal init system | ||
|
||
In addition to *init* problematic, **go-init** tries to solve another Docker flaw by adding *hooks* on start and stop of the main process. | ||
|
||
If you want to launch a command before the main process of your container and another one after the main process exit, you can't with Docker, see [issue 6982](https://github.com/moby/moby/issues/6982) | ||
|
||
With **go-init** you can do that with "pre" and "post" hooks. | ||
|
||
## Usage | ||
|
||
### one command | ||
|
||
``` | ||
$ go-init -main "my_command param1 param2" | ||
``` | ||
|
||
### pre-start and post-stop hooks | ||
|
||
``` | ||
$ go-init -pre "my_pre_command param1" -main "my_command param1 param2" -post "my_post_command param1" | ||
``` | ||
|
||
## docker | ||
|
||
Example of Dockerfile using *go-init*: | ||
``` | ||
FROM alpine:latest | ||
COPY go-init /bin/go-init | ||
RUN chmod +x /bin/go-init | ||
ENTRYPOINT ["go-init"] | ||
CMD ["-pre", "echo hello world", "-main", "sleep 5", "-post", "echo I finished my sleep bye"] | ||
``` | ||
|
||
Build it: | ||
``` | ||
docker build -t go-init-example | ||
``` | ||
|
||
Run it: | ||
``` | ||
docker run go-init-example | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"flag" | ||
"fmt" | ||
"log" | ||
"os" | ||
"os/exec" | ||
"os/signal" | ||
"strings" | ||
"sync" | ||
"syscall" | ||
"time" | ||
) | ||
|
||
var ( | ||
versionString = "undefined" | ||
) | ||
|
||
func main() { | ||
var preStartCmd string | ||
var mainCmd string | ||
var postStopCmd string | ||
var version bool | ||
|
||
flag.StringVar(&preStartCmd, "pre", "", "Pre-start command") | ||
flag.StringVar(&mainCmd, "main", "", "Main command") | ||
flag.StringVar(&postStopCmd, "post", "", "Post-stop command") | ||
flag.BoolVar(&version, "version", false, "Display go-init version") | ||
flag.Parse() | ||
|
||
if version { | ||
fmt.Println(versionString) | ||
os.Exit(0) | ||
} | ||
|
||
if mainCmd == "" { | ||
log.Fatal("[go-init] No main command defined, exiting") | ||
} | ||
|
||
// Routine to reap zombies (it's the job of init) | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
var wg sync.WaitGroup | ||
wg.Add(1) | ||
go removeZombies(ctx, &wg) | ||
|
||
// Launch pre-start command | ||
if preStartCmd == "" { | ||
log.Println("[go-init] No pre-start command defined, skip") | ||
} else { | ||
log.Printf("[go-init] Pre-start command launched : %s\n", preStartCmd) | ||
err := run(preStartCmd) | ||
if err != nil { | ||
log.Println("[go-init] Pre-start command failed") | ||
log.Printf("[go-init] %s\n", err) | ||
cleanQuit(cancel, &wg, 1) | ||
} else { | ||
log.Printf("[go-init] Pre-start command exited") | ||
} | ||
} | ||
|
||
// Launch main command | ||
var mainRC int | ||
log.Printf("[go-init] Main command launched : %s\n", mainCmd) | ||
err := run(mainCmd) | ||
if err != nil { | ||
log.Println("[go-init] Main command failed") | ||
log.Printf("[go-init] %s\n", err) | ||
mainRC = 1 | ||
} else { | ||
log.Printf("[go-init] Main command exited") | ||
} | ||
|
||
// Launch post-stop command | ||
if postStopCmd == "" { | ||
log.Println("[go-init] No post-stop command defined, skip") | ||
} else { | ||
log.Printf("[go-init] Post-stop command launched : %s\n", postStopCmd) | ||
err := run(postStopCmd) | ||
if err != nil { | ||
log.Println("[go-init] Post-stop command failed") | ||
log.Printf("[go-init] %s\n", err) | ||
cleanQuit(cancel, &wg, 1) | ||
} else { | ||
log.Printf("[go-init] Post-stop command exited") | ||
} | ||
} | ||
|
||
// Wait removeZombies goroutine | ||
cleanQuit(cancel, &wg, mainRC) | ||
} | ||
|
||
func removeZombies(ctx context.Context, wg *sync.WaitGroup) { | ||
for { | ||
var status syscall.WaitStatus | ||
|
||
// Wait for orphaned zombie process | ||
pid, _ := syscall.Wait4(-1, &status, syscall.WNOHANG, nil) | ||
|
||
if pid <= 0 { | ||
// PID is 0 or -1 if no child waiting | ||
// so we wait for 1 second for next check | ||
time.Sleep(1 * time.Second) | ||
} else { | ||
// PID is > 0 if a child was reaped | ||
// we immediately check if another one | ||
// is waiting | ||
continue | ||
} | ||
|
||
// Non-blocking test | ||
// if context is done | ||
select { | ||
case <-ctx.Done(): | ||
// Context is done | ||
// so we stop goroutine | ||
wg.Done() | ||
return | ||
default: | ||
} | ||
} | ||
} | ||
|
||
func run(command string) error { | ||
|
||
var commandStr string | ||
var argsSlice []string | ||
|
||
// Split cmd and args | ||
commandSlice := strings.Fields(command) | ||
commandStr = commandSlice[0] | ||
// if there is args | ||
if len(commandSlice) > 1 { | ||
argsSlice = commandSlice[1:] | ||
} | ||
|
||
// Register chan to receive system signals | ||
sigs := make(chan os.Signal, 1) | ||
defer close(sigs) | ||
signal.Notify(sigs) | ||
defer signal.Reset() | ||
|
||
// Define command and rebind | ||
// stdout and stdin | ||
cmd := exec.Command(commandStr, argsSlice...) | ||
cmd.Stdout = os.Stdout | ||
cmd.Stderr = os.Stderr | ||
// Create a dedicated pidgroup | ||
// used to forward signals to | ||
// main process and all children | ||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} | ||
|
||
// Goroutine for signals forwarding | ||
go func() { | ||
for sig := range sigs { | ||
// Ignore SIGCHLD signals since | ||
// thez are only usefull for go-init | ||
if sig != syscall.SIGCHLD { | ||
// Forward signal to main process and all children | ||
syscall.Kill(-cmd.Process.Pid, sig.(syscall.Signal)) | ||
} | ||
} | ||
}() | ||
|
||
// Start defined command | ||
err := cmd.Start() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Wait for command to exit | ||
err = cmd.Wait() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func cleanQuit(cancel context.CancelFunc, wg *sync.WaitGroup, code int) { | ||
// Signal zombie goroutine to stop | ||
// and wait for it to release waitgroup | ||
cancel() | ||
wg.Wait() | ||
|
||
os.Exit(code) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.