Path: blob/main/components/docker-up/runc-facade/main.go
2496 views
// Copyright (c) 2020 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package main56import (7"encoding/json"8"os"9"os/exec"10"syscall"11"time"1213"github.com/opencontainers/runtime-spec/specs-go"14"github.com/sirupsen/logrus"15"golang.org/x/xerrors"16)1718const RETRY = 101920var (21defaultOOMScoreAdj = 100022)2324func main() {25log := logrus.New()26log.SetLevel(logrus.DebugLevel)2728var err error29runcPath, err := exec.LookPath("runc")30if err != nil {31log.WithError(err).Fatal("runc not found")32}3334var useFacade bool35for _, arg := range os.Args {36if arg == "create" {37useFacade = true38break39}40}4142if useFacade {43err = createAndRunc(runcPath, log)44} else {45err = syscall.Exec(runcPath, os.Args, os.Environ())46}47if err != nil {48log.WithError(err).Fatal("failed")49}50}5152func createAndRunc(runcPath string, log *logrus.Logger) error {53fc, err := os.ReadFile("config.json")54if err != nil {55return xerrors.Errorf("cannot read config.json: %w", err)56}5758var cfg specs.Spec59err = json.Unmarshal(fc, &cfg)60if err != nil {61return xerrors.Errorf("cannot decode config.json: %w", err)62}6364cfg.Process.OOMScoreAdj = &defaultOOMScoreAdj65delete(cfg.Linux.Sysctl, "net.ipv4.ip_unprivileged_port_start")66// TODO(toru): Drop the `kernel.domainame`` setting as it currently fails in the rootless container.67// - https://github.com/opencontainers/runc/issues/209168// - https://github.com/opencontainers/runtime-spec/issues/59269// Perhaps using OCI hooks can solve this problem, but it's a bit tricky and hard.70if _, ok := cfg.Linux.Sysctl["kernel.domainname"]; ok {71log.Warnln("Since the rootless container cannot use domainname yet, we ignored it.")72delete(cfg.Linux.Sysctl, "kernel.domainname")73}74cfg.Process.Capabilities.Ambient = append(cfg.Process.Capabilities.Ambient, "CAP_NET_BIND_SERVICE")75cfg.Process.Capabilities.Bounding = append(cfg.Process.Capabilities.Bounding, "CAP_NET_BIND_SERVICE")76cfg.Process.Capabilities.Effective = append(cfg.Process.Capabilities.Effective, "CAP_NET_BIND_SERVICE")77cfg.Process.Capabilities.Inheritable = append(cfg.Process.Capabilities.Inheritable, "CAP_NET_BIND_SERVICE")78cfg.Process.Capabilities.Permitted = append(cfg.Process.Capabilities.Permitted, "CAP_NET_BIND_SERVICE")7980fc, err = json.Marshal(cfg)81if err != nil {82return xerrors.Errorf("cannot encode config.json: %w", err)83}84err = os.WriteFile("config.json", fc, 0644)85if err != nil {86return xerrors.Errorf("cannot encode config.json: %w", err)87}8889// See here for more details on why retries are necessary.90// https://github.com/gitpod-io/gitpod/issues/1236591for i := 0; i <= RETRY; i++ {9293cmd := exec.Command(runcPath, os.Args[1:]...)94cmd.Stdin = os.Stdin95cmd.Stderr = os.Stderr96cmd.Stdout = os.Stdout97err = cmd.Run()9899if err != nil {100log.WithError(err).Warn("runc failed")101102// runc creation failures can be caused by timing issues with workspacekit/seccomp notify under load.103// Easing of on the pressure here lowers the likelihood of that error.104// NOTE(cw): glossing over races with delays is bad style, but also pragmatic.105//106// Context: https://linear.app/gitpod/issue/ENG-797/docker-containers-sometimes-fail-to-start107time.Sleep(100 * time.Millisecond)108continue109}110return nil111}112return xerrors.Errorf("exec %s: %w", runcPath, err)113}114115116