forked from awslabs/oci-add-hooks
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
151 lines (136 loc) · 3.84 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package main
import (
"errors"
"fmt"
"os"
"os/exec"
"os/signal"
"path/filepath"
"syscall"
)
const (
// Size of the buffer for catching os.Signal sent to this process
signalBufferSize = 32
exitCodeFailure = 1
)
var (
errUnableToFindRuntime = errors.New("unable to find runtime")
commit string
)
func main() {
// We are doing manual flag parsing b/c the default flag package
// doesn't have the ability to parse only some flags and ignore unknown
// ones. Just requiring positional arguments for simplicity.
// We are expecting command line like one of the following:
// self --version
// self --hook-config-path /path/to/hookcfg --runtime-path /path/to/runc, ... runtime flags
// If we don't match one of these these, we can exit
if len(os.Args) == 2 && os.Args[1] == "--version" {
fmt.Println("commit:", commit)
os.Exit(0)
} else if len(os.Args) < 6 || (os.Args[1] != "--hook-config-path" && os.Args[3] != "--runtime-path") {
os.Exit(exitCodeFailure)
}
// If are args are present, grab the values
hookConfigPath := os.Args[2]
runcPath := os.Args[4]
passthroughArgs := os.Args[5:]
os.Exit(run(hookConfigPath, runcPath, passthroughArgs))
}
func run(hookConfigPath, runcPath string, runcArgs []string) int {
// If required args aren't present, bail
if hookConfigPath == "" || runcPath == "" {
return exitCodeFailure
}
// If a hookConfigPath passed, process the bundle and pass modified
// spec to runc
return processBundle(hookConfigPath, runcPath, runcArgs)
}
func processBundle(hookPath, runcPath string, runcArgs []string) int {
// find the bundle json location
for i, val := range runcArgs {
if val == "--bundle" && i != len(runcArgs)-1 {
// get the bundle Path
bundlePath := runcArgs[i+1]
bundlePath = filepath.Join(bundlePath, "config.json")
// Add the hooks from hookPath to our bundle/config.json
merged, err := addHooks(bundlePath, hookPath)
if err != nil {
return exitCodeFailure
}
err = merged.writeFile(bundlePath)
if err != nil {
return exitCodeFailure
}
break
}
}
// launch runc
path, err := verifyRuntimePath(runcPath)
if err != nil {
return exitCodeFailure
}
return launchRunc(path, runcArgs)
}
func verifyRuntimePath(userDefinedRuncPath string) (string, error) {
info, err := os.Stat(userDefinedRuncPath)
if err == nil && !info.Mode().IsDir() && info.Mode().IsRegular() {
return userDefinedRuncPath, nil
}
return "", errUnableToFindRuntime
}
// Launch runc with the provided args
func launchRunc(runcPath string, runcArgs []string) int {
cmd := prepareCommand(runcPath, runcArgs)
proc := make(chan os.Signal, signalBufferSize)
// Handle signals before we start command to make sure we don't
// miss any related to cmd.
signal.Notify(proc)
err := cmd.Start()
if err != nil {
return exitCodeFailure
}
// Forward signals after we start command
go func() {
for sig := range proc {
cmd.Process.Signal(sig)
}
}()
err = cmd.Wait()
return processRuncError(err)
}
func processRuncError(err error) int {
if err != nil {
if exit, ok := err.(*exec.ExitError); ok {
// We had a nonzero exitCode
if code, ok := exit.Sys().(syscall.WaitStatus); ok {
// and the code is retrievable
// so we exit with the same code
return code.ExitStatus()
}
}
// If we can't get the error code, still exit with error
return exitCodeFailure
}
return 0
}
func prepareCommand(runcPath string, args []string) *exec.Cmd {
cmd := exec.Command(runcPath, args...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd
}
// Add hooks specified inside hookPath to the bundle specified in args
func addHooks(bundlePath, hookPath string) (*config, error) {
specHooks, err := readHooks(bundlePath)
if err != nil {
return nil, err
}
addHooks, err := readHooks(hookPath)
if err != nil {
return nil, err
}
specHooks.merge(addHooks)
return specHooks, nil
}