Skip to content
Open
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
34 changes: 27 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ passing along to an OCI compatable runtime.

This runtime can be invoked by doing

```
```shell
oci-add-hooks \
--hook-config-path </path/to/hook/config>
--runtime-path </path/to/oci/runtime> \
Expand All @@ -18,12 +18,26 @@ oci-add-hooks \
```
- `hook-config-path` is a json file that follows the format described [here](https://github.com/opencontainers/runtime-spec/blob/master/config.md#posix-platform-hooks).
- `runtime-path` is a path to an OCI runtime binary.
- `bundle`,if present, specifies the path to the bundle directory.
- `bundle`, if present, specifies the path to the bundle directory.

If you want to save the log of this runtime for better debug, this runtime can be invoked by doing

```shell
oci-add-hooks \
--hook-config-path </path/to/hook/config>
--runtime-path </path/to/oci/runtime> \
--log-path </path/to/logpath> \
…\
[--bundle <path/to/bundle> \]
```

- `log-path` is a path to save the log of oci-add-hooks.

### With Docker

A few things need to be done to use `oci-add-hooks` with Docker. First modify
`/etc/docker/daemon.json` to includ a "runtimes" section similiar to the following:
`/etc/docker/daemon.json` to include a "runtimes" section similiar to the following:

```json
{
Expand All @@ -33,13 +47,17 @@ A few things need to be done to use `oci-add-hooks` with Docker. First modify
"runtimeArgs": ["--hook-config-path",
"/path/to/config.json",
"--runtime-path",
"<path/to/oci/runtime>"]
"<path/to/oci/runtime>",
"--log-path",
"<path/to/logpath>"]
}
}
}
```
> note: path here should either include this binaries name when it's on the path
> or the full path/name if it's not.
> note:
>
> - Path here should either include this binaries name when it's on the path or the full path/name if it's not. You can run add_to_bin.sh to add this runtime to make it on the path.
> - "--log-path", "<path/to/logpath>" is optional. And the log file name will be like path/to/logpath/20230911.log


If we had a hypothetical hook config located at `/home/user/hook-config.json`
Expand Down Expand Up @@ -68,7 +86,9 @@ look like:
"runtimeArgs": ["--hook-config-path",
"/home/user/hook-config.json",
"--runtime-path",
"runc"]
"runc",
"--log-path",
"/home/user/log"]
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions add_to_bin.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
cp ./oci-add-hooks /bin/oci-add-hooks
cp ./oci-add-hooks /usr/bin/oci-add-hooks
9 changes: 5 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module github.com/awslabs/oci-add-hooks

require (
github.com/bitly/go-simplejson v0.5.0 // indirect
github.com/joeshaw/json-lossless v0.0.0-20181204200226-e0cd1ca6349b
)
go 1.20

require github.com/joeshaw/json-lossless v0.0.0-20181204200226-e0cd1ca6349b

require github.com/bitly/go-simplejson v0.5.0 // indirect
5 changes: 2 additions & 3 deletions hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package main

import (
"encoding/json"
"io/ioutil"
"os"

lossless "github.com/joeshaw/json-lossless"
Expand Down Expand Up @@ -56,7 +55,7 @@ func (c *config) writeFile(path string) error {
} else {
mode = info.Mode()
}
return ioutil.WriteFile(path, bytes, mode.Perm())
return os.WriteFile(path, bytes, mode.Perm())
}

func (c *config) merge(in *config) {
Expand All @@ -73,7 +72,7 @@ func (c *config) merge(in *config) {
}

func readHooks(path string) (*config, error) {
bytes, err := ioutil.ReadFile(path)
bytes, err := os.ReadFile(path)
if err != nil {
return nil, err
}
Expand Down
83 changes: 82 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,32 @@ package main
import (
"errors"
"fmt"
"log"
"os"
"os/exec"
"os/signal"
"path/filepath"
"syscall"
"time"
)

const (
// Size of the buffer for catching os.Signal sent to this process
signalBufferSize = 32

// For the exit code, I only added createLogFailure,
// I'm not quite sure what numbers are included in runc's ExitStatus,
// I chose 111 for createLogFailure, hopefully it won't overlap with runc's ExitStatus
exitCodeFailure = 1
createLogFailure = 111
)

var (
errUnableToFindRuntime = errors.New("unable to find runtime")

commit string

logMode bool = false
)

func main() {
Expand All @@ -40,15 +49,38 @@ func main() {
hookConfigPath := os.Args[2]
runcPath := os.Args[4]
passthroughArgs := os.Args[5:]
// Check if --log-path flag is provided
if len(passthroughArgs) > 0 && passthroughArgs[0] == "--log-path" {
if len(passthroughArgs) < 2 {
os.Exit(exitCodeFailure)
}
logPath := passthroughArgs[1]
passthroughArgs = passthroughArgs[2:]
if createLogFile(logPath) != 0 {
os.Exit(createLogFailure)
}
logMode = true
}
if logMode {
log.Println("Running oci-add-hooks")
log.Println("Oci-add-hooks arguments right")
}
os.Exit(run(hookConfigPath, runcPath, passthroughArgs))
}

func run(hookConfigPath, runcPath string, runcArgs []string) int {
// If required args aren't present, bail
if hookConfigPath == "" || runcPath == "" {
if logMode {
log.Println("Error: hookConfigPath or runcPath is \"\"")
}
return exitCodeFailure
}

if logMode {
log.Printf("HookconfigPath: %v\n", hookConfigPath)
log.Printf("RuncPath: %v\n", runcPath)
log.Printf("RuncArgs: \n%v\n", runcArgs)
}
// If a hookConfigPath passed, process the bundle and pass modified
// spec to runc
return processBundle(hookConfigPath, runcPath, runcArgs)
Expand All @@ -62,20 +94,35 @@ func processBundle(hookPath, runcPath string, runcArgs []string) int {
bundlePath := runcArgs[i+1]
bundlePath = filepath.Join(bundlePath, "config.json")
// Add the hooks from hookPath to our bundle/config.json
if logMode {
log.Printf("BundleFile: \n%v\n", bundlePath)
}
merged, err := addHooks(bundlePath, hookPath)
if err != nil {
if logMode {
log.Printf("Error: %v\n", err)
}
return exitCodeFailure
}
err = merged.writeFile(bundlePath)
if err != nil {
if logMode {
log.Printf("Error: %v\n", err)
}
return exitCodeFailure
}
if logMode {
log.Println("Add hooks to bundlefile success")
}
break
}
}
// launch runc
path, err := verifyRuntimePath(runcPath)
if err != nil {
if logMode {
log.Printf("Error: runc path is wrong: %v\n", err)
}
return exitCodeFailure
}
return launchRunc(path, runcArgs)
Expand All @@ -98,8 +145,14 @@ func launchRunc(runcPath string, runcArgs []string) int {
signal.Notify(proc)
err := cmd.Start()
if err != nil {
if logMode {
log.Printf("Error: runc start failed: %v\n", err)
}
return exitCodeFailure
}
if logMode {
log.Println("Running runc")
}
// Forward signals after we start command
go func() {
for sig := range proc {
Expand Down Expand Up @@ -140,12 +193,40 @@ func prepareCommand(runcPath string, args []string) *exec.Cmd {
func addHooks(bundlePath, hookPath string) (*config, error) {
specHooks, err := readHooks(bundlePath)
if err != nil {
if logMode {
log.Println("Error: read bundlePath hooks failed")
}
return nil, err
}
addHooks, err := readHooks(hookPath)
if err != nil {
if logMode {
log.Println("Error: read hookPath hooks failed")
}
return nil, err
}
specHooks.merge(addHooks)
return specHooks, nil
}

// Create log file
func createLogFile(logFilePath string) int {
if logFilePath == "" {
return 1
}
if _, err := os.Stat(logFilePath); os.IsNotExist(err) {
err := os.MkdirAll(logFilePath, 0755)
if err != nil {
return 1
}
}
logFileName := time.Now().Format("20060102") + ".log"
logFileName = filepath.Join(logFilePath, logFileName)
logFile, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0755)
if err != nil {
return 1
}
log.SetOutput(logFile)
log.SetFlags(log.Ldate | log.Ltime)
return 0
}
Binary file added oci-add-hooks
Binary file not shown.