2022-12-08 13:05:19 -03:00

114 lines
2.4 KiB
Go

// Copyright (c) 2021 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License.AGPL.txt in the project root for license information.
package main
import (
"encoding/json"
"os"
"os/exec"
"path/filepath"
"syscall"
"github.com/gitpod-io/gitpod/common-go/log"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"golang.org/x/xerrors"
)
const RETRY = 3
func main() {
log := logrus.New()
log.SetLevel(logrus.DebugLevel)
var (
candidates = []string{"bob-runc", "runc"}
runcPath string
bundle string
err error
)
for _, c := range candidates {
runcPath, err = exec.LookPath(c)
if runcPath != "" {
break
}
}
if err != nil {
log.WithError(err).Fatal("runc not found")
}
var useFacade bool
for i, arg := range os.Args {
if arg == "run" {
useFacade = true
}
if arg == "--bundle" && i+1 < len(os.Args) {
bundle = os.Args[i+1]
}
}
if useFacade && bundle != "" {
err = createAndRunc(runcPath, bundle)
} else {
err = syscall.Exec(runcPath, os.Args, os.Environ())
}
if err != nil {
log.WithError(err).Fatal("failed")
}
}
func createAndRunc(runcPath, bundle string) error {
fn := filepath.Join(bundle, "config.json")
fc, err := os.ReadFile(fn)
if err != nil {
return xerrors.Errorf("cannot read config.json: %w", err)
}
var cfg specs.Spec
err = json.Unmarshal(fc, &cfg)
if err != nil {
return xerrors.Errorf("cannot decode config.json: %w", err)
}
var hasSysMount bool
for _, m := range cfg.Mounts {
if m.Destination == "/sys" {
hasSysMount = true
break
}
}
if !hasSysMount {
cfg.Mounts = append(cfg.Mounts, specs.Mount{
Destination: "/sys",
Type: "sysfs",
Source: "sysfs",
})
}
fc, err = json.Marshal(cfg)
if err != nil {
return xerrors.Errorf("cannot encode config.json: %w", err)
}
for _, fn := range []string{fn, "/tmp/debug.json"} {
err = os.WriteFile(fn, fc, 0644)
if err != nil {
return xerrors.Errorf("cannot encode config.json: %w", err)
}
}
// See here for more details on why retries are necessary.
// https://github.com/gitpod-io/gitpod/issues/12365
for i := 0; i <= RETRY; i++ {
err = syscall.Exec(runcPath, os.Args, os.Environ())
if err == nil {
return err
} else {
log.WithError(err).Warn("runc failed")
}
}
return xerrors.Errorf("exec %s: %w", runcPath, err)
}