// Copyright (c) 2020 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 container import ( "errors" "io/fs" "os" "path/filepath" "strings" "golang.org/x/xerrors" ) // NodeMountsLookupConfig confiugures the node mount/fs access type NodeMountsLookupConfig struct { // ProcLoc is the path to the node's /proc/mounts - ProcLoc string `json:"proc"` } // Config configures the container runtime interface type Config struct { // Mounts configures the node mounts lookup Mounts NodeMountsLookupConfig `json:"mounts"` // Mapping mapps a path from the node to the container by stripping the key and prepending the value of this map. // For example {"/var/lib/containerd": "/mnt/snapshots"} would translate /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/ to /mnt/snapshots/io.containerd.snapshotter.v1.overlayfs/snapshots/ Mapping map[string]string `json:"nodeToContainerMapping"` // Runtime marks the container runtime we ought to connect to. // Depending on the value set here we expect the corresponding config struct to have a value. Runtime RuntimeType `json:"runtime"` // Containerd contains the containerd CRI config if runtime == RuntimeContainerd Containerd *ContainerdConfig `json:"containerd,omitempty"` } // RuntimeType lists the supported container runtimes type RuntimeType string const ( // RuntimeContainerd connects to containerd RuntimeContainerd RuntimeType = "containerd" ) // ContainerdConfig configures access to containerd type ContainerdConfig struct { // SocketPath is the path in the local file system pointing to the containerd socket. // If this field is not set, full workspace backups are not available. SocketPath string `json:"socket"` } // FromConfig produces a container runtime interface instance from the configuration func FromConfig(cfg *Config) (rt Runtime, err error) { if cfg == nil { return } mounts, err := NewNodeMountsLookup(&cfg.Mounts) if err != nil { return nil, err } switch cfg.Runtime { case RuntimeContainerd: if cfg.Containerd == nil { return nil, xerrors.Errorf("runtime is set to containerd, but not containerd config is provided") } return NewContainerd(cfg.Containerd, mounts, cfg.Mapping) default: return nil, xerrors.Errorf("unknown runtime type: %s", cfg.Runtime) } } // PathMapping maps a node path to a path in the container type PathMapping map[string]string // Translate maps a node-level (root mount namespace) path to a container-level path func (mapping PathMapping) Translate(from string) (result string, err error) { for np, cp := range mapping { if !strings.HasPrefix(from, np) { continue } pth := filepath.Join(cp, strings.TrimPrefix(from, np)) if _, err := os.Stat(pth); errors.Is(err, fs.ErrNotExist) { return "", xerrors.Errorf("path does not exist in container at %s", pth) } else if err != nil { return "", err } return pth, nil } return "", xerrors.Errorf("mount entry %s has no appropriate mapping", from) }