mirror of
https://github.com/moby/moby.git
synced 2026-08-07 08:31:39 +00:00
Merge pull request #52898 from vvoland/work-containerd-process
daemon: Add embedded containerd mode
This commit is contained in:
3
.github/workflows/.test.yml
vendored
3
.github/workflows/.test.yml
vendored
@@ -230,6 +230,9 @@ jobs:
|
||||
if [[ "$MODE" == *"nftables"* ]]; then
|
||||
echo "DOCKER_FIREWALL_BACKEND=nftables" >> $GITHUB_ENV
|
||||
fi
|
||||
if [[ "$MODE" == *"embedded"* ]]; then
|
||||
echo "TEST_INTEGRATION_CONTAINERD_EMBEDDED=1" >> $GITHUB_ENV
|
||||
fi
|
||||
echo "CACHE_DEV_SCOPE=${CACHE_DEV_SCOPE}" >> $GITHUB_ENV
|
||||
-
|
||||
name: Set up AppArmor for RootlessKit
|
||||
|
||||
16
.github/workflows/.windows.yml
vendored
16
.github/workflows/.windows.yml
vendored
@@ -20,6 +20,10 @@ on:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
containerd:
|
||||
required: false
|
||||
type: string
|
||||
default: "socket"
|
||||
secrets:
|
||||
CODECOV_TOKEN:
|
||||
required: false
|
||||
@@ -373,6 +377,7 @@ jobs:
|
||||
id: start-daemon
|
||||
env:
|
||||
INPUT_STORAGE: ${{ inputs.storage }}
|
||||
INPUT_CONTAINERD: ${{ inputs.containerd }}
|
||||
MATRIX_RUNTIME: ${{ matrix.runtime }}
|
||||
run: |
|
||||
Write-Host "Creating service"
|
||||
@@ -384,7 +389,9 @@ jobs:
|
||||
"--pidfile=$env:TEMP\docker.pid", `
|
||||
"--register-service"
|
||||
)
|
||||
If ($env:MATRIX_RUNTIME -eq "containerd") {
|
||||
If ($env:INPUT_CONTAINERD -eq "embedded") {
|
||||
$args += "--feature=embedded-containerd"
|
||||
} ElseIf ($env:MATRIX_RUNTIME -eq "containerd") {
|
||||
$args += "--default-runtime=io.containerd.runhcs.v1"
|
||||
echo "DOCKER_WINDOWS_CONTAINERD_RUNTIME=1" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
||||
}
|
||||
@@ -401,6 +408,10 @@ jobs:
|
||||
$dockerEnviron += @("TEST_INTEGRATION_USE_GRAPHDRIVER=1")
|
||||
echo "TEST_INTEGRATION_USE_GRAPHDRIVER=1" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
||||
}
|
||||
If ($env:INPUT_CONTAINERD -eq "embedded") {
|
||||
$dockerEnviron += @("TEST_INTEGRATION_CONTAINERD_EMBEDDED=1")
|
||||
echo "TEST_INTEGRATION_CONTAINERD_EMBEDDED=1" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
||||
}
|
||||
New-ItemProperty -Name "Environment" -Path "HKLM:\SYSTEM\CurrentControlSet\Services\docker" -PropertyType MultiString -Value $dockerEnviron
|
||||
Write-Host "Starting service"
|
||||
Start-Service -Name docker
|
||||
@@ -408,6 +419,7 @@ jobs:
|
||||
-
|
||||
name: Waiting for test daemon to start
|
||||
env:
|
||||
INPUT_CONTAINERD: ${{ inputs.containerd }}
|
||||
MATRIX_RUNTIME: ${{ matrix.runtime }}
|
||||
DOCKER_HOST: npipe:////./pipe/docker_engine
|
||||
run: |
|
||||
@@ -428,7 +440,7 @@ jobs:
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
Write-Host "Test daemon started and replied!"
|
||||
If ($env:MATRIX_RUNTIME -eq "containerd") {
|
||||
If ($env:INPUT_CONTAINERD -ne "embedded" -and $env:MATRIX_RUNTIME -eq "containerd") {
|
||||
$containerdProcesses = Get-Process -Name containerd -ErrorAction:SilentlyContinue
|
||||
If (-not $containerdProcesses) {
|
||||
Throw "containerd process is not running"
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -92,7 +92,7 @@ jobs:
|
||||
- arch: amd64
|
||||
storage: snapshotter
|
||||
runnerSuffix: ""
|
||||
extraModes: '["rootless", "systemd", "rootless-systemd", "iptables+firewalld", "nftables", "nftables+firewalld"]'
|
||||
extraModes: '["rootless", "systemd", "rootless-systemd", "iptables+firewalld", "nftables", "nftables+firewalld", "embedded"]'
|
||||
- arch: amd64
|
||||
storage: graphdriver
|
||||
runnerSuffix: ""
|
||||
|
||||
11
.github/workflows/windows-2022.yml
vendored
11
.github/workflows/windows-2022.yml
vendored
@@ -24,12 +24,17 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
storage:
|
||||
- graphdriver
|
||||
- snapshotter
|
||||
include:
|
||||
- storage: graphdriver
|
||||
containerd: socket
|
||||
- storage: snapshotter
|
||||
containerd: socket
|
||||
- storage: snapshotter
|
||||
containerd: embedded
|
||||
with:
|
||||
os: windows-2022
|
||||
storage: ${{ matrix.storage }}
|
||||
containerd: ${{ matrix.containerd }}
|
||||
send_coverage: true
|
||||
secrets:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
11
.github/workflows/windows-2025.yml
vendored
11
.github/workflows/windows-2025.yml
vendored
@@ -29,12 +29,17 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
storage:
|
||||
- graphdriver
|
||||
- snapshotter
|
||||
include:
|
||||
- storage: graphdriver
|
||||
containerd: socket
|
||||
- storage: snapshotter
|
||||
containerd: socket
|
||||
- storage: snapshotter
|
||||
containerd: embedded
|
||||
with:
|
||||
os: windows-2025
|
||||
storage: ${{ matrix.storage }}
|
||||
containerd: ${{ matrix.containerd }}
|
||||
send_coverage: false
|
||||
secrets:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
"github.com/moby/moby/v2/daemon/config"
|
||||
buildkit "github.com/moby/moby/v2/daemon/internal/builder-next"
|
||||
"github.com/moby/moby/v2/daemon/internal/builder-next/exporter"
|
||||
"github.com/moby/moby/v2/daemon/internal/libcontainerd/supervisor"
|
||||
"github.com/moby/moby/v2/daemon/internal/containerd/server/supervisor"
|
||||
"github.com/moby/moby/v2/daemon/internal/otelutil"
|
||||
"github.com/moby/moby/v2/daemon/internal/rootless"
|
||||
"github.com/moby/moby/v2/daemon/listeners"
|
||||
@@ -76,6 +76,10 @@ type daemonCLI struct {
|
||||
d *daemon.Daemon
|
||||
authzMiddleware *authorization.Middleware // authzMiddleware enables to dynamically reload the authorization plugins
|
||||
|
||||
// containerdDialer is the in-memory dialer for the embedded containerd. It
|
||||
// is set in embedded mode and nil otherwise. See [daemon.ContainerdDialer].
|
||||
containerdDialer daemon.ContainerdDialer
|
||||
|
||||
stopOnce sync.Once
|
||||
apiShutdown chan struct{}
|
||||
apiTLSConfig *tls.Config
|
||||
@@ -294,7 +298,7 @@ func (cli *daemonCLI) start(ctx context.Context) (retErr error) {
|
||||
}
|
||||
cli.authzMiddleware = authz
|
||||
|
||||
d, err := daemon.NewDaemon(ctx, cli.Config, pluginStore, cli.authzMiddleware)
|
||||
d, err := daemon.NewDaemon(ctx, cli.Config, pluginStore, cli.authzMiddleware, cli.containerdDialer)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to start daemon")
|
||||
}
|
||||
@@ -322,7 +326,7 @@ func (cli *daemonCLI) start(ctx context.Context) (retErr error) {
|
||||
// initialized the cluster.
|
||||
d.RestartSwarmContainers()
|
||||
|
||||
b, shutdownBuildKit, err := initBuildkit(ctx, d, cdiCache)
|
||||
b, shutdownBuildKit, err := initBuildkit(ctx, d, cdiCache, cli.containerdDialer)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error initializing buildkit: %w", err)
|
||||
}
|
||||
@@ -444,7 +448,7 @@ func setOTLPProtoDefault() {
|
||||
}
|
||||
}
|
||||
|
||||
func initBuildkit(ctx context.Context, d *daemon.Daemon, cdiCache *cdi.Cache) (_ builderOptions, closeFn func(), _ error) {
|
||||
func initBuildkit(ctx context.Context, d *daemon.Daemon, cdiCache *cdi.Cache, containerdDialer daemon.ContainerdDialer) (_ builderOptions, closeFn func(), _ error) {
|
||||
log.G(ctx).Info("Initializing buildkit")
|
||||
closeFn = func() {}
|
||||
|
||||
@@ -477,6 +481,7 @@ func initBuildkit(ctx context.Context, d *daemon.Daemon, cdiCache *cdi.Cache) (_
|
||||
UseSnapshotter: d.UsesSnapshotter(),
|
||||
Snapshotter: d.ImageService().StorageDriver(),
|
||||
ContainerdAddress: cfg.ContainerdAddr,
|
||||
ContainerdDialer: containerdDialer,
|
||||
ContainerdNamespace: cfg.ContainerdNamespace,
|
||||
HyperVIsolation: d.DefaultIsolation().IsHyperV(),
|
||||
Callbacks: exporter.BuildkitCallbacks{
|
||||
@@ -1159,7 +1164,11 @@ func (cli *daemonCLI) initializeContainerd(ctx context.Context) (func(time.Durat
|
||||
return nil, errors.Wrap(err, "failed to generate containerd options")
|
||||
}
|
||||
|
||||
r, err := supervisor.Start(ctx, filepath.Join(cli.Config.Root, "containerd"), filepath.Join(cli.Config.ExecRoot, "containerd"), opts...)
|
||||
rootDir, err := containerdRootDir(ctx, cli.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, err := supervisor.Start(ctx, rootDir, filepath.Join(cli.Config.ExecRoot, "containerd"), opts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to start containerd")
|
||||
}
|
||||
@@ -1169,6 +1178,24 @@ func (cli *daemonCLI) initializeContainerd(ctx context.Context) (func(time.Durat
|
||||
return r.WaitTimeout, nil
|
||||
}
|
||||
|
||||
// containerdRootEnv is a temporary testing override for reusing a standalone
|
||||
// containerd data root with embedded or supervised containerd.
|
||||
const containerdRootEnv = "DOCKER_CONTAINERD_ROOT"
|
||||
|
||||
func containerdRootDir(ctx context.Context, cfg *config.Config) (string, error) {
|
||||
if root := os.Getenv(containerdRootEnv); root != "" {
|
||||
if !filepath.IsAbs(root) {
|
||||
return "", fmt.Errorf("%s must be an absolute path: %q", containerdRootEnv, root)
|
||||
}
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
"environment-variable": containerdRootEnv,
|
||||
"root": root,
|
||||
}).Warn("overriding containerd root from environment")
|
||||
return root, nil
|
||||
}
|
||||
return filepath.Join(cfg.Root, "containerd", "daemon"), nil
|
||||
}
|
||||
|
||||
// cdiEnabled returns true if CDI feature wasn't explicitly disabled via
|
||||
// features.
|
||||
func cdiEnabled(conf *config.Config) bool {
|
||||
|
||||
36
daemon/command/daemon_embedded_containerd.go
Normal file
36
daemon/command/daemon_embedded_containerd.go
Normal file
@@ -0,0 +1,36 @@
|
||||
//go:build !no_embedded_containerd
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/moby/moby/v2/daemon/internal/containerd/server/embedded"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// initEmbeddedContainerd starts containerd inside the daemon process and points
|
||||
// the containerd clients at it. It is selected by the "embedded-containerd"
|
||||
// feature.
|
||||
func (cli *daemonCLI) initEmbeddedContainerd(ctx context.Context) (func(time.Duration) error, error) {
|
||||
rootDir, err := containerdRootDir(ctx, cli.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.G(ctx).Warn("Running with experimental embedded-containerd mode")
|
||||
d, err := embedded.Start(
|
||||
ctx,
|
||||
rootDir,
|
||||
filepath.Join(cli.Config.ExecRoot, "containerd"),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to start embedded containerd")
|
||||
}
|
||||
cli.Config.ContainerdAddr = d.Address()
|
||||
cli.containerdDialer = d.Dial
|
||||
|
||||
return d.WaitTimeout, nil
|
||||
}
|
||||
13
daemon/command/daemon_no_embedded_containerd.go
Normal file
13
daemon/command/daemon_no_embedded_containerd.go
Normal file
@@ -0,0 +1,13 @@
|
||||
//go:build no_embedded_containerd
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (cli *daemonCLI) initEmbeddedContainerd(context.Context) (func(time.Duration) error, error) {
|
||||
return nil, errors.New("embedded containerd is not supported in this build")
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
@@ -181,6 +182,24 @@ func TestLoadDaemonConfigWithEmbeddedOptions(t *testing.T) {
|
||||
assert.Check(t, is.Equal("syslog", loadedConfig.LogConfig.Type))
|
||||
}
|
||||
|
||||
func TestContainerdRootDir(t *testing.T) {
|
||||
cfg := config.Config{CommonConfig: config.CommonConfig{Root: t.TempDir()}}
|
||||
t.Setenv(containerdRootEnv, "")
|
||||
root, err := containerdRootDir(t.Context(), &cfg)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(root, filepath.Join(cfg.Root, "containerd", "daemon")))
|
||||
|
||||
override := t.TempDir()
|
||||
t.Setenv(containerdRootEnv, override)
|
||||
root, err = containerdRootDir(t.Context(), &cfg)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(root, override))
|
||||
|
||||
t.Setenv(containerdRootEnv, filepath.Join("relative", "containerd"))
|
||||
_, err = containerdRootDir(t.Context(), &cfg)
|
||||
assert.Check(t, is.ErrorContains(err, "must be an absolute path"))
|
||||
}
|
||||
|
||||
func TestLoadDaemonConfigWithRegistryOptions(t *testing.T) {
|
||||
content := `{
|
||||
"registry-mirrors": ["https://mirrors.example.com"],
|
||||
|
||||
@@ -117,6 +117,11 @@ func newCgroupParent(cfg *config.Config) string {
|
||||
}
|
||||
|
||||
func (cli *daemonCLI) initContainerd(ctx context.Context) (func(time.Duration) error, error) {
|
||||
// Check embedded-containerd first so daemon.json can opt in even when
|
||||
// packaged service units pass a default --containerd socket.
|
||||
if cli.Config.Features["embedded-containerd"] {
|
||||
return cli.initEmbeddedContainerd(ctx)
|
||||
}
|
||||
if cli.Config.ContainerdAddr != "" {
|
||||
// use system containerd at the given address.
|
||||
return nil, nil
|
||||
|
||||
@@ -110,6 +110,11 @@ func newCgroupParent(*config.Config) string {
|
||||
}
|
||||
|
||||
func (cli *daemonCLI) initContainerd(ctx context.Context) (func(time.Duration) error, error) {
|
||||
// Check embedded-containerd first so daemon.json can opt in even when
|
||||
// packaged service units pass a default --containerd socket.
|
||||
if cli.Config.Features["embedded-containerd"] {
|
||||
return cli.initEmbeddedContainerd(ctx)
|
||||
}
|
||||
if cli.Config.ContainerdAddr != "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -119,6 +119,10 @@ var skipDuplicates = map[string]bool{
|
||||
"runtimes": true,
|
||||
}
|
||||
|
||||
// errEmbeddedContainerdWithCRI is returned when both the "embedded-containerd"
|
||||
// feature and CRI support are enabled.
|
||||
var errEmbeddedContainerdWithCRI = errors.New(`conflicting options: cannot use the "embedded-containerd" feature and CRI support (--cri-containerd) at the same time`)
|
||||
|
||||
// migratedNamedConfig describes legacy configuration file keys that have been migrated
|
||||
// from simple entries equivalent to command line flags, to a named option.
|
||||
//
|
||||
@@ -710,6 +714,10 @@ func Validate(config *Config) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.Features["embedded-containerd"] && config.CriContainerd {
|
||||
return errEmbeddedContainerdWithCRI
|
||||
}
|
||||
|
||||
// validate DNSSearch
|
||||
for _, dnsSearch := range config.DNSSearch {
|
||||
if _, err := opts.ValidateDNSSearch(dnsSearch); err != nil {
|
||||
|
||||
@@ -234,6 +234,16 @@ func TestValidateConfigurationErrors(t *testing.T) {
|
||||
},
|
||||
expectedErr: "bad attribute format: one",
|
||||
},
|
||||
{
|
||||
name: "embedded-containerd with cri-containerd",
|
||||
config: &Config{
|
||||
CommonConfig: CommonConfig{
|
||||
Features: map[string]bool{"embedded-containerd": true},
|
||||
CriContainerd: true,
|
||||
},
|
||||
},
|
||||
expectedErr: errEmbeddedContainerdWithCRI.Error(),
|
||||
},
|
||||
{
|
||||
name: "multiple label without value",
|
||||
config: &Config{
|
||||
@@ -459,6 +469,17 @@ func TestValidateConfigurationErrors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateEmbeddedContainerdAllowsExplicitAddr(t *testing.T) {
|
||||
cfg := &Config{
|
||||
CommonConfig: CommonConfig{
|
||||
Features: map[string]bool{"embedded-containerd": true},
|
||||
ContainerdAddr: "/run/containerd/containerd.sock",
|
||||
},
|
||||
}
|
||||
|
||||
assert.NilError(t, Validate(cfg))
|
||||
}
|
||||
|
||||
func withForceOverwrite(fieldName string) func(config *mergo.Config) {
|
||||
return mergo.WithTransformers(overwriteTransformer{fieldName: fieldName})
|
||||
}
|
||||
|
||||
@@ -96,6 +96,13 @@ type configStore struct {
|
||||
Runtimes runtimes
|
||||
}
|
||||
|
||||
// ContainerdDialer dials the in-process containerd over an in-memory pipe.
|
||||
//
|
||||
// It is set in embedded mode, so the daemon's own client can skip socket
|
||||
// syscalls, and is nil otherwise. The signature matches grpc.WithContextDialer,
|
||||
// and the address argument is ignored.
|
||||
type ContainerdDialer = func(ctx context.Context, addr string) (net.Conn, error)
|
||||
|
||||
// Daemon holds information about the Docker daemon.
|
||||
type Daemon struct {
|
||||
id string
|
||||
@@ -846,7 +853,7 @@ func CheckSystem() error {
|
||||
|
||||
// NewDaemon sets up everything for the daemon to be able to service
|
||||
// requests from the webserver.
|
||||
func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.Store, authzMiddleware *authorization.Middleware) (_ *Daemon, retErr error) {
|
||||
func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.Store, authzMiddleware *authorization.Middleware, containerdDialer ContainerdDialer) (_ *Daemon, retErr error) {
|
||||
registryService, err := registry.NewService(config.ServiceOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1009,6 +1016,12 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
|
||||
grpc.WithUnaryInterceptor(grpcerrors.UnaryClientInterceptor),
|
||||
grpc.WithStreamInterceptor(grpcerrors.StreamClientInterceptor),
|
||||
}
|
||||
if containerdDialer != nil {
|
||||
// Keep ContainerdAddr as the gRPC target (so containerd.New still
|
||||
// installs its namespace interceptors) but override the connection to
|
||||
// use the in-memory pipe.
|
||||
gopts = append(gopts, grpc.WithContextDialer(containerdDialer))
|
||||
}
|
||||
|
||||
if cfgStore.ContainerdAddr != "" {
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
|
||||
@@ -251,6 +251,9 @@ func (daemon *Daemon) fillDebugInfo(ctx context.Context, v *system.Info) {
|
||||
// fillContainerdInfo provides information about the containerd configuration
|
||||
// for debugging purposes.
|
||||
func (daemon *Daemon) fillContainerdInfo(v *system.Info, cfg *config.Config) {
|
||||
if cfg.Features["embedded-containerd"] {
|
||||
v.Warnings = append(v.Warnings, "WARNING: Running with experimental embedded-containerd mode. This feature may change or be removed in a future release.")
|
||||
}
|
||||
if cfg.ContainerdAddr == "" {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -98,6 +99,10 @@ type Opt struct {
|
||||
UseSnapshotter bool
|
||||
Snapshotter string
|
||||
ContainerdAddress string
|
||||
// ContainerdDialer, when set (embedded mode), connects to the in-process
|
||||
// containerd over an in-memory pipe so BuildKit avoids socket syscalls.
|
||||
// ContainerdAddress is still the gRPC target.
|
||||
ContainerdDialer func(ctx context.Context, address string) (net.Conn, error)
|
||||
ContainerdNamespace string
|
||||
HyperVIsolation bool
|
||||
Callbacks exporter.BuildkitCallbacks
|
||||
|
||||
@@ -58,6 +58,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
"go.opentelemetry.io/otel/sdk/trace"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func newController(ctx context.Context, rt http.RoundTripper, opt Opt) (*control.Controller, error) {
|
||||
@@ -134,7 +135,16 @@ func newSnapshotterController(ctx context.Context, rt http.RoundTripper, opt Opt
|
||||
CDIManager: cdiManager,
|
||||
}
|
||||
|
||||
wo, err := containerd.NewWorkerOpt(workerOpts, ctd.WithTimeout(60*time.Second))
|
||||
ctdOpts := []ctd.Opt{ctd.WithTimeout(60 * time.Second)}
|
||||
if opt.ContainerdDialer != nil {
|
||||
// Embedded mode: dial the in-process containerd over the in-memory
|
||||
// pipe. The address stays the gRPC target, and the dialer overrides the
|
||||
// connection.
|
||||
ctdOpts = append(ctdOpts, ctd.WithExtraDialOpts([]grpc.DialOption{
|
||||
grpc.WithContextDialer(opt.ContainerdDialer),
|
||||
}))
|
||||
}
|
||||
wo, err := containerd.NewWorkerOpt(workerOpts, ctdOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -163,6 +173,7 @@ func newSnapshotterController(ctx context.Context, rt http.RoundTripper, opt Opt
|
||||
rootless: opt.Rootless,
|
||||
identityMapping: opt.IdentityMapping,
|
||||
containerdAddr: opt.ContainerdAddress,
|
||||
containerdDialer: opt.ContainerdDialer,
|
||||
containerdNamespace: opt.ContainerdNamespace,
|
||||
hypervIsolation: opt.HyperVIsolation,
|
||||
})
|
||||
@@ -384,6 +395,7 @@ func newGraphDriverController(ctx context.Context, rt http.RoundTripper, opt Opt
|
||||
|
||||
// Windows-only fields (currently not used, as newExecutorGD is not implemented on Windows)
|
||||
containerdAddr: opt.ContainerdAddress,
|
||||
containerdDialer: opt.ContainerdDialer,
|
||||
containerdNamespace: opt.ContainerdNamespace,
|
||||
hypervIsolation: opt.HyperVIsolation,
|
||||
})
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package buildkit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/moby/buildkit/executor/oci"
|
||||
"github.com/moby/buildkit/solver/llbsolver/cdidevices"
|
||||
"github.com/moby/buildkit/util/network"
|
||||
@@ -26,6 +29,7 @@ type executorOpts struct {
|
||||
|
||||
// windows-only fields
|
||||
containerdAddr string
|
||||
containerdDialer func(ctx context.Context, address string) (net.Conn, error)
|
||||
containerdNamespace string
|
||||
hypervIsolation bool
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/util/network"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
const networkName = "nat"
|
||||
@@ -23,8 +24,14 @@ func newExecutor(opts executorOpts) (executor.Executor, network.ProxyProvider, e
|
||||
pb.NetMode_NONE: network.NewNoneProvider(),
|
||||
}
|
||||
|
||||
opt := ctd.WithDefaultNamespace(opts.containerdNamespace)
|
||||
client, err := ctd.New(opts.containerdAddr, opt)
|
||||
ctdOpts := []ctd.Opt{ctd.WithDefaultNamespace(opts.containerdNamespace)}
|
||||
if opts.containerdDialer != nil {
|
||||
// Embedded containerd: connect over the in-memory pipe.
|
||||
ctdOpts = append(ctdOpts, ctd.WithExtraDialOpts([]grpc.DialOption{
|
||||
grpc.WithContextDialer(opts.containerdDialer),
|
||||
}))
|
||||
}
|
||||
client, err := ctd.New(opts.containerdAddr, ctdOpts...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
284
daemon/internal/containerd/server/embedded/containerd.go
Normal file
284
daemon/internal/containerd/server/embedded/containerd.go
Normal file
@@ -0,0 +1,284 @@
|
||||
//go:build (linux || windows) && !no_embedded_containerd
|
||||
|
||||
package embedded
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/containerd/containerd/v2/defaults"
|
||||
"github.com/containerd/containerd/v2/pkg/namespaces"
|
||||
"github.com/containerd/containerd/v2/pkg/sys"
|
||||
"github.com/containerd/containerd/v2/plugins"
|
||||
"github.com/containerd/log"
|
||||
"github.com/containerd/plugin"
|
||||
"github.com/containerd/plugin/registry"
|
||||
"github.com/containerd/ttrpc"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type grpcService interface {
|
||||
Register(*grpc.Server) error
|
||||
}
|
||||
|
||||
type ttrpcService interface {
|
||||
RegisterTTRPC(*ttrpc.Server) error
|
||||
}
|
||||
|
||||
type serverConfig struct {
|
||||
root string
|
||||
state string
|
||||
grpcAddress string
|
||||
ttrpcAddress string
|
||||
maxRecvMessageSize int
|
||||
maxSendMessageSize int
|
||||
}
|
||||
|
||||
type containerdServer struct {
|
||||
grpcServer *grpc.Server
|
||||
ttrpcServer *ttrpc.Server
|
||||
serveCtx context.Context
|
||||
cancelServe context.CancelFunc
|
||||
plugins []*plugin.Plugin
|
||||
ready sync.WaitGroup
|
||||
stopOnce sync.Once
|
||||
}
|
||||
|
||||
// newServer initializes the containerd plugin graph and the RPC servers needed
|
||||
// by dockerd.
|
||||
// Command-owned behavior such as process mutation, proxy plugins, TCP/TLS, and
|
||||
// Prometheus registration is intentionally not part of the embedded server.
|
||||
func newServer(ctx context.Context, cfg *serverConfig) (*containerdServer, error) {
|
||||
registrations := registry.Graph(nil)
|
||||
return newServerWithRegistrations(ctx, cfg, registrations)
|
||||
}
|
||||
|
||||
// newServerWithRegistrations mirrors containerd's plugin lifecycle for the
|
||||
// subset used by dockerd.
|
||||
// It is based on containerd v2.3.3's cmd/containerd/server.New:
|
||||
// https://github.com/containerd/containerd/blob/aad11006b869517fcd3009450b6f82da282e1a9b/cmd/containerd/server/server.go
|
||||
func newServerWithRegistrations(ctx context.Context, cfg *serverConfig, registrations []plugin.Registration) (_ *containerdServer, retErr error) {
|
||||
// BuildKit registers collectors with the same names in dockerd, and the
|
||||
// embedded containerd metrics are not exposed, so omit gRPC metrics instead
|
||||
// of mutating the process-global Prometheus registerer.
|
||||
grpcOptions := []grpc.ServerOption{
|
||||
grpc.StatsHandler(otelgrpc.NewServerHandler()),
|
||||
grpc.ChainStreamInterceptor(streamNamespaceInterceptor),
|
||||
grpc.ChainUnaryInterceptor(unaryNamespaceInterceptor),
|
||||
}
|
||||
if cfg.maxRecvMessageSize > 0 {
|
||||
grpcOptions = append(grpcOptions, grpc.MaxRecvMsgSize(cfg.maxRecvMessageSize))
|
||||
}
|
||||
if cfg.maxSendMessageSize > 0 {
|
||||
grpcOptions = append(grpcOptions, grpc.MaxSendMsgSize(cfg.maxSendMessageSize))
|
||||
}
|
||||
|
||||
ttrpcServer, err := newTTRPCServer()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating containerd ttrpc server: %w", err)
|
||||
}
|
||||
|
||||
serveCtx, cancelServe := context.WithCancel(ctx)
|
||||
srv := &containerdServer{
|
||||
grpcServer: grpc.NewServer(grpcOptions...),
|
||||
ttrpcServer: ttrpcServer,
|
||||
serveCtx: serveCtx,
|
||||
cancelServe: cancelServe,
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
srv.Stop()
|
||||
}
|
||||
}()
|
||||
|
||||
initialized := plugin.NewPluginSet()
|
||||
|
||||
var grpcServices []grpcService
|
||||
var ttrpcServices []ttrpcService
|
||||
for _, registration := range registrations {
|
||||
id := registration.URI()
|
||||
log.G(ctx).WithFields(log.Fields{"id": id, "type": registration.Type}).Info("loading plugin")
|
||||
|
||||
var mustSucceed atomic.Bool
|
||||
initContext := plugin.NewContext(
|
||||
ctx,
|
||||
initialized,
|
||||
map[string]string{
|
||||
plugins.PropertyRootDir: filepath.Join(cfg.root, id),
|
||||
plugins.PropertyStateDir: filepath.Join(cfg.state, id),
|
||||
plugins.PropertyGRPCAddress: cfg.grpcAddress,
|
||||
plugins.PropertyTTRPCAddress: cfg.ttrpcAddress,
|
||||
},
|
||||
)
|
||||
initContext.RegisterReadiness = func() func() {
|
||||
mustSucceed.Store(true)
|
||||
return srv.registerReadiness()
|
||||
}
|
||||
initContext.Config = registration.Config
|
||||
|
||||
result := registration.Init(initContext)
|
||||
if err := initialized.Add(result); err != nil {
|
||||
return nil, fmt.Errorf("adding plugin %q to initialized set: %w", id, err)
|
||||
}
|
||||
|
||||
instance, err := result.Instance()
|
||||
if err != nil {
|
||||
fields := log.Fields{"error": err, "id": id, "type": registration.Type}
|
||||
if plugin.IsSkipPlugin(err) {
|
||||
log.G(ctx).WithFields(fields).Info("skip loading plugin")
|
||||
} else {
|
||||
log.G(ctx).WithFields(fields).Warn("failed to load plugin")
|
||||
}
|
||||
if mustSucceed.Load() {
|
||||
return nil, fmt.Errorf("plugin %q failed after registering readiness: %w", id, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if service, ok := instance.(grpcService); ok {
|
||||
grpcServices = append(grpcServices, service)
|
||||
}
|
||||
if service, ok := instance.(ttrpcService); ok {
|
||||
ttrpcServices = append(ttrpcServices, service)
|
||||
}
|
||||
srv.plugins = append(srv.plugins, result)
|
||||
}
|
||||
|
||||
for _, service := range grpcServices {
|
||||
if err := service.Register(srv.grpcServer); err != nil {
|
||||
return nil, fmt.Errorf("registering containerd grpc service: %w", err)
|
||||
}
|
||||
}
|
||||
for _, service := range ttrpcServices {
|
||||
if err := service.RegisterTTRPC(srv.ttrpcServer); err != nil {
|
||||
return nil, fmt.Errorf("registering containerd ttrpc service: %w", err)
|
||||
}
|
||||
}
|
||||
return srv, nil
|
||||
}
|
||||
|
||||
func createTopLevelDirectories(cfg *serverConfig) error {
|
||||
switch {
|
||||
case cfg.root == "":
|
||||
return errors.New("containerd root must be specified")
|
||||
case cfg.state == "":
|
||||
return errors.New("containerd state must be specified")
|
||||
case cfg.root == cfg.state:
|
||||
return errors.New("containerd root and state must be different paths")
|
||||
}
|
||||
|
||||
if err := sys.MkdirAllWithACL(cfg.root, 0o700); err != nil {
|
||||
return fmt.Errorf("creating containerd root directory: %w", err)
|
||||
}
|
||||
if err := os.Chmod(cfg.root, 0o700); err != nil {
|
||||
return fmt.Errorf("setting containerd root directory permissions: %w", err)
|
||||
}
|
||||
|
||||
// State must be searchable by remapped users so they can reach plugin-owned
|
||||
// directories with more restrictive permissions below it.
|
||||
if err := sys.MkdirAllWithACL(cfg.state, 0o711); err != nil {
|
||||
return fmt.Errorf("creating containerd state directory: %w", err)
|
||||
}
|
||||
if cfg.state != defaults.DefaultStateDir {
|
||||
// Shim sockets and FIFOs still use the default state directory even when
|
||||
// containerd is configured with a different state directory.
|
||||
if err := sys.MkdirAllWithACL(defaults.DefaultStateDir, 0o711); err != nil {
|
||||
return fmt.Errorf("creating default containerd state directory: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *containerdServer) ServeGRPC(listener net.Listener) error {
|
||||
return trapClosedConnErr(s.grpcServer.Serve(listener))
|
||||
}
|
||||
|
||||
func (s *containerdServer) ServeTTRPC(listener net.Listener) error {
|
||||
return trapClosedConnErr(s.ttrpcServer.Serve(s.serveCtx, listener))
|
||||
}
|
||||
|
||||
func (s *containerdServer) Stop() {
|
||||
s.stopOnce.Do(func() {
|
||||
s.cancelServe()
|
||||
s.grpcServer.Stop()
|
||||
_ = s.ttrpcServer.Close()
|
||||
s.closePlugins()
|
||||
})
|
||||
}
|
||||
|
||||
func (s *containerdServer) registerReadiness() func() {
|
||||
s.ready.Add(1)
|
||||
return s.ready.Done
|
||||
}
|
||||
|
||||
func (s *containerdServer) Wait() {
|
||||
s.ready.Wait()
|
||||
}
|
||||
|
||||
func (s *containerdServer) closePlugins() {
|
||||
for i := len(s.plugins) - 1; i >= 0; i-- {
|
||||
initialized := s.plugins[i]
|
||||
instance, err := initialized.Instance()
|
||||
if err != nil {
|
||||
log.L.WithFields(log.Fields{
|
||||
"error": err,
|
||||
"id": initialized.Registration.URI(),
|
||||
}).Error("could not get plugin instance")
|
||||
continue
|
||||
}
|
||||
closer, ok := instance.(io.Closer)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if err := closer.Close(); err != nil {
|
||||
log.L.WithFields(log.Fields{
|
||||
"error": err,
|
||||
"id": initialized.Registration.URI(),
|
||||
}).Error("failed to close plugin")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func unaryNamespaceInterceptor(ctx context.Context, req any, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
||||
if namespace, ok := namespaces.Namespace(ctx); ok {
|
||||
// Namespace reads incoming metadata; add it to outgoing metadata for
|
||||
// service handlers that call other containerd components.
|
||||
ctx = namespaces.WithNamespace(ctx, namespace)
|
||||
}
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
func streamNamespaceInterceptor(server any, stream grpc.ServerStream, _ *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
ctx := stream.Context()
|
||||
if namespace, ok := namespaces.Namespace(ctx); ok {
|
||||
// Namespace reads incoming metadata; add it to outgoing metadata for
|
||||
// service handlers that call other containerd components.
|
||||
ctx = namespaces.WithNamespace(ctx, namespace)
|
||||
stream = &serverStreamWithContext{ServerStream: stream, ctx: ctx}
|
||||
}
|
||||
return handler(server, stream)
|
||||
}
|
||||
|
||||
type serverStreamWithContext struct {
|
||||
grpc.ServerStream
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (s *serverStreamWithContext) Context() context.Context {
|
||||
return s.ctx
|
||||
}
|
||||
|
||||
func trapClosedConnErr(err error) error {
|
||||
if err == nil || errors.Is(err, net.ErrClosed) || errors.Is(err, ttrpc.ErrServerClosed) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
203
daemon/internal/containerd/server/embedded/containerd_test.go
Normal file
203
daemon/internal/containerd/server/embedded/containerd_test.go
Normal file
@@ -0,0 +1,203 @@
|
||||
//go:build (linux || windows) && !no_embedded_containerd
|
||||
|
||||
package embedded
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/v2/plugins"
|
||||
"github.com/containerd/plugin"
|
||||
"github.com/containerd/ttrpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
type recordingService struct {
|
||||
id string
|
||||
grpcRegistrations *[]string
|
||||
ttrpcRegistrations *[]string
|
||||
closed *[]string
|
||||
registerErr error
|
||||
}
|
||||
|
||||
func (s *recordingService) Register(*grpc.Server) error {
|
||||
if s.grpcRegistrations != nil {
|
||||
*s.grpcRegistrations = append(*s.grpcRegistrations, s.id)
|
||||
}
|
||||
return s.registerErr
|
||||
}
|
||||
|
||||
func (s *recordingService) RegisterTTRPC(*ttrpc.Server) error {
|
||||
if s.ttrpcRegistrations != nil {
|
||||
*s.ttrpcRegistrations = append(*s.ttrpcRegistrations, s.id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *recordingService) Close() error {
|
||||
if s.closed != nil {
|
||||
*s.closed = append(*s.closed, s.id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestContainerdServerPluginLifecycle(t *testing.T) {
|
||||
var initialized, grpcRegistrations, ttrpcRegistrations, closed []string
|
||||
cfg := &serverConfig{
|
||||
root: filepath.Join(t.TempDir(), "root"),
|
||||
state: filepath.Join(t.TempDir(), "state"),
|
||||
grpcAddress: "grpc-address",
|
||||
ttrpcAddress: "ttrpc-address",
|
||||
}
|
||||
|
||||
const pluginType = plugin.Type("io.moby.test.v1")
|
||||
newRegistration := func(id string) plugin.Registration {
|
||||
pluginConfig := &struct{ ID string }{ID: id}
|
||||
return plugin.Registration{
|
||||
Type: pluginType,
|
||||
ID: id,
|
||||
Config: pluginConfig,
|
||||
InitFn: func(initContext *plugin.InitContext) (any, error) {
|
||||
uri := string(pluginType) + "." + id
|
||||
assert.Check(t, is.Equal(initContext.Properties[plugins.PropertyRootDir], filepath.Join(cfg.root, uri)))
|
||||
assert.Check(t, is.Equal(initContext.Properties[plugins.PropertyStateDir], filepath.Join(cfg.state, uri)))
|
||||
assert.Check(t, is.Equal(initContext.Properties[plugins.PropertyGRPCAddress], cfg.grpcAddress))
|
||||
assert.Check(t, is.Equal(initContext.Properties[plugins.PropertyTTRPCAddress], cfg.ttrpcAddress))
|
||||
assert.Check(t, is.Equal(initContext.Config, pluginConfig))
|
||||
ready := initContext.RegisterReadiness()
|
||||
ready()
|
||||
initialized = append(initialized, id)
|
||||
return &recordingService{
|
||||
id: id,
|
||||
grpcRegistrations: &grpcRegistrations,
|
||||
ttrpcRegistrations: &ttrpcRegistrations,
|
||||
closed: &closed,
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
registrations := []plugin.Registration{
|
||||
newRegistration("first"),
|
||||
newRegistration("second"),
|
||||
}
|
||||
server, err := newServerWithRegistrations(t.Context(), cfg, registrations)
|
||||
assert.NilError(t, err)
|
||||
server.Wait()
|
||||
assert.Check(t, is.DeepEqual(initialized, []string{"first", "second"}))
|
||||
assert.Check(t, is.DeepEqual(grpcRegistrations, []string{"first", "second"}))
|
||||
assert.Check(t, is.DeepEqual(ttrpcRegistrations, []string{"first", "second"}))
|
||||
|
||||
server.Stop()
|
||||
server.Stop()
|
||||
assert.Check(t, is.DeepEqual(closed, []string{"second", "first"}))
|
||||
}
|
||||
|
||||
func TestContainerdServerClosesPluginsAfterRegistrationFailure(t *testing.T) {
|
||||
var closed []string
|
||||
cfg := &serverConfig{
|
||||
root: t.TempDir(),
|
||||
state: t.TempDir(),
|
||||
}
|
||||
registrations := []plugin.Registration{
|
||||
{
|
||||
Type: "io.moby.test.v1",
|
||||
ID: "loaded",
|
||||
InitFn: func(*plugin.InitContext) (any, error) {
|
||||
return &recordingService{id: "loaded", closed: &closed}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "io.moby.test.v1",
|
||||
ID: "failing",
|
||||
InitFn: func(*plugin.InitContext) (any, error) {
|
||||
return &recordingService{
|
||||
id: "failing",
|
||||
closed: &closed,
|
||||
registerErr: errors.New("registration failed"),
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
server, err := newServerWithRegistrations(t.Context(), cfg, registrations)
|
||||
assert.Check(t, is.Nil(server))
|
||||
assert.Check(t, is.ErrorContains(err, "registration failed"))
|
||||
assert.Check(t, is.DeepEqual(closed, []string{"failing", "loaded"}))
|
||||
}
|
||||
|
||||
func TestContainerdServerStopCancelsTTRPCRequests(t *testing.T) {
|
||||
ttrpcServer, err := ttrpc.NewServer()
|
||||
assert.NilError(t, err)
|
||||
|
||||
requestStarted := make(chan struct{})
|
||||
requestCanceled := make(chan struct{})
|
||||
ttrpcServer.RegisterService("test", &ttrpc.ServiceDesc{
|
||||
Methods: map[string]ttrpc.Method{
|
||||
"Wait": func(ctx context.Context, _ func(any) error) (any, error) {
|
||||
close(requestStarted)
|
||||
<-ctx.Done()
|
||||
close(requestCanceled)
|
||||
return nil, ctx.Err()
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
serveCtx, cancelServe := context.WithCancel(t.Context())
|
||||
server := &containerdServer{
|
||||
grpcServer: grpc.NewServer(),
|
||||
ttrpcServer: ttrpcServer,
|
||||
serveCtx: serveCtx,
|
||||
cancelServe: cancelServe,
|
||||
}
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
assert.NilError(t, err)
|
||||
t.Cleanup(func() { _ = listener.Close() })
|
||||
|
||||
serveErr := make(chan error, 1)
|
||||
go func() {
|
||||
serveErr <- server.ServeTTRPC(listener)
|
||||
}()
|
||||
|
||||
conn, err := net.Dial("tcp", listener.Addr().String())
|
||||
assert.NilError(t, err)
|
||||
client := ttrpc.NewClient(conn)
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
callErr := make(chan error, 1)
|
||||
go func() {
|
||||
callErr <- client.Call(t.Context(), "test", "Wait", &emptypb.Empty{}, &emptypb.Empty{})
|
||||
}()
|
||||
|
||||
deadline, cancel := context.WithTimeout(t.Context(), 5*time.Second)
|
||||
defer cancel()
|
||||
select {
|
||||
case <-requestStarted:
|
||||
case <-deadline.Done():
|
||||
t.Fatal("timed out waiting for ttrpc request to start")
|
||||
}
|
||||
|
||||
server.Stop()
|
||||
|
||||
select {
|
||||
case <-requestCanceled:
|
||||
case <-deadline.Done():
|
||||
t.Fatal("timed out waiting for ttrpc request cancellation")
|
||||
}
|
||||
select {
|
||||
case err := <-serveErr:
|
||||
assert.NilError(t, err)
|
||||
case <-deadline.Done():
|
||||
t.Fatal("timed out waiting for ttrpc server to stop")
|
||||
}
|
||||
select {
|
||||
case <-callErr:
|
||||
case <-deadline.Done():
|
||||
t.Fatal("timed out waiting for ttrpc client call to return")
|
||||
}
|
||||
}
|
||||
34
daemon/internal/containerd/server/embedded/embedded.go
Normal file
34
daemon/internal/containerd/server/embedded/embedded.go
Normal file
@@ -0,0 +1,34 @@
|
||||
//go:build !no_embedded_containerd
|
||||
|
||||
// Package embedded runs containerd's full gRPC server inside the dockerd
|
||||
// process.
|
||||
//
|
||||
// The same API is served on two endpoints. One is a unix socket (a named pipe
|
||||
// on Windows) in the daemon's exec-root, used by the plugin executor and by
|
||||
// tooling such as ctr. The other is an in-memory pipe, used by dockerd's own
|
||||
// client to avoid socket syscalls.
|
||||
//
|
||||
// containerd still runs each container's shim as a separate process, so
|
||||
// containers keep running across a daemon restart, as they do today.
|
||||
package embedded
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Daemon is an in-process containerd server.
|
||||
type Daemon interface {
|
||||
// Address returns the containerd gRPC address (unix socket path, or named
|
||||
// pipe on Windows) external clients and tooling should dial.
|
||||
Address() string
|
||||
// Dial returns an in-memory connection to the server, which dockerd's own
|
||||
// client uses to avoid socket syscalls. The signature matches
|
||||
// grpc.WithContextDialer, and the addr argument is ignored.
|
||||
Dial(ctx context.Context, addr string) (net.Conn, error)
|
||||
// WaitTimeout waits up to d for the server to stop after Shutdown.
|
||||
WaitTimeout(d time.Duration) error
|
||||
// Shutdown gracefully stops the in-process server and waits for it to exit.
|
||||
Shutdown(ctx context.Context) error
|
||||
}
|
||||
35
daemon/internal/containerd/server/embedded/embedded_linux.go
Normal file
35
daemon/internal/containerd/server/embedded/embedded_linux.go
Normal file
@@ -0,0 +1,35 @@
|
||||
//go:build !no_embedded_containerd
|
||||
|
||||
package embedded
|
||||
|
||||
import (
|
||||
"net"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containerd/ttrpc"
|
||||
"github.com/docker/go-connections/sockets"
|
||||
|
||||
// Linux-specific containerd plugin registrations: the overlayfs
|
||||
// snapshotter, the walking differ, the cgroups task monitor (for container
|
||||
// metrics), and the runc runtime options type. Cross-platform plugins are
|
||||
// registered in server.go.
|
||||
_ "github.com/containerd/containerd/api/types/runc/options"
|
||||
_ "github.com/containerd/containerd/v2/core/metrics/cgroups"
|
||||
_ "github.com/containerd/containerd/v2/plugins/diff/walking/plugin"
|
||||
_ "github.com/containerd/containerd/v2/plugins/snapshots/native/plugin"
|
||||
_ "github.com/containerd/containerd/v2/plugins/snapshots/overlay/plugin"
|
||||
)
|
||||
|
||||
func defaultAddress(stateDir string) string {
|
||||
return filepath.Join(stateDir, "containerd.sock")
|
||||
}
|
||||
|
||||
func listen(address string) (net.Listener, error) {
|
||||
return sockets.NewUnixSocketWithOpts(address, sockets.WithChmod(0o660))
|
||||
}
|
||||
|
||||
func newTTRPCServer() (*ttrpc.Server, error) {
|
||||
return ttrpc.NewServer(
|
||||
ttrpc.WithServerHandshaker(ttrpc.UnixSocketRequireSameUser()),
|
||||
)
|
||||
}
|
||||
42
daemon/internal/containerd/server/embedded/embedded_test.go
Normal file
42
daemon/internal/containerd/server/embedded/embedded_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
//go:build (linux || windows) && !no_embedded_containerd
|
||||
|
||||
package embedded
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
// TestPluginGraphResolves guards the blank-import plugin set: it starts the
|
||||
// embedded containerd server and fails if any required plugin is missing from
|
||||
// the registry or its dependency graph cannot be satisfied. Runtime init
|
||||
// failures unrelated to registration (e.g. requiring root) are tolerated, so
|
||||
// the test is safe to run unprivileged.
|
||||
func TestPluginGraphResolves(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
d, err := Start(ctx, t.TempDir(), t.TempDir())
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "not found") || strings.Contains(err.Error(), "no plugins registered") {
|
||||
t.Fatalf("embedded containerd plugin graph is incomplete: %v", err)
|
||||
}
|
||||
t.Skipf("embedded containerd did not start in this environment: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = d.Shutdown(ctx) })
|
||||
}
|
||||
|
||||
func TestBuildServerConfigUsesSupervisedLayout(t *testing.T) {
|
||||
rootDir := filepath.Join(t.TempDir(), "root")
|
||||
stateDir := filepath.Join(t.TempDir(), "state")
|
||||
address := defaultAddress(stateDir)
|
||||
|
||||
cfg := buildServerConfig(rootDir, stateDir, address)
|
||||
|
||||
assert.Check(t, is.Equal(cfg.root, rootDir))
|
||||
assert.Check(t, is.Equal(cfg.state, filepath.Join(stateDir, "daemon")))
|
||||
assert.Check(t, is.Equal(cfg.grpcAddress, address))
|
||||
assert.Check(t, is.Equal(cfg.ttrpcAddress, address+".ttrpc"))
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//go:build !no_embedded_containerd
|
||||
|
||||
package embedded
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
"github.com/containerd/ttrpc"
|
||||
|
||||
// Windows-specific containerd plugin registrations. Cross-platform plugins
|
||||
// are registered in server.go.
|
||||
_ "github.com/containerd/containerd/v2/plugins/diff/lcow"
|
||||
_ "github.com/containerd/containerd/v2/plugins/diff/windows"
|
||||
_ "github.com/containerd/containerd/v2/plugins/snapshots/windows"
|
||||
)
|
||||
|
||||
func defaultAddress(stateDir string) string {
|
||||
return `\\.\pipe\docker-containerd-embedded-` + stateDirID(stateDir)
|
||||
}
|
||||
|
||||
// stateDirID returns a stable identifier for the daemon state directory.
|
||||
func stateDirID(stateDir string) string {
|
||||
sum := sha256.Sum256([]byte(filepath.Clean(stateDir)))
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
// namedPipePermissions grants full access only to built-in Administrators and
|
||||
// LocalSystem.
|
||||
const namedPipePermissions = "D:P(A;;GA;;;BA)(A;;GA;;;SY)"
|
||||
|
||||
func listen(address string) (net.Listener, error) {
|
||||
return winio.ListenPipe(address, &winio.PipeConfig{
|
||||
SecurityDescriptor: namedPipePermissions,
|
||||
})
|
||||
}
|
||||
|
||||
func newTTRPCServer() (*ttrpc.Server, error) {
|
||||
return ttrpc.NewServer()
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
//go:build windows && !no_embedded_containerd
|
||||
|
||||
package embedded
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestDefaultAddressUsesStateDir(t *testing.T) {
|
||||
stateDir := `C:\ProgramData\docker\execroot\daemon-1\containerd`
|
||||
|
||||
address := defaultAddress(stateDir)
|
||||
assert.Check(t, is.Equal(address, defaultAddress(stateDir)))
|
||||
assert.Check(t, strings.HasPrefix(address, `\\.\pipe\docker-containerd-embedded-`))
|
||||
assert.Check(t, address != defaultAddress(`C:\ProgramData\docker\execroot\daemon-2\containerd`))
|
||||
}
|
||||
243
daemon/internal/containerd/server/embedded/server.go
Normal file
243
daemon/internal/containerd/server/embedded/server.go
Normal file
@@ -0,0 +1,243 @@
|
||||
//go:build (linux || windows) && !no_embedded_containerd
|
||||
|
||||
package embedded
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/v2/defaults"
|
||||
"github.com/containerd/containerd/v2/version"
|
||||
"github.com/containerd/log"
|
||||
|
||||
// Cross-platform plugin registrations. Each blank import registers one or
|
||||
// more containerd plugins that the local bootstrap then loads. Platform-specific
|
||||
// snapshotters and differs are registered in embedded_linux.go /
|
||||
// embedded_windows.go.
|
||||
//
|
||||
// This is a trimmed subset of containerd's cmd/containerd/builtins: dockerd
|
||||
// does not need CRI, sandbox, streaming, transfer, NRI, or the restart
|
||||
// monitor.
|
||||
_ "github.com/containerd/containerd/v2/core/runtime/v2"
|
||||
_ "github.com/containerd/containerd/v2/plugins/content/local/plugin"
|
||||
_ "github.com/containerd/containerd/v2/plugins/events"
|
||||
_ "github.com/containerd/containerd/v2/plugins/gc"
|
||||
_ "github.com/containerd/containerd/v2/plugins/leases"
|
||||
_ "github.com/containerd/containerd/v2/plugins/metadata"
|
||||
_ "github.com/containerd/containerd/v2/plugins/mount"
|
||||
_ "github.com/containerd/containerd/v2/plugins/services/containers"
|
||||
_ "github.com/containerd/containerd/v2/plugins/services/content"
|
||||
_ "github.com/containerd/containerd/v2/plugins/services/diff"
|
||||
_ "github.com/containerd/containerd/v2/plugins/services/events"
|
||||
_ "github.com/containerd/containerd/v2/plugins/services/healthcheck"
|
||||
_ "github.com/containerd/containerd/v2/plugins/services/images"
|
||||
_ "github.com/containerd/containerd/v2/plugins/services/introspection"
|
||||
_ "github.com/containerd/containerd/v2/plugins/services/leases"
|
||||
_ "github.com/containerd/containerd/v2/plugins/services/namespaces"
|
||||
_ "github.com/containerd/containerd/v2/plugins/services/snapshots"
|
||||
_ "github.com/containerd/containerd/v2/plugins/services/tasks"
|
||||
_ "github.com/containerd/containerd/v2/plugins/services/version"
|
||||
_ "github.com/containerd/containerd/v2/plugins/services/warning"
|
||||
)
|
||||
|
||||
// Start initializes and runs the embedded containerd server, using rootDir for
|
||||
// persistent state (bolt DB, content store) and the daemon subdirectory under
|
||||
// stateDir for runtime state.
|
||||
// See the package doc for the transport layout.
|
||||
// Plugins self-register via the blank imports above and in the platform-specific
|
||||
// files.
|
||||
func Start(ctx context.Context, rootDir, stateDir string) (Daemon, error) {
|
||||
setContainerdVersion()
|
||||
|
||||
address := defaultAddress(stateDir)
|
||||
cfg := buildServerConfig(rootDir, stateDir, address)
|
||||
|
||||
// Create the root and state directories with the permissions containerd
|
||||
// expects (e.g. the state dir at 0o711 for userns-remapped containers),
|
||||
// matching what containerd's own command does before initializing plugins.
|
||||
if err := createTopLevelDirectories(cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.G(ctx).WithField("address", address).Info("starting embedded containerd server")
|
||||
srv, err := newServer(ctx, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
socket, err := listen(cfg.grpcAddress)
|
||||
if err != nil {
|
||||
srv.Stop()
|
||||
return nil, err
|
||||
}
|
||||
// Shims (separate processes) publish task events back to containerd over
|
||||
// ttrpc, so it must be a real socket. The runtime plugin hands its address
|
||||
// (PropertyTTRPCAddress) to each shim.
|
||||
ttrpcL, err := listen(cfg.ttrpcAddress)
|
||||
if err != nil {
|
||||
srv.Stop()
|
||||
_ = socket.Close()
|
||||
return nil, err
|
||||
}
|
||||
inMemory := newInMemoryListener()
|
||||
|
||||
e := &embeddedDaemon{srv: srv, address: address, inMemory: inMemory, ttrpcL: ttrpcL, stopCh: make(chan struct{})}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
serve := func(name string, l net.Listener, fn func(net.Listener) error) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Serve returns an error once the server is stopped, which is
|
||||
// expected during Shutdown, so only log unexpected failures.
|
||||
if err := fn(l); err != nil && !e.stopping.Load() {
|
||||
log.G(ctx).WithError(err).Errorf("embedded containerd %s server exited", name)
|
||||
}
|
||||
}()
|
||||
}
|
||||
serve("gRPC socket", socket, srv.ServeGRPC)
|
||||
serve("gRPC in-memory", inMemory, srv.ServeGRPC)
|
||||
serve("ttrpc", ttrpcL, srv.ServeTTRPC)
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(e.stopCh)
|
||||
}()
|
||||
|
||||
// Tie the server to the daemon context: shut down when it is cancelled,
|
||||
// leaving running shims untouched. WithoutCancel so Shutdown can still wait
|
||||
// for the server to stop after ctx is already done.
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
if err := e.Shutdown(context.WithoutCancel(ctx)); err != nil {
|
||||
log.G(ctx).WithError(err).Error("failed to shut down embedded containerd")
|
||||
}
|
||||
}()
|
||||
|
||||
// Do not report successful startup until asynchronous plugin initialization
|
||||
// has completed.
|
||||
srv.Wait()
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// setContainerdVersion makes the embedded server report the vendored containerd
|
||||
// version in "docker info" instead of the source default ("2.2.4+unknown").
|
||||
//
|
||||
// The version comes from the build's module info. The git commit is not
|
||||
// recorded there, so only the version is set.
|
||||
func setContainerdVersion() {
|
||||
bi, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
for _, dep := range bi.Deps {
|
||||
if dep.Path == "github.com/containerd/containerd/v2" && dep.Version != "" {
|
||||
version.Version = dep.Version
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildServerConfig(rootDir, stateDir, address string) *serverConfig {
|
||||
return &serverConfig{
|
||||
root: rootDir,
|
||||
state: filepath.Join(stateDir, "daemon"),
|
||||
grpcAddress: address,
|
||||
ttrpcAddress: address + ".ttrpc",
|
||||
maxRecvMessageSize: defaults.DefaultMaxRecvMsgSize,
|
||||
maxSendMessageSize: defaults.DefaultMaxSendMsgSize,
|
||||
}
|
||||
}
|
||||
|
||||
type embeddedDaemon struct {
|
||||
srv *containerdServer
|
||||
address string
|
||||
inMemory *inMemoryListener
|
||||
ttrpcL net.Listener
|
||||
stopCh chan struct{}
|
||||
stopping atomic.Bool
|
||||
}
|
||||
|
||||
func (e *embeddedDaemon) Address() string {
|
||||
return e.address
|
||||
}
|
||||
|
||||
// Dial returns one end of an in-memory pipe connected to the gRPC server. The
|
||||
// addr argument is ignored, and only present to satisfy grpc.WithContextDialer.
|
||||
func (e *embeddedDaemon) Dial(ctx context.Context, _ string) (net.Conn, error) {
|
||||
serverConn, clientConn := net.Pipe()
|
||||
select {
|
||||
case e.inMemory.ch <- serverConn:
|
||||
return clientConn, nil
|
||||
case <-ctx.Done():
|
||||
_ = serverConn.Close()
|
||||
_ = clientConn.Close()
|
||||
return nil, ctx.Err()
|
||||
case <-e.inMemory.done:
|
||||
_ = serverConn.Close()
|
||||
_ = clientConn.Close()
|
||||
return nil, errors.New("embedded containerd server is stopped")
|
||||
}
|
||||
}
|
||||
|
||||
func (e *embeddedDaemon) WaitTimeout(d time.Duration) error {
|
||||
timer := time.NewTimer(d)
|
||||
defer timer.Stop()
|
||||
select {
|
||||
case <-timer.C:
|
||||
return errors.New("timeout waiting for embedded containerd to stop")
|
||||
case <-e.stopCh:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (e *embeddedDaemon) Shutdown(ctx context.Context) error {
|
||||
e.stopping.Store(true)
|
||||
// Stop closes both RPC servers and their listeners.
|
||||
e.srv.Stop()
|
||||
e.inMemory.Close()
|
||||
select {
|
||||
case <-e.stopCh:
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// inMemoryListener is a net.Listener whose connections are supplied by
|
||||
// Dial rather than accepted from the kernel.
|
||||
type inMemoryListener struct {
|
||||
ch chan net.Conn
|
||||
done chan struct{}
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func newInMemoryListener() *inMemoryListener {
|
||||
return &inMemoryListener{
|
||||
ch: make(chan net.Conn, 16),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *inMemoryListener) Accept() (net.Conn, error) {
|
||||
select {
|
||||
case conn := <-l.ch:
|
||||
return conn, nil
|
||||
case <-l.done:
|
||||
return nil, net.ErrClosed
|
||||
}
|
||||
}
|
||||
|
||||
func (l *inMemoryListener) Close() error {
|
||||
l.once.Do(func() { close(l.done) })
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *inMemoryListener) Addr() net.Addr {
|
||||
return &net.UnixAddr{Name: "embedded-containerd", Net: "inmem"}
|
||||
}
|
||||
@@ -61,12 +61,14 @@ type Daemon interface {
|
||||
// DaemonOpt allows to configure parameters of container daemons
|
||||
type DaemonOpt func(c *remote) error
|
||||
|
||||
// Start starts a containerd daemon and monitors it
|
||||
// Start starts a containerd daemon and monitors it.
|
||||
// It uses rootDir for persistent state and the daemon subdirectory under
|
||||
// stateDir for runtime state.
|
||||
func Start(ctx context.Context, rootDir, stateDir string, opts ...DaemonOpt) (Daemon, error) {
|
||||
r := &remote{
|
||||
Config: config.Config{
|
||||
Version: 2, // FIXME(thaJeztah): update to v3 when we drop support for containerd v1.
|
||||
Root: filepath.Join(rootDir, "daemon"),
|
||||
Root: rootDir,
|
||||
State: filepath.Join(stateDir, "daemon"),
|
||||
GRPC: config.GRPCConfig{ //nolint:staticcheck // Deprecated in config v4, but required for config v3.
|
||||
Address: defaultGRPCAddress(stateDir), //nolint:staticcheck // Deprecated in config v4, but required for config v3.
|
||||
16
go.mod
16
go.mod
@@ -32,8 +32,10 @@ require (
|
||||
github.com/containerd/errdefs v1.0.0
|
||||
github.com/containerd/fifo v1.1.0
|
||||
github.com/containerd/log v0.1.0
|
||||
github.com/containerd/nri v0.12.1
|
||||
github.com/containerd/nri v0.12.0
|
||||
github.com/containerd/platforms v1.0.0-rc.4
|
||||
github.com/containerd/plugin v1.1.0
|
||||
github.com/containerd/ttrpc v1.2.9
|
||||
github.com/containerd/typeurl/v2 v2.3.0
|
||||
github.com/coreos/go-systemd/v22 v22.7.0
|
||||
github.com/cpuguy83/tar2go v0.3.1
|
||||
@@ -154,6 +156,7 @@ require (
|
||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/checkpoint-restore/checkpointctl v1.5.0 // indirect
|
||||
github.com/cilium/ebpf v0.17.3 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/container-storage-interface/spec v1.5.0 // indirect
|
||||
@@ -163,9 +166,8 @@ require (
|
||||
github.com/containerd/go-cni v1.1.13 // indirect
|
||||
github.com/containerd/go-runc v1.1.0 // indirect
|
||||
github.com/containerd/nydus-snapshotter v0.15.15 // indirect
|
||||
github.com/containerd/plugin v1.1.0 // indirect
|
||||
github.com/containerd/otelttrpc v0.1.0 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect
|
||||
github.com/containerd/ttrpc v1.2.9 // indirect
|
||||
github.com/containernetworking/cni v1.3.0 // indirect
|
||||
github.com/containernetworking/plugins v1.9.1 // indirect
|
||||
github.com/coreos/go-semver v0.3.1 // indirect
|
||||
@@ -180,6 +182,7 @@ require (
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fernet/fernet-go v0.0.0-20240119011108-303da6aec611 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/analysis v0.25.2 // indirect
|
||||
@@ -230,9 +233,12 @@ require (
|
||||
github.com/hiddeco/sshsig v0.2.0 // indirect
|
||||
github.com/in-toto/attestation v1.2.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/intel/goresctrl v0.12.0 // indirect
|
||||
github.com/jmoiron/sqlx v1.4.0 // indirect
|
||||
github.com/klauspost/compress v1.19.1 // indirect
|
||||
github.com/knqyf263/go-plugin v0.9.0 // indirect
|
||||
github.com/mdlayher/socket v0.5.1 // indirect
|
||||
github.com/mdlayher/vsock v1.2.1 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/sys/capability v0.4.0 // indirect
|
||||
github.com/morikuni/aec v1.1.0 // indirect
|
||||
@@ -271,6 +277,7 @@ require (
|
||||
github.com/transparency-dev/formats v0.1.1 // indirect
|
||||
github.com/transparency-dev/merkle v0.0.2 // indirect
|
||||
github.com/weppos/publicsuffix-go v0.30.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
github.com/zmap/zcrypto v0.0.0-20230310154051-c8b263fd8300 // indirect
|
||||
github.com/zmap/zlint/v3 v3.5.0 // indirect
|
||||
@@ -303,8 +310,11 @@ require (
|
||||
google.golang.org/api v0.283.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20260526163538-3dc84a4a5aaa // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/apimachinery v0.36.0 // indirect
|
||||
k8s.io/klog/v2 v2.140.0 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
tags.cncf.io/container-device-interface/specs-go v1.1.0 // indirect
|
||||
)
|
||||
|
||||
24
go.sum
24
go.sum
@@ -135,6 +135,8 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/checkpoint-restore/checkpointctl v1.5.0 h1:Uu+D2cOf/GUyCMk23Y8L69P6YoATTe6pH+Au64O3y28=
|
||||
github.com/checkpoint-restore/checkpointctl v1.5.0/go.mod h1:y5HRs1ZWQUZGyEuthlTHmTJN9PUMOjlaH6JvVaNq9kE=
|
||||
github.com/cilium/ebpf v0.17.3 h1:FnP4r16PWYSE4ux6zN+//jMcW4nMVRvuTLVTvCjyyjg=
|
||||
github.com/cilium/ebpf v0.17.3/go.mod h1:G5EDHij8yiLzaqn0WjyfJHvRa+3aDlReIaLVRMvOyJk=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
@@ -177,10 +179,12 @@ github.com/containerd/go-runc v1.1.0 h1:OX4f+/i2y5sUT7LhmcJH7GYrjjhHa1QI4e8yO0gG
|
||||
github.com/containerd/go-runc v1.1.0/go.mod h1:xJv2hFF7GvHtTJd9JqTS2UVxMkULUYw4JN5XAUZqH5U=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/nri v0.12.1 h1:Nkp14W/mdhP0ze83ja/O437du6NQA33W5Y0O+ar3aKM=
|
||||
github.com/containerd/nri v0.12.1/go.mod h1:TGAfPLH4a+qwbv0PxsefPiR+PobYecDj2aXMtz7GQcg=
|
||||
github.com/containerd/nri v0.12.0 h1:RvZtyCM64XOB1UmMAFOlfReTTwCd+hE2IEQ5XEpkKA0=
|
||||
github.com/containerd/nri v0.12.0/go.mod h1:TGAfPLH4a+qwbv0PxsefPiR+PobYecDj2aXMtz7GQcg=
|
||||
github.com/containerd/nydus-snapshotter v0.15.15 h1:kVYbFpYA4K43qxGVoc/VBwRXLAVWn4X9mdwGrR+HsLk=
|
||||
github.com/containerd/nydus-snapshotter v0.15.15/go.mod h1:L96yO+4iE6qqDiqXKhxMXBoPeaE7JgzXir9yanUVuOY=
|
||||
github.com/containerd/otelttrpc v0.1.0 h1:UOX68eVTE8H/T45JveIg+I22Ev2aFj4qPITCmXsskjw=
|
||||
github.com/containerd/otelttrpc v0.1.0/go.mod h1:XhoA2VvaGPW1clB2ULwrBZfXVuEWuyOd2NUD1IM0yTg=
|
||||
github.com/containerd/platforms v1.0.0-rc.4 h1:M42JrUT4zfZTqtkUwkr0GzmUWbfyO5VO0Q5b3op97T4=
|
||||
github.com/containerd/platforms v1.0.0-rc.4/go.mod h1:lKlMXyLybmBedS/JJm11uDofzI8L2v0J2ZbYvNsbq1A=
|
||||
github.com/containerd/plugin v1.1.0 h1:O+7lczNJVMy8rz0YNx3xGB8tTf5qY4i5abF041Ew19U=
|
||||
@@ -263,6 +267,8 @@ github.com/fluent/fluent-logger-golang v1.10.1/go.mod h1:qOuXG4ZMrXaSTk12ua+uAb2
|
||||
github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||
github.com/go-chi/chi/v5 v5.3.0 h1:halUjDxhshgXHMrao5bB8eNBXo/rnzwr8m5m36glehM=
|
||||
github.com/go-chi/chi/v5 v5.3.0/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto=
|
||||
@@ -473,6 +479,8 @@ github.com/in-toto/in-toto-golang v0.11.0/go.mod h1:u3PjTnwFKjp5a1YCcw8SJg0G+tMe
|
||||
github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/intel/goresctrl v0.12.0 h1:F44m7jiVgOdqWfTTWaREF+5HTeX3i06qhvpuzpnrBko=
|
||||
github.com/intel/goresctrl v0.12.0/go.mod h1:5GWtmPY4BWl/a9rU8apGED9Xul5b5WoLtg/qOWaghWU=
|
||||
github.com/ishidawataru/sctp v0.0.0-20251114114122-19ddcbc6aae2 h1:36qep4gxKs+JgeHGWeQ040RyZdt9kQlLglL1rFVn/oQ=
|
||||
github.com/ishidawataru/sctp v0.0.0-20251114114122-19ddcbc6aae2/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg=
|
||||
github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b h1:ZGiXF8sz7PDk6RgkP+A/SFfUD0ZR/AgG6SpRNEDKZy8=
|
||||
@@ -520,6 +528,8 @@ github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/
|
||||
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
||||
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
|
||||
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
||||
github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ=
|
||||
github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE=
|
||||
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||
github.com/mistifyio/go-zfs/v4 v4.0.0 h1:sU0+5dX45tdDK5xNZ3HBi95nxUc48FS92qbIZEvpAg4=
|
||||
@@ -788,6 +798,8 @@ github.com/weppos/publicsuffix-go v0.13.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8L
|
||||
github.com/weppos/publicsuffix-go v0.30.0 h1:QHPZ2GRu/YE7cvejH9iyavPOkVCB4dNxp2ZvtT+vQLY=
|
||||
github.com/weppos/publicsuffix-go v0.30.0/go.mod h1:kBi8zwYnR0zrbm8RcuN1o9Fzgpnnn+btVN8uWPMyXAY=
|
||||
github.com/weppos/publicsuffix-go/publicsuffix/generator v0.0.0-20220927085643-dc0d00c92642/go.mod h1:GHfoeIdZLdZmLjMlzBftbTDntahTttUMWjxZwQJhULE=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
@@ -1039,6 +1051,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@@ -1053,6 +1067,8 @@ gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/apimachinery v0.36.0 h1:jZyPzhd5Z+3h9vJLt0z9XdzW9VzNzWAUw+P1xZ9PXtQ=
|
||||
k8s.io/apimachinery v0.36.0/go.mod h1:FklypaRJt6n5wUIwWXIP6GJlIpUizTgfo1T/As+Tyxc=
|
||||
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
|
||||
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
|
||||
kernel.org/pub/linux/libs/security/libcap/cap v1.2.78 h1:jgqg4gyu2BaYW9L6uzEtGLf8GNREwk/z4UFdwt5F3pE=
|
||||
@@ -1063,6 +1079,10 @@ pgregory.net/rapid v1.3.0 h1:vBvO0VSqti75J1jjYqpgPNBLKMd1+gxa9fYo7vk/Exc=
|
||||
pgregory.net/rapid v1.3.0/go.mod h1:dPlE4OBBxgXPqkP79flB6sJL1dx5azpI7HQ9MY9Z7uk=
|
||||
resenje.org/singleflight v0.4.3 h1:l7foFYg8X/VEHPxWs1K/Pw77807RMVzvXgWGb0J1sdM=
|
||||
resenje.org/singleflight v0.4.3/go.mod h1:lAgQK7VfjG6/pgredbQfmV0RvG/uVhKo6vSuZ0vCWfk=
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
|
||||
|
||||
@@ -143,8 +143,14 @@ func RegistryHosting() bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// RuntimeIsWindowsContainerd returns whether the containerd runtime is used on
|
||||
// Windows.
|
||||
// It is true when either the legacy DOCKER_WINDOWS_CONTAINERD_RUNTIME=1 env
|
||||
// var is set, or when the embedded-containerd feature is enabled via
|
||||
// TEST_INTEGRATION_CONTAINERD_EMBEDDED (which also uses containerd).
|
||||
func RuntimeIsWindowsContainerd() bool {
|
||||
return os.Getenv("DOCKER_WINDOWS_CONTAINERD_RUNTIME") == "1"
|
||||
return os.Getenv("DOCKER_WINDOWS_CONTAINERD_RUNTIME") == "1" ||
|
||||
(runtime.GOOS == "windows" && os.Getenv("TEST_INTEGRATION_CONTAINERD_EMBEDDED") != "")
|
||||
}
|
||||
|
||||
func SwarmInactive() bool {
|
||||
|
||||
@@ -56,6 +56,13 @@ const (
|
||||
|
||||
var errDaemonNotStarted = errors.New("daemon not started")
|
||||
|
||||
// containerdEmbeddedFromEnv reports whether test daemons should run containerd
|
||||
// in-process, via the embedded-containerd feature. It is controlled by
|
||||
// TEST_INTEGRATION_CONTAINERD_EMBEDDED.
|
||||
func containerdEmbeddedFromEnv() bool {
|
||||
return os.Getenv("TEST_INTEGRATION_CONTAINERD_EMBEDDED") != ""
|
||||
}
|
||||
|
||||
// SockRoot holds the path of the default docker integration daemon socket
|
||||
var SockRoot = filepath.Join(os.TempDir(), "docker-integration")
|
||||
|
||||
@@ -88,6 +95,7 @@ type Daemon struct {
|
||||
args []string
|
||||
extraEnv []string
|
||||
containerdSocket string
|
||||
containerdEmbedded bool
|
||||
usernsRemap string
|
||||
rootlessUser *user.User
|
||||
rootlessXDGRuntimeDir string
|
||||
@@ -142,12 +150,13 @@ func NewDaemon(workingDir string, ops ...Option) (*Daemon, error) {
|
||||
storageDriver: storageDriver,
|
||||
userlandProxy: userlandProxy,
|
||||
// dxr stands for docker-execroot (shortened for avoiding unix(7) path length limitation)
|
||||
execRoot: filepath.Join(os.TempDir(), "dxr", id),
|
||||
dockerdBinary: defaultDockerdBinary,
|
||||
swarmListenAddr: defaultSwarmListenAddr,
|
||||
SwarmPort: DefaultSwarmPort,
|
||||
log: nopLog{},
|
||||
containerdSocket: defaultContainerdSocket,
|
||||
execRoot: filepath.Join(os.TempDir(), "dxr", id),
|
||||
dockerdBinary: defaultDockerdBinary,
|
||||
swarmListenAddr: defaultSwarmListenAddr,
|
||||
SwarmPort: DefaultSwarmPort,
|
||||
log: nopLog{},
|
||||
containerdSocket: defaultContainerdSocket,
|
||||
containerdEmbedded: containerdEmbeddedFromEnv(),
|
||||
}
|
||||
|
||||
for _, op := range ops {
|
||||
@@ -537,7 +546,9 @@ func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error {
|
||||
"--containerd-namespace", d.id,
|
||||
"--containerd-plugins-namespace", d.id+"p",
|
||||
)
|
||||
if d.containerdSocket != "" {
|
||||
if d.containerdEmbedded {
|
||||
d.args = append(d.args, "--feature", "embedded-containerd")
|
||||
} else if d.containerdSocket != "" {
|
||||
d.args = append(d.args, "--containerd", d.containerdSocket)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -156,9 +157,13 @@ func (e *Execution) IsUserNamespace() bool {
|
||||
return root != ""
|
||||
}
|
||||
|
||||
// RuntimeIsWindowsContainerd returns whether containerd runtime is used on Windows
|
||||
// RuntimeIsWindowsContainerd returns whether the containerd runtime is used on Windows.
|
||||
// It is true when either the legacy DOCKER_WINDOWS_CONTAINERD_RUNTIME=1 env
|
||||
// var is set, or when the embedded-containerd feature is enabled via
|
||||
// TEST_INTEGRATION_CONTAINERD_EMBEDDED (which also uses containerd).
|
||||
func (e *Execution) RuntimeIsWindowsContainerd() bool {
|
||||
return os.Getenv("DOCKER_WINDOWS_CONTAINERD_RUNTIME") == "1"
|
||||
return os.Getenv("DOCKER_WINDOWS_CONTAINERD_RUNTIME") == "1" ||
|
||||
(runtime.GOOS == "windows" && os.Getenv("TEST_INTEGRATION_CONTAINERD_EMBEDDED") != "")
|
||||
}
|
||||
|
||||
// IsRootless returns whether the rootless mode is enabled
|
||||
|
||||
167
vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go
generated
vendored
Normal file
167
vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go
generated
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
//go:build windows
|
||||
|
||||
package security
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type (
|
||||
accessMask uint32
|
||||
accessMode uint32
|
||||
desiredAccess uint32
|
||||
inheritMode uint32
|
||||
objectType uint32
|
||||
shareMode uint32
|
||||
securityInformation uint32
|
||||
trusteeForm uint32
|
||||
trusteeType uint32
|
||||
|
||||
explicitAccess struct {
|
||||
accessPermissions accessMask
|
||||
accessMode accessMode
|
||||
inheritance inheritMode
|
||||
trustee trustee
|
||||
}
|
||||
|
||||
trustee struct {
|
||||
multipleTrustee *trustee
|
||||
multipleTrusteeOperation int32
|
||||
trusteeForm trusteeForm
|
||||
trusteeType trusteeType
|
||||
name uintptr
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
accessMaskDesiredPermission accessMask = 1 << 31 // GENERIC_READ
|
||||
|
||||
accessModeGrant accessMode = 1
|
||||
|
||||
desiredAccessReadControl desiredAccess = 0x20000
|
||||
desiredAccessWriteDac desiredAccess = 0x40000
|
||||
|
||||
//cspell:disable-next-line
|
||||
gvmga = "GrantVmGroupAccess:"
|
||||
|
||||
inheritModeNoInheritance inheritMode = 0x0
|
||||
inheritModeSubContainersAndObjectsInherit inheritMode = 0x3
|
||||
|
||||
objectTypeFileObject objectType = 0x1
|
||||
|
||||
securityInformationDACL securityInformation = 0x4
|
||||
|
||||
shareModeRead shareMode = 0x1
|
||||
shareModeWrite shareMode = 0x2
|
||||
|
||||
sidVMGroup = "S-1-5-83-0"
|
||||
|
||||
trusteeFormIsSID trusteeForm = 0
|
||||
|
||||
trusteeTypeWellKnownGroup trusteeType = 5
|
||||
)
|
||||
|
||||
// GrantVMGroupAccess sets the DACL for a specified file or directory to
|
||||
// include Grant ACE entries for the VM Group SID. This is a golang re-
|
||||
// implementation of the same function in vmcompute, just not exported in
|
||||
// RS5. Which kind of sucks. Sucks a lot :/
|
||||
//
|
||||
//revive:disable-next-line:var-naming VM, not Vm
|
||||
func GrantVmGroupAccess(name string) error {
|
||||
// Stat (to determine if `name` is a directory).
|
||||
s, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s os.Stat %s: %w", gvmga, name, err)
|
||||
}
|
||||
|
||||
// Get a handle to the file/directory. Must defer Close on success.
|
||||
fd, err := createFile(name, s.IsDir())
|
||||
if err != nil {
|
||||
return err // Already wrapped
|
||||
}
|
||||
defer windows.CloseHandle(fd) //nolint:errcheck
|
||||
|
||||
// Get the current DACL and Security Descriptor. Must defer LocalFree on success.
|
||||
ot := objectTypeFileObject
|
||||
si := securityInformationDACL
|
||||
sd := uintptr(0)
|
||||
origDACL := uintptr(0)
|
||||
if err := getSecurityInfo(fd, uint32(ot), uint32(si), nil, nil, &origDACL, nil, &sd); err != nil {
|
||||
return fmt.Errorf("%s GetSecurityInfo %s: %w", gvmga, name, err)
|
||||
}
|
||||
defer windows.LocalFree(windows.Handle(sd)) //nolint:errcheck
|
||||
|
||||
// Generate a new DACL which is the current DACL with the required ACEs added.
|
||||
// Must defer LocalFree on success.
|
||||
newDACL, err := generateDACLWithAcesAdded(name, s.IsDir(), origDACL)
|
||||
if err != nil {
|
||||
return err // Already wrapped
|
||||
}
|
||||
defer windows.LocalFree(windows.Handle(newDACL)) //nolint:errcheck
|
||||
|
||||
// And finally use SetSecurityInfo to apply the updated DACL.
|
||||
if err := setSecurityInfo(fd, uint32(ot), uint32(si), uintptr(0), uintptr(0), newDACL, uintptr(0)); err != nil {
|
||||
return fmt.Errorf("%s SetSecurityInfo %s: %w", gvmga, name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createFile is a helper function to call [Nt]CreateFile to get a handle to
|
||||
// the file or directory.
|
||||
func createFile(name string, isDir bool) (windows.Handle, error) {
|
||||
namep, err := windows.UTF16FromString(name)
|
||||
if err != nil {
|
||||
return windows.InvalidHandle, fmt.Errorf("could not convernt name to UTF-16: %w", err)
|
||||
}
|
||||
da := uint32(desiredAccessReadControl | desiredAccessWriteDac)
|
||||
sm := uint32(shareModeRead | shareModeWrite)
|
||||
fa := uint32(windows.FILE_ATTRIBUTE_NORMAL)
|
||||
if isDir {
|
||||
fa |= windows.FILE_FLAG_BACKUP_SEMANTICS
|
||||
}
|
||||
fd, err := windows.CreateFile(&namep[0], da, sm, nil, windows.OPEN_EXISTING, fa, 0)
|
||||
if err != nil {
|
||||
return windows.InvalidHandle, fmt.Errorf("%s windows.CreateFile %s: %w", gvmga, name, err)
|
||||
}
|
||||
return fd, nil
|
||||
}
|
||||
|
||||
// generateDACLWithAcesAdded generates a new DACL with the two needed ACEs added.
|
||||
// The caller is responsible for LocalFree of the returned DACL on success.
|
||||
func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintptr, error) {
|
||||
// Generate pointers to the SIDs based on the string SIDs
|
||||
sid, err := windows.StringToSid(sidVMGroup)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("%s windows.StringToSid %s %s: %w", gvmga, name, sidVMGroup, err)
|
||||
}
|
||||
|
||||
inheritance := inheritModeNoInheritance
|
||||
if isDir {
|
||||
inheritance = inheritModeSubContainersAndObjectsInherit
|
||||
}
|
||||
|
||||
eaArray := []explicitAccess{
|
||||
{
|
||||
accessPermissions: accessMaskDesiredPermission,
|
||||
accessMode: accessModeGrant,
|
||||
inheritance: inheritance,
|
||||
trustee: trustee{
|
||||
trusteeForm: trusteeFormIsSID,
|
||||
trusteeType: trusteeTypeWellKnownGroup,
|
||||
name: uintptr(unsafe.Pointer(sid)),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
modifiedDACL := uintptr(0)
|
||||
if err := setEntriesInAcl(uintptr(uint32(1)), uintptr(unsafe.Pointer(&eaArray[0])), origDACL, &modifiedDACL); err != nil {
|
||||
return 0, fmt.Errorf("%s SetEntriesInAcl %s: %w", gvmga, name, err)
|
||||
}
|
||||
|
||||
return modifiedDACL, nil
|
||||
}
|
||||
7
vendor/github.com/Microsoft/go-winio/pkg/security/syscall_windows.go
generated
vendored
Normal file
7
vendor/github.com/Microsoft/go-winio/pkg/security/syscall_windows.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package security
|
||||
|
||||
//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go syscall_windows.go
|
||||
|
||||
//sys getSecurityInfo(handle windows.Handle, objectType uint32, si uint32, ppsidOwner **uintptr, ppsidGroup **uintptr, ppDacl *uintptr, ppSacl *uintptr, ppSecurityDescriptor *uintptr) (win32err error) = advapi32.GetSecurityInfo
|
||||
//sys setSecurityInfo(handle windows.Handle, objectType uint32, si uint32, psidOwner uintptr, psidGroup uintptr, pDacl uintptr, pSacl uintptr) (win32err error) = advapi32.SetSecurityInfo
|
||||
//sys setEntriesInAcl(count uintptr, pListOfEEs uintptr, oldAcl uintptr, newAcl *uintptr) (win32err error) = advapi32.SetEntriesInAclW
|
||||
69
vendor/github.com/Microsoft/go-winio/pkg/security/zsyscall_windows.go
generated
vendored
Normal file
69
vendor/github.com/Microsoft/go-winio/pkg/security/zsyscall_windows.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
//go:build windows
|
||||
|
||||
// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
|
||||
|
||||
package security
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
errERROR_EINVAL error = syscall.EINVAL
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return errERROR_EINVAL
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||
|
||||
procGetSecurityInfo = modadvapi32.NewProc("GetSecurityInfo")
|
||||
procSetEntriesInAclW = modadvapi32.NewProc("SetEntriesInAclW")
|
||||
procSetSecurityInfo = modadvapi32.NewProc("SetSecurityInfo")
|
||||
)
|
||||
|
||||
func getSecurityInfo(handle windows.Handle, objectType uint32, si uint32, ppsidOwner **uintptr, ppsidGroup **uintptr, ppDacl *uintptr, ppSacl *uintptr, ppSecurityDescriptor *uintptr) (win32err error) {
|
||||
r0, _, _ := syscall.SyscallN(procGetSecurityInfo.Addr(), uintptr(handle), uintptr(objectType), uintptr(si), uintptr(unsafe.Pointer(ppsidOwner)), uintptr(unsafe.Pointer(ppsidGroup)), uintptr(unsafe.Pointer(ppDacl)), uintptr(unsafe.Pointer(ppSacl)), uintptr(unsafe.Pointer(ppSecurityDescriptor)))
|
||||
if r0 != 0 {
|
||||
win32err = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setEntriesInAcl(count uintptr, pListOfEEs uintptr, oldAcl uintptr, newAcl *uintptr) (win32err error) {
|
||||
r0, _, _ := syscall.SyscallN(procSetEntriesInAclW.Addr(), uintptr(count), uintptr(pListOfEEs), uintptr(oldAcl), uintptr(unsafe.Pointer(newAcl)))
|
||||
if r0 != 0 {
|
||||
win32err = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setSecurityInfo(handle windows.Handle, objectType uint32, si uint32, psidOwner uintptr, psidGroup uintptr, pDacl uintptr, pSacl uintptr) (win32err error) {
|
||||
r0, _, _ := syscall.SyscallN(procSetSecurityInfo.Addr(), uintptr(handle), uintptr(objectType), uintptr(si), uintptr(psidOwner), uintptr(psidGroup), uintptr(pDacl), uintptr(pSacl))
|
||||
if r0 != 0 {
|
||||
win32err = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
268
vendor/github.com/Microsoft/hcsshim/ext4/dmverity/dmverity.go
generated
vendored
Normal file
268
vendor/github.com/Microsoft/hcsshim/ext4/dmverity/dmverity.go
generated
vendored
Normal file
@@ -0,0 +1,268 @@
|
||||
package dmverity
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/Microsoft/hcsshim/ext4/internal/compactext4"
|
||||
"github.com/Microsoft/hcsshim/internal/memory"
|
||||
)
|
||||
|
||||
const (
|
||||
blockSize = compactext4.BlockSize
|
||||
// MerkleTreeBufioSize is a default buffer size to use with bufio.Reader
|
||||
MerkleTreeBufioSize = memory.MiB // 1MB
|
||||
// RecommendedVHDSizeGB is the recommended size in GB for VHDs, which is not a hard limit.
|
||||
RecommendedVHDSizeGB = 128 * memory.GiB
|
||||
// VeritySignature is a value written to dm-verity super-block.
|
||||
VeritySignature = "verity"
|
||||
)
|
||||
|
||||
var (
|
||||
salt = bytes.Repeat([]byte{0}, 32)
|
||||
sbSize = binary.Size(dmveritySuperblock{})
|
||||
)
|
||||
|
||||
var (
|
||||
ErrSuperBlockReadFailure = errors.New("failed to read dm-verity super block")
|
||||
ErrSuperBlockParseFailure = errors.New("failed to parse dm-verity super block")
|
||||
ErrRootHashReadFailure = errors.New("failed to read dm-verity root hash")
|
||||
ErrNotVeritySuperBlock = errors.New("invalid dm-verity super-block signature")
|
||||
)
|
||||
|
||||
type dmveritySuperblock struct {
|
||||
/* (0) "verity\0\0" */
|
||||
Signature [8]byte
|
||||
/* (8) superblock version, 1 */
|
||||
Version uint32
|
||||
/* (12) 0 - Chrome OS, 1 - normal */
|
||||
HashType uint32
|
||||
/* (16) UUID of hash device */
|
||||
UUID [16]byte
|
||||
/* (32) Name of the hash algorithm (e.g., sha256) */
|
||||
Algorithm [32]byte
|
||||
/* (64) The data block size in bytes */
|
||||
DataBlockSize uint32
|
||||
/* (68) The hash block size in bytes */
|
||||
HashBlockSize uint32
|
||||
/* (72) The number of data blocks */
|
||||
DataBlocks uint64
|
||||
/* (80) Size of the salt */
|
||||
SaltSize uint16
|
||||
/* (82) Padding */
|
||||
_ [6]byte
|
||||
/* (88) The salt */
|
||||
Salt [256]byte
|
||||
/* (344) Padding */
|
||||
_ [168]byte
|
||||
}
|
||||
|
||||
// VerityInfo is minimal exported version of dmveritySuperblock
|
||||
type VerityInfo struct {
|
||||
// Offset in blocks on hash device
|
||||
HashOffsetInBlocks int64
|
||||
// Set to true, when dm-verity super block is also written on the hash device
|
||||
SuperBlock bool
|
||||
RootDigest string
|
||||
Salt string
|
||||
Algorithm string
|
||||
DataBlockSize uint32
|
||||
HashBlockSize uint32
|
||||
DataBlocks uint64
|
||||
Version uint32
|
||||
}
|
||||
|
||||
// MerkleTree constructs dm-verity hash-tree for a given io.Reader with a fixed salt (0-byte) and algorithm (sha256).
|
||||
func MerkleTree(r io.Reader) ([]byte, error) {
|
||||
layers := make([][]byte, 0)
|
||||
currentLevel := r
|
||||
|
||||
for {
|
||||
nextLevel := bytes.NewBuffer(make([]byte, 0))
|
||||
for {
|
||||
block := make([]byte, blockSize)
|
||||
if _, err := io.ReadFull(currentLevel, block); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return nil, errors.Wrap(err, "failed to read data block")
|
||||
}
|
||||
h := hash2(salt, block)
|
||||
nextLevel.Write(h)
|
||||
}
|
||||
|
||||
if nextLevel.Len()%blockSize != 0 {
|
||||
padding := bytes.Repeat([]byte{0}, blockSize-(nextLevel.Len()%blockSize))
|
||||
nextLevel.Write(padding)
|
||||
}
|
||||
|
||||
layers = append(layers, nextLevel.Bytes())
|
||||
currentLevel = bufio.NewReaderSize(nextLevel, MerkleTreeBufioSize)
|
||||
|
||||
// This means that only root hash remains and our job is done
|
||||
if nextLevel.Len() == blockSize {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
tree := bytes.NewBuffer(make([]byte, 0))
|
||||
for i := len(layers) - 1; i >= 0; i-- {
|
||||
if _, err := tree.Write(layers[i]); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to write merkle tree")
|
||||
}
|
||||
}
|
||||
|
||||
return tree.Bytes(), nil
|
||||
}
|
||||
|
||||
// RootHash computes root hash of dm-verity hash-tree
|
||||
func RootHash(tree []byte) []byte {
|
||||
return hash2(salt, tree[:blockSize])
|
||||
}
|
||||
|
||||
// NewDMVeritySuperblock returns a dm-verity superblock for a device with a given size, salt, algorithm and versions are
|
||||
// fixed.
|
||||
func NewDMVeritySuperblock(size uint64) *dmveritySuperblock {
|
||||
superblock := &dmveritySuperblock{
|
||||
Version: 1,
|
||||
HashType: 1,
|
||||
UUID: generateUUID(),
|
||||
DataBlockSize: blockSize,
|
||||
HashBlockSize: blockSize,
|
||||
DataBlocks: size / blockSize,
|
||||
SaltSize: uint16(len(salt)),
|
||||
}
|
||||
|
||||
copy(superblock.Signature[:], VeritySignature)
|
||||
copy(superblock.Algorithm[:], "sha256")
|
||||
copy(superblock.Salt[:], salt)
|
||||
|
||||
return superblock
|
||||
}
|
||||
|
||||
func hash2(a, b []byte) []byte {
|
||||
h := sha256.New()
|
||||
h.Write(append(a, b...))
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
func generateUUID() [16]byte {
|
||||
res := [16]byte{}
|
||||
if _, err := rand.Read(res[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// ReadDMVerityInfo extracts dm-verity super block information and merkle tree root hash
|
||||
func ReadDMVerityInfo(vhdPath string, offsetInBytes int64) (*VerityInfo, error) {
|
||||
vhd, err := os.OpenFile(vhdPath, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer vhd.Close()
|
||||
|
||||
// Skip the ext4 data to get to dm-verity super block
|
||||
if s, err := vhd.Seek(offsetInBytes, io.SeekStart); err != nil || s != offsetInBytes {
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to seek dm-verity super block")
|
||||
}
|
||||
return nil, errors.Errorf("failed to seek dm-verity super block: expected bytes=%d, actual=%d", offsetInBytes, s)
|
||||
}
|
||||
|
||||
return ReadDMVerityInfoReader(vhd)
|
||||
}
|
||||
|
||||
func ReadDMVerityInfoReader(r io.Reader) (*VerityInfo, error) {
|
||||
block := make([]byte, blockSize)
|
||||
if s, err := r.Read(block); err != nil || s != blockSize {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %w", ErrSuperBlockReadFailure, err)
|
||||
}
|
||||
return nil, fmt.Errorf("unexpected bytes read expected=%d actual=%d: %w", blockSize, s, ErrSuperBlockReadFailure)
|
||||
}
|
||||
|
||||
dmvSB := &dmveritySuperblock{}
|
||||
b := bytes.NewBuffer(block)
|
||||
if err := binary.Read(b, binary.LittleEndian, dmvSB); err != nil {
|
||||
return nil, fmt.Errorf("%w: %w", ErrSuperBlockParseFailure, err)
|
||||
}
|
||||
|
||||
if string(bytes.Trim(dmvSB.Signature[:], "\x00")[:]) != VeritySignature {
|
||||
return nil, ErrNotVeritySuperBlock
|
||||
}
|
||||
|
||||
if s, err := r.Read(block); err != nil || s != blockSize {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %w", ErrRootHashReadFailure, err)
|
||||
}
|
||||
return nil, fmt.Errorf("unexpected bytes read expected=%d, actual=%d: %w", blockSize, s, ErrRootHashReadFailure)
|
||||
}
|
||||
|
||||
rootHash := hash2(dmvSB.Salt[:dmvSB.SaltSize], block)
|
||||
return &VerityInfo{
|
||||
RootDigest: fmt.Sprintf("%x", rootHash),
|
||||
Algorithm: string(bytes.Trim(dmvSB.Algorithm[:], "\x00")),
|
||||
Salt: fmt.Sprintf("%x", dmvSB.Salt[:dmvSB.SaltSize]),
|
||||
HashOffsetInBlocks: int64(dmvSB.DataBlocks),
|
||||
SuperBlock: true,
|
||||
DataBlocks: dmvSB.DataBlocks,
|
||||
DataBlockSize: dmvSB.DataBlockSize,
|
||||
HashBlockSize: blockSize,
|
||||
Version: dmvSB.Version,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ComputeAndWriteHashDevice builds merkle tree from a given io.ReadSeeker and
|
||||
// writes the result hash device (dm-verity super-block combined with merkle
|
||||
// tree) to io.Writer.
|
||||
func ComputeAndWriteHashDevice(r io.ReadSeeker, w io.Writer) error {
|
||||
// save current reader position
|
||||
currBytePos, err := r.Seek(0, io.SeekCurrent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// reset to the beginning to find the device size
|
||||
if _, err := r.Seek(0, io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tree, err := MerkleTree(r)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to build merkle tree")
|
||||
}
|
||||
|
||||
devSize, err := r.Seek(0, io.SeekEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// reset reader to initial position
|
||||
if _, err := r.Seek(currBytePos, io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dmVeritySB := NewDMVeritySuperblock(uint64(devSize))
|
||||
if err := binary.Write(w, binary.LittleEndian, dmVeritySB); err != nil {
|
||||
return errors.Wrap(err, "failed to write dm-verity super-block")
|
||||
}
|
||||
// write super-block padding
|
||||
padding := bytes.Repeat([]byte{0}, blockSize-(sbSize%blockSize))
|
||||
if _, err = w.Write(padding); err != nil {
|
||||
return err
|
||||
}
|
||||
// write tree
|
||||
if _, err := w.Write(tree); err != nil {
|
||||
return errors.Wrap(err, "failed to write merkle tree")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
1351
vendor/github.com/Microsoft/hcsshim/ext4/internal/compactext4/compact.go
generated
vendored
Normal file
1351
vendor/github.com/Microsoft/hcsshim/ext4/internal/compactext4/compact.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
411
vendor/github.com/Microsoft/hcsshim/ext4/internal/format/format.go
generated
vendored
Normal file
411
vendor/github.com/Microsoft/hcsshim/ext4/internal/format/format.go
generated
vendored
Normal file
@@ -0,0 +1,411 @@
|
||||
package format
|
||||
|
||||
type SuperBlock struct {
|
||||
InodesCount uint32
|
||||
BlocksCountLow uint32
|
||||
RootBlocksCountLow uint32
|
||||
FreeBlocksCountLow uint32
|
||||
FreeInodesCount uint32
|
||||
FirstDataBlock uint32
|
||||
LogBlockSize uint32
|
||||
LogClusterSize uint32
|
||||
BlocksPerGroup uint32
|
||||
ClustersPerGroup uint32
|
||||
InodesPerGroup uint32
|
||||
Mtime uint32
|
||||
Wtime uint32
|
||||
MountCount uint16
|
||||
MaxMountCount uint16
|
||||
Magic uint16
|
||||
State uint16
|
||||
Errors uint16
|
||||
MinorRevisionLevel uint16
|
||||
LastCheck uint32
|
||||
CheckInterval uint32
|
||||
CreatorOS uint32
|
||||
RevisionLevel uint32
|
||||
DefaultReservedUid uint16
|
||||
DefaultReservedGid uint16
|
||||
FirstInode uint32
|
||||
InodeSize uint16
|
||||
BlockGroupNr uint16
|
||||
FeatureCompat CompatFeature
|
||||
FeatureIncompat IncompatFeature
|
||||
FeatureRoCompat RoCompatFeature
|
||||
UUID [16]uint8
|
||||
VolumeName [16]byte
|
||||
LastMounted [64]byte
|
||||
AlgorithmUsageBitmap uint32
|
||||
PreallocBlocks uint8
|
||||
PreallocDirBlocks uint8
|
||||
ReservedGdtBlocks uint16
|
||||
JournalUUID [16]uint8
|
||||
JournalInum uint32
|
||||
JournalDev uint32
|
||||
LastOrphan uint32
|
||||
HashSeed [4]uint32
|
||||
DefHashVersion uint8
|
||||
JournalBackupType uint8
|
||||
DescSize uint16
|
||||
DefaultMountOpts uint32
|
||||
FirstMetaBg uint32
|
||||
MkfsTime uint32
|
||||
JournalBlocks [17]uint32
|
||||
BlocksCountHigh uint32
|
||||
RBlocksCountHigh uint32
|
||||
FreeBlocksCountHigh uint32
|
||||
MinExtraIsize uint16
|
||||
WantExtraIsize uint16
|
||||
Flags uint32
|
||||
RaidStride uint16
|
||||
MmpInterval uint16
|
||||
MmpBlock uint64
|
||||
RaidStripeWidth uint32
|
||||
LogGroupsPerFlex uint8
|
||||
ChecksumType uint8
|
||||
ReservedPad uint16
|
||||
KbytesWritten uint64
|
||||
SnapshotInum uint32
|
||||
SnapshotID uint32
|
||||
SnapshotRBlocksCount uint64
|
||||
SnapshotList uint32
|
||||
ErrorCount uint32
|
||||
FirstErrorTime uint32
|
||||
FirstErrorInode uint32
|
||||
FirstErrorBlock uint64
|
||||
FirstErrorFunc [32]uint8
|
||||
FirstErrorLine uint32
|
||||
LastErrorTime uint32
|
||||
LastErrorInode uint32
|
||||
LastErrorLine uint32
|
||||
LastErrorBlock uint64
|
||||
LastErrorFunc [32]uint8
|
||||
MountOpts [64]uint8
|
||||
UserQuotaInum uint32
|
||||
GroupQuotaInum uint32
|
||||
OverheadBlocks uint32
|
||||
BackupBgs [2]uint32
|
||||
EncryptAlgos [4]uint8
|
||||
EncryptPwSalt [16]uint8
|
||||
LpfInode uint32
|
||||
ProjectQuotaInum uint32
|
||||
ChecksumSeed uint32
|
||||
WtimeHigh uint8
|
||||
MtimeHigh uint8
|
||||
MkfsTimeHigh uint8
|
||||
LastcheckHigh uint8
|
||||
FirstErrorTimeHigh uint8
|
||||
LastErrorTimeHigh uint8
|
||||
Pad [2]uint8
|
||||
Reserved [96]uint32
|
||||
Checksum uint32
|
||||
}
|
||||
|
||||
const SuperBlockMagic uint16 = 0xef53
|
||||
|
||||
type CompatFeature uint32
|
||||
type IncompatFeature uint32
|
||||
type RoCompatFeature uint32
|
||||
|
||||
const (
|
||||
CompatDirPrealloc CompatFeature = 0x1
|
||||
CompatImagicInodes CompatFeature = 0x2
|
||||
CompatHasJournal CompatFeature = 0x4
|
||||
CompatExtAttr CompatFeature = 0x8
|
||||
CompatResizeInode CompatFeature = 0x10
|
||||
CompatDirIndex CompatFeature = 0x20
|
||||
CompatLazyBg CompatFeature = 0x40
|
||||
CompatExcludeInode CompatFeature = 0x80
|
||||
CompatExcludeBitmap CompatFeature = 0x100
|
||||
CompatSparseSuper2 CompatFeature = 0x200
|
||||
|
||||
IncompatCompression IncompatFeature = 0x1
|
||||
IncompatFiletype IncompatFeature = 0x2
|
||||
IncompatRecover IncompatFeature = 0x4
|
||||
IncompatJournalDev IncompatFeature = 0x8
|
||||
IncompatMetaBg IncompatFeature = 0x10
|
||||
IncompatExtents IncompatFeature = 0x40
|
||||
Incompat_64Bit IncompatFeature = 0x80
|
||||
IncompatMmp IncompatFeature = 0x100
|
||||
IncompatFlexBg IncompatFeature = 0x200
|
||||
IncompatEaInode IncompatFeature = 0x400
|
||||
IncompatDirdata IncompatFeature = 0x1000
|
||||
IncompatCsumSeed IncompatFeature = 0x2000
|
||||
IncompatLargedir IncompatFeature = 0x4000
|
||||
IncompatInlineData IncompatFeature = 0x8000
|
||||
IncompatEncrypt IncompatFeature = 0x10000
|
||||
|
||||
RoCompatSparseSuper RoCompatFeature = 0x1
|
||||
RoCompatLargeFile RoCompatFeature = 0x2
|
||||
RoCompatBtreeDir RoCompatFeature = 0x4
|
||||
RoCompatHugeFile RoCompatFeature = 0x8
|
||||
RoCompatGdtCsum RoCompatFeature = 0x10
|
||||
RoCompatDirNlink RoCompatFeature = 0x20
|
||||
RoCompatExtraIsize RoCompatFeature = 0x40
|
||||
RoCompatHasSnapshot RoCompatFeature = 0x80
|
||||
RoCompatQuota RoCompatFeature = 0x100
|
||||
RoCompatBigalloc RoCompatFeature = 0x200
|
||||
RoCompatMetadataCsum RoCompatFeature = 0x400
|
||||
RoCompatReplica RoCompatFeature = 0x800
|
||||
RoCompatReadonly RoCompatFeature = 0x1000
|
||||
RoCompatProject RoCompatFeature = 0x2000
|
||||
)
|
||||
|
||||
type BlockGroupFlag uint16
|
||||
|
||||
const (
|
||||
BlockGroupInodeUninit BlockGroupFlag = 0x1
|
||||
BlockGroupBlockUninit BlockGroupFlag = 0x2
|
||||
BlockGroupInodeZeroed BlockGroupFlag = 0x4
|
||||
)
|
||||
|
||||
type GroupDescriptor struct {
|
||||
BlockBitmapLow uint32
|
||||
InodeBitmapLow uint32
|
||||
InodeTableLow uint32
|
||||
FreeBlocksCountLow uint16
|
||||
FreeInodesCountLow uint16
|
||||
UsedDirsCountLow uint16
|
||||
Flags BlockGroupFlag
|
||||
ExcludeBitmapLow uint32
|
||||
BlockBitmapCsumLow uint16
|
||||
InodeBitmapCsumLow uint16
|
||||
ItableUnusedLow uint16
|
||||
Checksum uint16
|
||||
}
|
||||
|
||||
type GroupDescriptor64 struct {
|
||||
GroupDescriptor
|
||||
BlockBitmapHigh uint32
|
||||
InodeBitmapHigh uint32
|
||||
InodeTableHigh uint32
|
||||
FreeBlocksCountHigh uint16
|
||||
FreeInodesCountHigh uint16
|
||||
UsedDirsCountHigh uint16
|
||||
ItableUnusedHigh uint16
|
||||
ExcludeBitmapHigh uint32
|
||||
BlockBitmapCsumHigh uint16
|
||||
InodeBitmapCsumHigh uint16
|
||||
Reserved uint32
|
||||
}
|
||||
|
||||
const (
|
||||
S_IXOTH = 0x1
|
||||
S_IWOTH = 0x2
|
||||
S_IROTH = 0x4
|
||||
S_IXGRP = 0x8
|
||||
S_IWGRP = 0x10
|
||||
S_IRGRP = 0x20
|
||||
S_IXUSR = 0x40
|
||||
S_IWUSR = 0x80
|
||||
S_IRUSR = 0x100
|
||||
S_ISVTX = 0x200
|
||||
S_ISGID = 0x400
|
||||
S_ISUID = 0x800
|
||||
S_IFIFO = 0x1000
|
||||
S_IFCHR = 0x2000
|
||||
S_IFDIR = 0x4000
|
||||
S_IFBLK = 0x6000
|
||||
S_IFREG = 0x8000
|
||||
S_IFLNK = 0xA000
|
||||
S_IFSOCK = 0xC000
|
||||
|
||||
TypeMask uint16 = 0xF000
|
||||
)
|
||||
|
||||
type InodeNumber uint32
|
||||
|
||||
const (
|
||||
InodeRoot = 2
|
||||
)
|
||||
|
||||
type Inode struct {
|
||||
Mode uint16
|
||||
Uid uint16
|
||||
SizeLow uint32
|
||||
Atime uint32
|
||||
Ctime uint32
|
||||
Mtime uint32
|
||||
Dtime uint32
|
||||
Gid uint16
|
||||
LinksCount uint16
|
||||
BlocksLow uint32
|
||||
Flags InodeFlag
|
||||
Version uint32
|
||||
Block [60]byte
|
||||
Generation uint32
|
||||
XattrBlockLow uint32
|
||||
SizeHigh uint32
|
||||
ObsoleteFragmentAddr uint32
|
||||
BlocksHigh uint16
|
||||
XattrBlockHigh uint16
|
||||
UidHigh uint16
|
||||
GidHigh uint16
|
||||
ChecksumLow uint16
|
||||
Reserved uint16
|
||||
ExtraIsize uint16
|
||||
ChecksumHigh uint16
|
||||
CtimeExtra uint32
|
||||
MtimeExtra uint32
|
||||
AtimeExtra uint32
|
||||
Crtime uint32
|
||||
CrtimeExtra uint32
|
||||
VersionHigh uint32
|
||||
Projid uint32
|
||||
}
|
||||
|
||||
type InodeFlag uint32
|
||||
|
||||
const (
|
||||
InodeFlagSecRm InodeFlag = 0x1
|
||||
InodeFlagUnRm InodeFlag = 0x2
|
||||
InodeFlagCompressed InodeFlag = 0x4
|
||||
InodeFlagSync InodeFlag = 0x8
|
||||
InodeFlagImmutable InodeFlag = 0x10
|
||||
InodeFlagAppend InodeFlag = 0x20
|
||||
InodeFlagNoDump InodeFlag = 0x40
|
||||
InodeFlagNoAtime InodeFlag = 0x80
|
||||
InodeFlagDirtyCompressed InodeFlag = 0x100
|
||||
InodeFlagCompressedClusters InodeFlag = 0x200
|
||||
InodeFlagNoCompress InodeFlag = 0x400
|
||||
InodeFlagEncrypted InodeFlag = 0x800
|
||||
InodeFlagHashedIndex InodeFlag = 0x1000
|
||||
InodeFlagMagic InodeFlag = 0x2000
|
||||
InodeFlagJournalData InodeFlag = 0x4000
|
||||
InodeFlagNoTail InodeFlag = 0x8000
|
||||
InodeFlagDirSync InodeFlag = 0x10000
|
||||
InodeFlagTopDir InodeFlag = 0x20000
|
||||
InodeFlagHugeFile InodeFlag = 0x40000
|
||||
InodeFlagExtents InodeFlag = 0x80000
|
||||
InodeFlagEaInode InodeFlag = 0x200000
|
||||
InodeFlagEOFBlocks InodeFlag = 0x400000
|
||||
InodeFlagSnapfile InodeFlag = 0x01000000
|
||||
InodeFlagSnapfileDeleted InodeFlag = 0x04000000
|
||||
InodeFlagSnapfileShrunk InodeFlag = 0x08000000
|
||||
InodeFlagInlineData InodeFlag = 0x10000000
|
||||
InodeFlagProjectIDInherit InodeFlag = 0x20000000
|
||||
InodeFlagReserved InodeFlag = 0x80000000
|
||||
)
|
||||
|
||||
const (
|
||||
MaxLinks = 65000
|
||||
)
|
||||
|
||||
type ExtentHeader struct {
|
||||
Magic uint16
|
||||
Entries uint16
|
||||
Max uint16
|
||||
Depth uint16
|
||||
Generation uint32
|
||||
}
|
||||
|
||||
const ExtentHeaderMagic uint16 = 0xf30a
|
||||
|
||||
type ExtentIndexNode struct {
|
||||
Block uint32
|
||||
LeafLow uint32
|
||||
LeafHigh uint16
|
||||
Unused uint16
|
||||
}
|
||||
|
||||
type ExtentLeafNode struct {
|
||||
Block uint32
|
||||
Length uint16
|
||||
StartHigh uint16
|
||||
StartLow uint32
|
||||
}
|
||||
|
||||
type ExtentTail struct {
|
||||
Checksum uint32
|
||||
}
|
||||
|
||||
type DirectoryEntry struct {
|
||||
Inode InodeNumber
|
||||
RecordLength uint16
|
||||
NameLength uint8
|
||||
FileType FileType
|
||||
//Name []byte
|
||||
}
|
||||
|
||||
type FileType uint8
|
||||
|
||||
const (
|
||||
FileTypeUnknown FileType = 0x0
|
||||
FileTypeRegular FileType = 0x1
|
||||
FileTypeDirectory FileType = 0x2
|
||||
FileTypeCharacter FileType = 0x3
|
||||
FileTypeBlock FileType = 0x4
|
||||
FileTypeFIFO FileType = 0x5
|
||||
FileTypeSocket FileType = 0x6
|
||||
FileTypeSymbolicLink FileType = 0x7
|
||||
)
|
||||
|
||||
type DirectoryEntryTail struct {
|
||||
ReservedZero1 uint32
|
||||
RecordLength uint16
|
||||
ReservedZero2 uint8
|
||||
FileType uint8
|
||||
Checksum uint32
|
||||
}
|
||||
|
||||
type DirectoryTreeRoot struct {
|
||||
Dot DirectoryEntry
|
||||
DotName [4]byte
|
||||
DotDot DirectoryEntry
|
||||
DotDotName [4]byte
|
||||
ReservedZero uint32
|
||||
HashVersion uint8
|
||||
InfoLength uint8
|
||||
IndirectLevels uint8
|
||||
UnusedFlags uint8
|
||||
Limit uint16
|
||||
Count uint16
|
||||
Block uint32
|
||||
//Entries []DirectoryTreeEntry
|
||||
}
|
||||
|
||||
type DirectoryTreeNode struct {
|
||||
FakeInode uint32
|
||||
FakeRecordLength uint16
|
||||
NameLength uint8
|
||||
FileType uint8
|
||||
Limit uint16
|
||||
Count uint16
|
||||
Block uint32
|
||||
//Entries []DirectoryTreeEntry
|
||||
}
|
||||
|
||||
type DirectoryTreeEntry struct {
|
||||
Hash uint32
|
||||
Block uint32
|
||||
}
|
||||
|
||||
type DirectoryTreeTail struct {
|
||||
Reserved uint32
|
||||
Checksum uint32
|
||||
}
|
||||
|
||||
type XAttrInodeBodyHeader struct {
|
||||
Magic uint32
|
||||
}
|
||||
|
||||
type XAttrHeader struct {
|
||||
Magic uint32
|
||||
ReferenceCount uint32
|
||||
Blocks uint32
|
||||
Hash uint32
|
||||
Checksum uint32
|
||||
Reserved [3]uint32
|
||||
}
|
||||
|
||||
const XAttrHeaderMagic uint32 = 0xea020000
|
||||
|
||||
type XAttrEntry struct {
|
||||
NameLength uint8
|
||||
NameIndex uint8
|
||||
ValueOffset uint16
|
||||
ValueInum uint32
|
||||
ValueSize uint32
|
||||
Hash uint32
|
||||
//Name []byte
|
||||
}
|
||||
369
vendor/github.com/Microsoft/hcsshim/ext4/tar2ext4/tar2ext4.go
generated
vendored
Normal file
369
vendor/github.com/Microsoft/hcsshim/ext4/tar2ext4/tar2ext4.go
generated
vendored
Normal file
@@ -0,0 +1,369 @@
|
||||
package tar2ext4
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/Microsoft/hcsshim/ext4/dmverity"
|
||||
"github.com/Microsoft/hcsshim/ext4/internal/compactext4"
|
||||
"github.com/Microsoft/hcsshim/ext4/internal/format"
|
||||
"github.com/Microsoft/hcsshim/internal/log"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type params struct {
|
||||
convertWhiteout bool
|
||||
convertBackslash bool
|
||||
appendVhdFooter bool
|
||||
onlyAppendVhdFooter bool
|
||||
appendDMVerity bool
|
||||
ext4opts []compactext4.Option
|
||||
}
|
||||
|
||||
// Option is the type for optional parameters to Convert.
|
||||
type Option func(*params)
|
||||
|
||||
// ConvertWhiteout instructs the converter to convert OCI-style whiteouts
|
||||
// (beginning with .wh.) to overlay-style whiteouts.
|
||||
func ConvertWhiteout(p *params) {
|
||||
p.convertWhiteout = true
|
||||
}
|
||||
|
||||
// ConvertBackslash instructs the converter to replace `\` in path names with `/`.
|
||||
// This is useful if the tar file was created on Windows, where `\` is the filepath separator.
|
||||
func ConvertBackslash(p *params) {
|
||||
p.convertBackslash = true
|
||||
}
|
||||
|
||||
// AppendVhdFooter instructs the converter to add a fixed VHD footer to the
|
||||
// file.
|
||||
func AppendVhdFooter(p *params) {
|
||||
p.appendVhdFooter = true
|
||||
}
|
||||
|
||||
// OnlyAppendVhdFooter instructs the converter not to convert but still to add a fixed VHD footer to the
|
||||
// file.
|
||||
func OnlyAppendVhdFooter(p *params) {
|
||||
p.onlyAppendVhdFooter = true
|
||||
}
|
||||
|
||||
// AppendDMVerity instructs the converter to add a dmverity Merkle tree for
|
||||
// the ext4 filesystem after the filesystem and before the optional VHD footer
|
||||
func AppendDMVerity(p *params) {
|
||||
p.appendDMVerity = true
|
||||
}
|
||||
|
||||
// InlineData instructs the converter to write small files into the inode
|
||||
// structures directly. This creates smaller images but currently is not
|
||||
// compatible with DAX.
|
||||
func InlineData(p *params) {
|
||||
p.ext4opts = append(p.ext4opts, compactext4.InlineData)
|
||||
}
|
||||
|
||||
// MaximumDiskSize instructs the writer to limit the disk size to the specified
|
||||
// value. This also reserves enough metadata space for the specified disk size.
|
||||
// If not provided, then 16GB is the default.
|
||||
func MaximumDiskSize(size int64) Option {
|
||||
return func(p *params) {
|
||||
p.ext4opts = append(p.ext4opts, compactext4.MaximumDiskSize(size))
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
whiteoutPrefix = ".wh."
|
||||
opaqueWhiteout = ".wh..wh..opq"
|
||||
)
|
||||
|
||||
// ConvertTarToExt4 writes a compact ext4 file system image that contains the files in the
|
||||
// input tar stream.
|
||||
func ConvertTarToExt4(r io.Reader, w io.ReadWriteSeeker, options ...Option) error {
|
||||
var p params
|
||||
for _, opt := range options {
|
||||
opt(&p)
|
||||
}
|
||||
|
||||
t := tar.NewReader(bufio.NewReader(r))
|
||||
fs := compactext4.NewWriter(w, p.ext4opts...)
|
||||
for {
|
||||
hdr, err := t.Next()
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name := hdr.Name
|
||||
linkName := hdr.Linkname
|
||||
if p.convertBackslash {
|
||||
// compactext assumes all paths are `/` separated
|
||||
// unconditionally replace all instances of `/`, regardless of GOOS
|
||||
name = strings.ReplaceAll(name, `\`, "/")
|
||||
linkName = strings.ReplaceAll(linkName, `\`, "/")
|
||||
}
|
||||
|
||||
if err = fs.MakeParents(name); err != nil {
|
||||
return errors.Wrapf(err, "failed to ensure parent directories for %s", name)
|
||||
}
|
||||
|
||||
if p.convertWhiteout {
|
||||
dir, file := path.Split(name)
|
||||
if strings.HasPrefix(file, whiteoutPrefix) {
|
||||
if file == opaqueWhiteout {
|
||||
// Update the directory with the appropriate xattr.
|
||||
f, err := fs.Stat(dir)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to stat parent directory of whiteout %s", file)
|
||||
}
|
||||
f.Xattrs["trusted.overlay.opaque"] = []byte("y")
|
||||
err = fs.Create(dir, f)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to create opaque dir %s", file)
|
||||
}
|
||||
} else {
|
||||
// Create an overlay-style whiteout.
|
||||
f := &compactext4.File{
|
||||
Mode: compactext4.S_IFCHR,
|
||||
Devmajor: 0,
|
||||
Devminor: 0,
|
||||
}
|
||||
err = fs.Create(path.Join(dir, file[len(whiteoutPrefix):]), f)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to create whiteout file for %s", file)
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if hdr.Typeflag == tar.TypeLink {
|
||||
err = fs.Link(linkName, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
f := &compactext4.File{
|
||||
Mode: uint16(hdr.Mode),
|
||||
Atime: hdr.AccessTime,
|
||||
Mtime: hdr.ModTime,
|
||||
Ctime: hdr.ChangeTime,
|
||||
Crtime: hdr.ModTime,
|
||||
Size: hdr.Size,
|
||||
Uid: uint32(hdr.Uid),
|
||||
Gid: uint32(hdr.Gid),
|
||||
Linkname: linkName,
|
||||
Devmajor: uint32(hdr.Devmajor),
|
||||
Devminor: uint32(hdr.Devminor),
|
||||
Xattrs: make(map[string][]byte),
|
||||
}
|
||||
for key, value := range hdr.PAXRecords {
|
||||
const xattrPrefix = "SCHILY.xattr."
|
||||
if strings.HasPrefix(key, xattrPrefix) {
|
||||
f.Xattrs[key[len(xattrPrefix):]] = []byte(value)
|
||||
}
|
||||
}
|
||||
|
||||
var typ uint16
|
||||
switch hdr.Typeflag {
|
||||
case tar.TypeReg:
|
||||
typ = compactext4.S_IFREG
|
||||
case tar.TypeSymlink:
|
||||
typ = compactext4.S_IFLNK
|
||||
case tar.TypeChar:
|
||||
typ = compactext4.S_IFCHR
|
||||
case tar.TypeBlock:
|
||||
typ = compactext4.S_IFBLK
|
||||
case tar.TypeDir:
|
||||
typ = compactext4.S_IFDIR
|
||||
case tar.TypeFifo:
|
||||
typ = compactext4.S_IFIFO
|
||||
}
|
||||
f.Mode &= ^compactext4.TypeMask
|
||||
f.Mode |= typ
|
||||
err = fs.Create(name, f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(fs, t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return fs.Close()
|
||||
}
|
||||
|
||||
// Convert wraps ConvertTarToExt4 and conditionally computes (and appends) the file image's cryptographic
|
||||
// hashes (merkle tree) or/and appends a VHD footer.
|
||||
func Convert(r io.Reader, w io.ReadWriteSeeker, options ...Option) error {
|
||||
var p params
|
||||
for _, opt := range options {
|
||||
opt(&p)
|
||||
}
|
||||
|
||||
if p.onlyAppendVhdFooter {
|
||||
_, err := io.Copy(w, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ConvertToVhd(w)
|
||||
}
|
||||
|
||||
if err := ConvertTarToExt4(r, w, options...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.appendDMVerity {
|
||||
if err := dmverity.ComputeAndWriteHashDevice(w, w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if p.appendVhdFooter {
|
||||
return ConvertToVhd(w)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadExt4SuperBlock reads and returns ext4 super block from given device.
|
||||
func ReadExt4SuperBlock(devicePath string) (*format.SuperBlock, error) {
|
||||
dev, err := os.OpenFile(devicePath, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer dev.Close()
|
||||
|
||||
return ReadExt4SuperBlockReadSeeker(dev)
|
||||
}
|
||||
|
||||
// ReadExt4SuperBlockReadSeeker reads and returns ext4 super block given
|
||||
// an io.ReadSeeker.
|
||||
//
|
||||
// The layout on disk is as follows:
|
||||
// | Group 0 padding | - 1024 bytes
|
||||
// | ext4 SuperBlock | - 1 block
|
||||
// | Group Descriptors | - many blocks
|
||||
// | Reserved GDT Blocks | - many blocks
|
||||
// | Data Block Bitmap | - 1 block
|
||||
// | inode Bitmap | - 1 block
|
||||
// | inode Table | - many blocks
|
||||
// | Data Blocks | - many blocks
|
||||
//
|
||||
// More details can be found here https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout
|
||||
//
|
||||
// Our goal is to skip the Group 0 padding, read and return the ext4 SuperBlock
|
||||
func ReadExt4SuperBlockReadSeeker(rsc io.ReadSeeker) (*format.SuperBlock, error) {
|
||||
// save current reader position
|
||||
currBytePos, err := rsc.Seek(0, io.SeekCurrent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := rsc.Seek(1024, io.SeekCurrent); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var sb format.SuperBlock
|
||||
if err := binary.Read(rsc, binary.LittleEndian, &sb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// reset the reader to initial position
|
||||
if _, err := rsc.Seek(currBytePos, io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sb.Magic != format.SuperBlockMagic {
|
||||
return nil, errors.New("not an ext4 file system")
|
||||
}
|
||||
return &sb, nil
|
||||
}
|
||||
|
||||
// IsDeviceExt4 is will read the device's superblock and determine if it is
|
||||
// and ext4 superblock.
|
||||
func IsDeviceExt4(devicePath string) bool {
|
||||
// ReadExt4SuperBlock will check the superblock magic number for us,
|
||||
// so we know if no error is returned, this is an ext4 device.
|
||||
_, err := ReadExt4SuperBlock(devicePath)
|
||||
if err != nil {
|
||||
log.L.Warnf("failed to read Ext4 superblock: %s", err)
|
||||
}
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Ext4FileSystemSize reads ext4 superblock and returns the size of the underlying
|
||||
// ext4 file system and its block size.
|
||||
func Ext4FileSystemSize(r io.ReadSeeker) (int64, int, error) {
|
||||
sb, err := ReadExt4SuperBlockReadSeeker(r)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("failed to read ext4 superblock: %w", err)
|
||||
}
|
||||
blockSize := 1024 * (1 << sb.LogBlockSize)
|
||||
fsSize := int64(blockSize) * int64(sb.BlocksCountLow)
|
||||
return fsSize, blockSize, nil
|
||||
}
|
||||
|
||||
// ConvertAndComputeRootDigest writes a compact ext4 file system image that contains the files in the
|
||||
// input tar stream, computes the resulting file image's cryptographic hashes (merkle tree) and returns
|
||||
// merkle tree root digest. Convert is called with minimal options: ConvertWhiteout and MaximumDiskSize
|
||||
// set to dmverity.RecommendedVHDSizeGB.
|
||||
func ConvertAndComputeRootDigest(r io.Reader) (string, error) {
|
||||
out, err := os.CreateTemp("", "")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create temporary file: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = os.Remove(out.Name())
|
||||
}()
|
||||
defer out.Close()
|
||||
|
||||
options := []Option{
|
||||
ConvertWhiteout,
|
||||
MaximumDiskSize(dmverity.RecommendedVHDSizeGB),
|
||||
}
|
||||
if err := ConvertTarToExt4(r, out, options...); err != nil {
|
||||
return "", fmt.Errorf("failed to convert tar to ext4: %w", err)
|
||||
}
|
||||
|
||||
if _, err := out.Seek(0, io.SeekStart); err != nil {
|
||||
return "", fmt.Errorf("failed to seek start on temp file when creating merkle tree: %w", err)
|
||||
}
|
||||
|
||||
tree, err := dmverity.MerkleTree(bufio.NewReaderSize(out, dmverity.MerkleTreeBufioSize))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create merkle tree: %w", err)
|
||||
}
|
||||
|
||||
hash := dmverity.RootHash(tree)
|
||||
return fmt.Sprintf("%x", hash), nil
|
||||
}
|
||||
|
||||
// ConvertToVhd converts given io.WriteSeeker to VHD, by appending the VHD footer with a fixed size.
|
||||
func ConvertToVhd(w io.WriteSeeker) error {
|
||||
size, err := w.Seek(0, io.SeekEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return binary.Write(w, binary.BigEndian, makeFixedVHDFooter(size))
|
||||
}
|
||||
|
||||
// A convenience wrapper for ConverToVhd, instead of asking the caller to open the file and pass an io.WriteSeeker, this
|
||||
// takes in a file path and appends the VHD footer to that file.
|
||||
func ConvertFileToVhd(filePath string) error {
|
||||
f, err := os.OpenFile(filePath, os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open file `%s` : %w", filePath, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := ConvertToVhd(f); err != nil {
|
||||
return fmt.Errorf("failed to append VHD footer: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
76
vendor/github.com/Microsoft/hcsshim/ext4/tar2ext4/vhdfooter.go
generated
vendored
Normal file
76
vendor/github.com/Microsoft/hcsshim/ext4/tar2ext4/vhdfooter.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
package tar2ext4
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// Constants for the VHD footer
|
||||
const (
|
||||
cookieMagic = "conectix"
|
||||
featureMask = 0x2
|
||||
fileFormatVersionMagic = 0x00010000
|
||||
fixedDataOffset = -1
|
||||
creatorVersionMagic = 0x000a0000
|
||||
diskTypeFixed = 2
|
||||
)
|
||||
|
||||
type vhdFooter struct {
|
||||
Cookie [8]byte
|
||||
Features uint32
|
||||
FileFormatVersion uint32
|
||||
DataOffset int64
|
||||
TimeStamp uint32
|
||||
CreatorApplication [4]byte
|
||||
CreatorVersion uint32
|
||||
CreatorHostOS [4]byte
|
||||
OriginalSize int64
|
||||
CurrentSize int64
|
||||
DiskGeometry uint32
|
||||
DiskType uint32
|
||||
Checksum uint32
|
||||
UniqueID [16]uint8
|
||||
SavedState uint8
|
||||
Reserved [427]uint8
|
||||
}
|
||||
|
||||
func makeFixedVHDFooter(size int64) *vhdFooter {
|
||||
footer := &vhdFooter{
|
||||
Features: featureMask,
|
||||
FileFormatVersion: fileFormatVersionMagic,
|
||||
DataOffset: fixedDataOffset,
|
||||
CreatorVersion: creatorVersionMagic,
|
||||
OriginalSize: size,
|
||||
CurrentSize: size,
|
||||
DiskType: diskTypeFixed,
|
||||
UniqueID: generateUUID(),
|
||||
}
|
||||
copy(footer.Cookie[:], cookieMagic)
|
||||
footer.Checksum = calculateCheckSum(footer)
|
||||
return footer
|
||||
}
|
||||
|
||||
func calculateCheckSum(footer *vhdFooter) uint32 {
|
||||
oldchk := footer.Checksum
|
||||
footer.Checksum = 0
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
_ = binary.Write(buf, binary.BigEndian, footer)
|
||||
|
||||
var chk uint32
|
||||
bufBytes := buf.Bytes()
|
||||
for i := 0; i < len(bufBytes); i++ {
|
||||
chk += uint32(bufBytes[i])
|
||||
}
|
||||
footer.Checksum = oldchk
|
||||
return uint32(^chk)
|
||||
}
|
||||
|
||||
func generateUUID() [16]byte {
|
||||
res := [16]byte{}
|
||||
if _, err := rand.Read(res[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
157
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/block_cim_writer.go
generated
vendored
Normal file
157
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/block_cim_writer.go
generated
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
//go:build windows
|
||||
|
||||
package cim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
"github.com/Microsoft/hcsshim/internal/log"
|
||||
"github.com/Microsoft/hcsshim/internal/wclayer"
|
||||
"github.com/Microsoft/hcsshim/pkg/cimfs"
|
||||
)
|
||||
|
||||
// A BlockCIMLayerWriter implements the CIMLayerWriter interface to allow writing
|
||||
// container image layers in the blocked cim format.
|
||||
type BlockCIMLayerWriter struct {
|
||||
*cimLayerWriter
|
||||
// the layer that we are writing
|
||||
layer *cimfs.BlockCIM
|
||||
// parent layers
|
||||
parentLayers []*cimfs.BlockCIM
|
||||
// added files maintains a map of all files that have been added to this layer
|
||||
addedFiles map[string]struct{}
|
||||
}
|
||||
|
||||
var _ CIMLayerWriter = &BlockCIMLayerWriter{}
|
||||
|
||||
// NewBlockCIMLayerWriterWithOpts returns a writer for writing image layers in the block CIM format. The writer's behavior can be
|
||||
// controlled with the supports opts.
|
||||
func NewBlockCIMLayerWriterWithOpts(ctx context.Context, layer *cimfs.BlockCIM, parentLayers []*cimfs.BlockCIM, opts ...cimfs.BlockCIMOpt) (_ *BlockCIMLayerWriter, err error) {
|
||||
if layer.Type != cimfs.BlockCIMTypeSingleFile {
|
||||
// we only support writing single file CIMs for now because in layer
|
||||
// writing process we still need to write some files (registry hives)
|
||||
// outside the CIM. We currently use the parent directory of the CIM (i.e
|
||||
// the parent directory of block path in this case) for this. This can't
|
||||
// be reliably done with the block device CIM since the block path
|
||||
// provided will be a volume path. However, once we get rid of hive rollup
|
||||
// step during layer import we should be able to support block device
|
||||
// CIMs.
|
||||
return nil, ErrBlockCIMWriterNotSupported
|
||||
}
|
||||
|
||||
parentLayerPaths := make([]string, 0, len(parentLayers))
|
||||
for _, pl := range parentLayers {
|
||||
if pl.Type != layer.Type {
|
||||
return nil, ErrBlockCIMParentTypeMismatch
|
||||
}
|
||||
parentLayerPaths = append(parentLayerPaths, filepath.Dir(pl.BlockPath))
|
||||
}
|
||||
|
||||
// We always want to write layers with consistent flag
|
||||
bcimOpts := append([]cimfs.BlockCIMOpt{cimfs.WithConsistentCIM()}, opts...)
|
||||
|
||||
cim, err := cimfs.CreateBlockCIMWithOptions(ctx, layer, bcimOpts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error in creating a new cim: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
cErr := cim.Close()
|
||||
if cErr != nil {
|
||||
log.G(ctx).WithError(err).Warnf("failed to close cim after error: %s", cErr)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// std file writer writes registry hives outside the CIM for 2 reasons. 1. We can
|
||||
// merge the hives of this layer with the parent layer hives and then write the
|
||||
// merged hives into the CIM. 2. When importing child layer of this layer, we
|
||||
// have access to the merges hives of this layer.
|
||||
sfw, err := newStdFileWriter(filepath.Dir(layer.BlockPath), parentLayerPaths)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error in creating new standard file writer: %w", err)
|
||||
}
|
||||
|
||||
return &BlockCIMLayerWriter{
|
||||
layer: layer,
|
||||
parentLayers: parentLayers,
|
||||
addedFiles: make(map[string]struct{}),
|
||||
cimLayerWriter: &cimLayerWriter{
|
||||
ctx: ctx,
|
||||
cimWriter: cim,
|
||||
stdFileWriter: sfw,
|
||||
layerPath: filepath.Dir(layer.BlockPath),
|
||||
parentLayerPaths: parentLayerPaths,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewBlockCIMLayerWriter writes the layer files in the block CIM format.
|
||||
func NewBlockCIMLayerWriter(ctx context.Context, layer *cimfs.BlockCIM, parentLayers []*cimfs.BlockCIM) (_ *BlockCIMLayerWriter, err error) {
|
||||
return NewBlockCIMLayerWriterWithOpts(ctx, layer, parentLayers)
|
||||
}
|
||||
|
||||
// Add adds a file to the layer with given metadata.
|
||||
func (cw *BlockCIMLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo, fileSize int64, securityDescriptor []byte, extendedAttributes []byte, reparseData []byte) error {
|
||||
cw.addedFiles[name] = struct{}{}
|
||||
if name == wclayer.UtilityVMPath && len(cw.parentLayers) > 0 {
|
||||
// If there are UtilityVM files in non base layers, we will have to merge
|
||||
// those files with the parent layer UtilityVM files - either during image
|
||||
// pull or at runtime (i.e when starting the UVM). In order to merge at
|
||||
// image pull time, we will have to read parent layer block CIMs and copy
|
||||
// all the UtilityVM files from those CIMs into this block CIM one by one
|
||||
// i.e effectively merge all parent layer UtilityVM files in this
|
||||
// layer. Or we will need to be able to boot the UtilityVM with merged
|
||||
// block CIMs. None of these options are implemented yet so log a
|
||||
// warning. However, this shouldn't cause issues with most of the standard
|
||||
// use cases because usually the pod is a nanoserver image and that is
|
||||
// always a single layer.
|
||||
log.G(cw.ctx).Warn("UtilityVM files in non base layers is not supported for block CIMs")
|
||||
}
|
||||
return cw.cimLayerWriter.Add(name, fileInfo, fileSize, securityDescriptor, extendedAttributes, reparseData)
|
||||
}
|
||||
|
||||
// Remove removes a file that was present in a parent layer from the layer.
|
||||
func (cw *BlockCIMLayerWriter) Remove(name string) error {
|
||||
// set active write to nil so that we panic if layer tar is incorrectly formatted.
|
||||
cw.activeWriter = nil
|
||||
err := cw.cimWriter.AddTombstone(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to remove file : %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddLink adds a hard link to the layer. Note that the link added here is evaluated only
|
||||
// at the CIM merge time. So an invalid link will not throw an error here.
|
||||
func (cw *BlockCIMLayerWriter) AddLink(name string, target string) error {
|
||||
// set active write to nil so that we panic if layer tar is incorrectly formatted.
|
||||
cw.activeWriter = nil
|
||||
|
||||
// when adding links to a block CIM, we need to know if the target file is present
|
||||
// in this same block CIM or if it is coming from one of the parent layers. If the
|
||||
// file is in the same CIM we add a standard hard link. If the file is not in the
|
||||
// same CIM we add a special type of link called merged link. This merged link is
|
||||
// resolved when all the individual block CIM layers are merged. In order to
|
||||
// reliably know if the target is a part of the CIM or not, we wait until all
|
||||
// files are added and then lookup the added entries in a map to make the
|
||||
// decision.
|
||||
pendingLinkOp := func(c *cimfs.CimFsWriter) error {
|
||||
if _, ok := cw.addedFiles[target]; ok {
|
||||
// target was added in this layer - add a normal link. Once a
|
||||
// hardlink is added that hardlink also becomes a valid target for
|
||||
// other links so include it in the map.
|
||||
cw.addedFiles[name] = struct{}{}
|
||||
return c.AddLink(target, name)
|
||||
} else {
|
||||
// target is from a parent layer - add a merged link
|
||||
return c.AddMergedLink(target, name)
|
||||
}
|
||||
}
|
||||
cw.pendingOps = append(cw.pendingOps, pendingCimOpFunc(pendingLinkOp))
|
||||
return nil
|
||||
|
||||
}
|
||||
204
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/common.go
generated
vendored
Normal file
204
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/common.go
generated
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
//go:build windows
|
||||
|
||||
package cim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
"github.com/Microsoft/hcsshim/internal/wclayer"
|
||||
"github.com/Microsoft/hcsshim/pkg/cimfs"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBlockCIMWriterNotSupported = fmt.Errorf("writing block device CIM isn't supported")
|
||||
ErrBlockCIMParentTypeMismatch = fmt.Errorf("parent layer block CIM type doesn't match with extraction layer")
|
||||
ErrBlockCIMIntegrityMismatch = fmt.Errorf("verified CIMs can not be mixed with non verified CIMs")
|
||||
)
|
||||
|
||||
type hive struct {
|
||||
name string
|
||||
base string
|
||||
delta string
|
||||
}
|
||||
|
||||
var (
|
||||
hives = []hive{
|
||||
{"SYSTEM", "SYSTEM_BASE", "SYSTEM_DELTA"},
|
||||
{"SOFTWARE", "SOFTWARE_BASE", "SOFTWARE_DELTA"},
|
||||
{"SAM", "SAM_BASE", "SAM_DELTA"},
|
||||
{"SECURITY", "SECURITY_BASE", "SECURITY_DELTA"},
|
||||
{"DEFAULT", "DEFAULTUSER_BASE", "DEFAULTUSER_DELTA"},
|
||||
}
|
||||
)
|
||||
|
||||
// CIMLayerWriter is an interface that supports writing a new container image layer to the
|
||||
// CIM format
|
||||
type CIMLayerWriter interface {
|
||||
// Add adds a file to the layer with given metadata.
|
||||
Add(string, *winio.FileBasicInfo, int64, []byte, []byte, []byte) error
|
||||
// AddLink adds a hard link to the layer. The target must already have been added.
|
||||
AddLink(string, string) error
|
||||
// AddAlternateStream adds an alternate stream to a file
|
||||
AddAlternateStream(string, uint64) error
|
||||
// Remove removes a file that was present in a parent layer from the layer.
|
||||
Remove(string) error
|
||||
// Write writes data to the current file. The data must be in the format of a Win32
|
||||
// backup stream.
|
||||
Write([]byte) (int, error)
|
||||
// Close finishes the layer writing process and releases any resources.
|
||||
Close(context.Context) error
|
||||
}
|
||||
|
||||
func isDeltaOrBaseHive(path string) bool {
|
||||
for _, hv := range hives {
|
||||
if strings.EqualFold(path, filepath.Join(wclayer.HivesPath, hv.delta)) ||
|
||||
strings.EqualFold(path, filepath.Join(wclayer.RegFilesPath, hv.name)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// checks if this particular file should be written with a stdFileWriter instead of
|
||||
// using the cimWriter.
|
||||
func isStdFile(path string) bool {
|
||||
return (isDeltaOrBaseHive(path) ||
|
||||
path == filepath.Join(wclayer.UtilityVMPath, wclayer.RegFilesPath, "SYSTEM") ||
|
||||
path == filepath.Join(wclayer.UtilityVMPath, wclayer.RegFilesPath, "SOFTWARE") ||
|
||||
path == wclayer.BcdFilePath || path == wclayer.BootMgrFilePath)
|
||||
}
|
||||
|
||||
// cimLayerWriter is a base struct that is further extended by forked cim writer & blocked
|
||||
// cim writer to provide full functionality of writing layers.
|
||||
type cimLayerWriter struct {
|
||||
ctx context.Context
|
||||
// Handle to the layer cim - writes to the cim file
|
||||
cimWriter *cimfs.CimFsWriter
|
||||
// Handle to the writer for writing files in the local filesystem
|
||||
stdFileWriter *stdFileWriter
|
||||
// reference to currently active writer either cimWriter or stdFileWriter
|
||||
activeWriter io.Writer
|
||||
// denotes if this layer has the UtilityVM directory
|
||||
hasUtilityVM bool
|
||||
// path to the layer (i.e layer's directory) as provided by the caller.
|
||||
// Even if a layer is stored as a cim in the cim directory, some files associated
|
||||
// with a layer are still stored in this path.
|
||||
layerPath string
|
||||
// parent layer paths
|
||||
parentLayerPaths []string
|
||||
// some files are written outside the cim during initial import (via stdFileWriter) because we need to
|
||||
// make some modifications to these files before writing them to the cim. The pendingOps slice
|
||||
// maintains a list of such delayed modifications to the layer cim. These modifications are applied at
|
||||
// the very end of layer import process.
|
||||
pendingOps []pendingCimOp
|
||||
}
|
||||
|
||||
// Add adds a file to the layer with given metadata.
|
||||
func (cw *cimLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo, fileSize int64, securityDescriptor []byte, extendedAttributes []byte, reparseData []byte) error {
|
||||
if name == wclayer.UtilityVMPath {
|
||||
cw.hasUtilityVM = true
|
||||
}
|
||||
if isStdFile(name) {
|
||||
// create a pending op for this file
|
||||
cw.pendingOps = append(cw.pendingOps, &addOp{
|
||||
pathInCim: name,
|
||||
hostPath: filepath.Join(cw.layerPath, name),
|
||||
fileInfo: fileInfo,
|
||||
securityDescriptor: securityDescriptor,
|
||||
extendedAttributes: extendedAttributes,
|
||||
reparseData: reparseData,
|
||||
})
|
||||
if err := cw.stdFileWriter.Add(name); err != nil {
|
||||
return err
|
||||
}
|
||||
cw.activeWriter = cw.stdFileWriter
|
||||
} else {
|
||||
if err := cw.cimWriter.AddFile(name, fileInfo, fileSize, securityDescriptor, extendedAttributes, reparseData); err != nil {
|
||||
return err
|
||||
}
|
||||
cw.activeWriter = cw.cimWriter
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddLink adds a hard link to the layer. The target must already have been added.
|
||||
func (cw *cimLayerWriter) AddLink(name string, target string) error {
|
||||
// set active write to nil so that we panic if layer tar is incorrectly formatted.
|
||||
cw.activeWriter = nil
|
||||
if isStdFile(target) {
|
||||
// If this is a link to a std file it will have to be added later once the
|
||||
// std file is written to the CIM. Create a pending op for this
|
||||
cw.pendingOps = append(cw.pendingOps, &linkOp{
|
||||
oldPath: target,
|
||||
newPath: name,
|
||||
})
|
||||
return nil
|
||||
} else if isStdFile(name) {
|
||||
// None of the predefined std files are links. If they show up as links this is unexpected
|
||||
// behavior. Error out.
|
||||
return fmt.Errorf("unexpected link %s in layer", name)
|
||||
} else {
|
||||
return cw.cimWriter.AddLink(target, name)
|
||||
}
|
||||
}
|
||||
|
||||
// AddAlternateStream creates another alternate stream at the given
|
||||
// path. Any writes made after this call will go to that stream.
|
||||
func (cw *cimLayerWriter) AddAlternateStream(name string, size uint64) error {
|
||||
if isStdFile(name) {
|
||||
// As of now there is no known case of std file having multiple data streams.
|
||||
// If such a file is encountered our assumptions are wrong. Error out.
|
||||
return fmt.Errorf("unexpected alternate stream %s in layer", name)
|
||||
}
|
||||
|
||||
if err := cw.cimWriter.CreateAlternateStream(name, size); err != nil {
|
||||
return err
|
||||
}
|
||||
cw.activeWriter = cw.cimWriter
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes data to the current file. The data must be in the format of a Win32
|
||||
// backup stream.
|
||||
func (cw *cimLayerWriter) Write(b []byte) (int, error) {
|
||||
return cw.activeWriter.Write(b)
|
||||
}
|
||||
|
||||
// Close finishes the layer writing process and releases any resources.
|
||||
func (cw *cimLayerWriter) Close(ctx context.Context) (retErr error) {
|
||||
if err := cw.stdFileWriter.Close(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// cimWriter must be closed even if there are errors.
|
||||
defer func() {
|
||||
if err := cw.cimWriter.Close(); retErr == nil {
|
||||
retErr = err
|
||||
}
|
||||
}()
|
||||
|
||||
// We don't support running UtilityVM with CIM layers yet.
|
||||
processUtilityVM := false
|
||||
|
||||
if len(cw.parentLayerPaths) == 0 {
|
||||
if err := cw.processBaseLayer(ctx, processUtilityVM); err != nil {
|
||||
return fmt.Errorf("process base layer: %w", err)
|
||||
}
|
||||
} else {
|
||||
if err := cw.processNonBaseLayer(ctx, processUtilityVM); err != nil {
|
||||
return fmt.Errorf("process non base layer: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, op := range cw.pendingOps {
|
||||
if err := op.apply(cw.cimWriter); err != nil {
|
||||
return fmt.Errorf("apply pending operations: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
3
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/doc.go
generated
vendored
Normal file
3
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// This package provides utilities for working with container image layers in the cim format
|
||||
// via the wclayer APIs.
|
||||
package cim
|
||||
93
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/file_writer.go
generated
vendored
Normal file
93
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/file_writer.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
//go:build windows
|
||||
|
||||
package cim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
"github.com/Microsoft/hcsshim/internal/safefile"
|
||||
"github.com/Microsoft/hcsshim/internal/winapi"
|
||||
)
|
||||
|
||||
// stdFileWriter writes the files of a layer to the layer folder instead of writing them inside the cim.
|
||||
// For some files (like the Hive files or some UtilityVM files) it is necessary to write them as a normal file
|
||||
// first, do some modifications on them (for example merging of hives or processing of UtilityVM files)
|
||||
// and then write the modified versions into the cim. This writer is used for such files.
|
||||
type stdFileWriter struct {
|
||||
activeFile *os.File
|
||||
// parent layer paths
|
||||
parentLayerPaths []string
|
||||
// path to the current layer
|
||||
path string
|
||||
// the open handle to the path directory
|
||||
root *os.File
|
||||
}
|
||||
|
||||
func newStdFileWriter(root string, parentRoots []string) (sfw *stdFileWriter, err error) {
|
||||
sfw = &stdFileWriter{
|
||||
path: root,
|
||||
parentLayerPaths: parentRoots,
|
||||
}
|
||||
sfw.root, err = safefile.OpenRoot(root)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (sfw *stdFileWriter) closeActiveFile() (err error) {
|
||||
if sfw.activeFile != nil {
|
||||
err = sfw.activeFile.Close()
|
||||
sfw.activeFile = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Adds a new file or an alternate data stream to an existing file inside the layer directory.
|
||||
func (sfw *stdFileWriter) Add(name string) error {
|
||||
if err := sfw.closeActiveFile(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The directory of this file might be created inside the cim.
|
||||
// make sure we have the same parent directory chain here
|
||||
if err := safefile.MkdirAllRelative(filepath.Dir(name), sfw.root); err != nil {
|
||||
return fmt.Errorf("failed to create file %s: %w", name, err)
|
||||
}
|
||||
|
||||
f, err := safefile.OpenRelative(
|
||||
name,
|
||||
sfw.root,
|
||||
syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER,
|
||||
syscall.FILE_SHARE_READ,
|
||||
winapi.FILE_CREATE,
|
||||
0,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating file %s: %w", name, err)
|
||||
}
|
||||
sfw.activeFile = f
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes data to the current file. The data must be in the format of a Win32
|
||||
// backup stream.
|
||||
func (sfw *stdFileWriter) Write(b []byte) (int, error) {
|
||||
return sfw.activeFile.Write(b)
|
||||
}
|
||||
|
||||
// Close finishes the layer writing process and releases any resources.
|
||||
func (sfw *stdFileWriter) Close(ctx context.Context) error {
|
||||
if err := sfw.closeActiveFile(); err != nil {
|
||||
return fmt.Errorf("failed to close active file %s : %w", sfw.activeFile.Name(), err)
|
||||
}
|
||||
if err := sfw.root.Close(); err != nil {
|
||||
return fmt.Errorf("failed to close root dir: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
78
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/forked_cim_writer.go
generated
vendored
Normal file
78
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/forked_cim_writer.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
//go:build windows
|
||||
|
||||
package cim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Microsoft/hcsshim/internal/log"
|
||||
"github.com/Microsoft/hcsshim/pkg/cimfs"
|
||||
)
|
||||
|
||||
// A ForkedCimLayerWriter implements the wclayer.LayerWriter interface to allow writing container
|
||||
// image layers in the cim format.
|
||||
// A cim layer consist of cim files (which are usually stored in the `cim-layers` directory and
|
||||
// some other files which are stored in the directory of that layer (i.e the `path` directory).
|
||||
type ForkedCimLayerWriter struct {
|
||||
*cimLayerWriter
|
||||
}
|
||||
|
||||
var _ CIMLayerWriter = &ForkedCimLayerWriter{}
|
||||
|
||||
func NewForkedCimLayerWriter(ctx context.Context, layerPath, cimPath string, parentLayerPaths, parentLayerCimPaths []string) (_ *ForkedCimLayerWriter, err error) {
|
||||
if !cimfs.IsCimFSSupported() {
|
||||
return nil, fmt.Errorf("CimFs not supported on this build")
|
||||
}
|
||||
|
||||
parentCim := ""
|
||||
if len(parentLayerPaths) > 0 {
|
||||
// We only need to provide parent CIM name, it is assumed that both parent CIM
|
||||
// and newly created CIM are present in the same directory.
|
||||
parentCim = filepath.Base(parentLayerCimPaths[0])
|
||||
}
|
||||
|
||||
cim, err := cimfs.Create(filepath.Dir(cimPath), parentCim, filepath.Base(cimPath))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error in creating a new cim: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
cErr := cim.Close()
|
||||
if cErr != nil {
|
||||
log.G(ctx).WithError(err).Warnf("failed to close cim after error: %s", cErr)
|
||||
}
|
||||
cErr = cimfs.DestroyCim(ctx, cimPath)
|
||||
if cErr != nil {
|
||||
log.G(ctx).WithError(err).Warnf("failed to cleanup cim after error: %s", cErr)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
sfw, err := newStdFileWriter(layerPath, parentLayerPaths)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error in creating new standard file writer: %w", err)
|
||||
}
|
||||
return &ForkedCimLayerWriter{
|
||||
cimLayerWriter: &cimLayerWriter{
|
||||
parentLayerPaths: parentLayerPaths,
|
||||
ctx: ctx,
|
||||
cimWriter: cim,
|
||||
stdFileWriter: sfw,
|
||||
layerPath: layerPath,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Remove removes a file that was present in a parent layer from the layer.
|
||||
func (cw *ForkedCimLayerWriter) Remove(name string) error {
|
||||
// set active write to nil so that we panic if layer tar is incorrectly formatted.
|
||||
cw.activeWriter = nil
|
||||
err := cw.cimWriter.Unlink(name)
|
||||
if err == nil || os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("failed to remove file: %w", err)
|
||||
}
|
||||
150
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/mount.go
generated
vendored
Normal file
150
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/mount.go
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
//go:build windows
|
||||
|
||||
package cim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Microsoft/go-winio/pkg/guid"
|
||||
"github.com/Microsoft/hcsshim/internal/log"
|
||||
"github.com/Microsoft/hcsshim/internal/oc"
|
||||
cimfs "github.com/Microsoft/hcsshim/pkg/cimfs"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var cimMountNamespace guid.GUID = guid.GUID{Data1: 0x6827367b, Data2: 0xc388, Data3: 0x4e9b, Data4: [8]byte{0x96, 0x1c, 0x6d, 0x2c, 0x93, 0x6c}}
|
||||
|
||||
// MountForkedCimLayer mounts the cim at path `cimPath` and returns the mount location of
|
||||
// that cim. The containerID is used to generate the volumeID for the volume at which
|
||||
// this CIM is mounted. containerID is used so that if the shim process crashes for any
|
||||
// reason, the mounted cim can be correctly cleaned up during `shim delete` call.
|
||||
func MountForkedCimLayer(ctx context.Context, cimPath, containerID string) (string, error) {
|
||||
volumeGUID, err := guid.NewV5(cimMountNamespace, []byte(containerID))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("generated cim mount GUID: %w", err)
|
||||
}
|
||||
|
||||
vol, err := cimfs.Mount(cimPath, volumeGUID, 0)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return vol, nil
|
||||
}
|
||||
|
||||
// MountBlockCIMLayer mounts the given block cim and returns the mount
|
||||
// location of that cim. The containerID is used to generate the volumeID for the volume
|
||||
// at which this CIM is mounted. containerID is used so that if the shim process crashes
|
||||
// for any reason, the mounted cim can be correctly cleaned up during `shim delete` call.
|
||||
func MountBlockCIMLayer(ctx context.Context, layer *cimfs.BlockCIM, containerID string) (_ string, err error) {
|
||||
ctx, span := oc.StartSpan(ctx, "MountBlockCIMLayer")
|
||||
defer func() {
|
||||
oc.SetSpanStatus(span, err)
|
||||
span.End()
|
||||
}()
|
||||
span.AddAttributes(
|
||||
trace.StringAttribute("layer", layer.String()))
|
||||
|
||||
var mountFlags uint32
|
||||
switch layer.Type {
|
||||
case cimfs.BlockCIMTypeDevice:
|
||||
mountFlags |= cimfs.CimMountBlockDeviceCim
|
||||
case cimfs.BlockCIMTypeSingleFile:
|
||||
mountFlags |= cimfs.CimMountSingleFileCim
|
||||
default:
|
||||
return "", fmt.Errorf("invalid BlockCIMType for merged layer: %w", os.ErrInvalid)
|
||||
}
|
||||
|
||||
volumeGUID, err := guid.NewV5(cimMountNamespace, []byte(containerID))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("generated cim mount GUID: %w", err)
|
||||
}
|
||||
|
||||
cimPath := filepath.Join(layer.BlockPath, layer.CimName)
|
||||
|
||||
log.G(ctx).WithFields(logrus.Fields{
|
||||
"flags": mountFlags,
|
||||
"volume": volumeGUID.String(),
|
||||
}).Debug("mounting block layer CIM")
|
||||
|
||||
vol, err := cimfs.Mount(cimPath, volumeGUID, mountFlags)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return vol, nil
|
||||
}
|
||||
|
||||
// MergeMountBlockCIMLayer mounts the given merged block cim and returns the mount
|
||||
// location of that cim. The containerID is used to generate the volumeID for the volume
|
||||
// at which this CIM is mounted. containerID is used so that if the shim process crashes
|
||||
// for any reason, the mounted cim can be correctly cleaned up during `shim delete` call.
|
||||
// parentLayers MUST be in the base to topmost order. I.e base layer should be at index 0
|
||||
// and immediate parent MUST be at the last index.
|
||||
func MergeMountBlockCIMLayer(ctx context.Context, mergedLayer *cimfs.BlockCIM, parentLayers []*cimfs.BlockCIM, containerID string) (_ string, err error) {
|
||||
_, span := oc.StartSpan(ctx, "MergeMountBlockCIMLayer")
|
||||
defer func() {
|
||||
oc.SetSpanStatus(span, err)
|
||||
span.End()
|
||||
}()
|
||||
span.AddAttributes(
|
||||
trace.StringAttribute("merged layer", mergedLayer.String()),
|
||||
trace.StringAttribute("parent layers", fmt.Sprintf("%v", parentLayers)))
|
||||
|
||||
var mountFlags uint32
|
||||
switch mergedLayer.Type {
|
||||
case cimfs.BlockCIMTypeDevice:
|
||||
mountFlags |= cimfs.CimMountBlockDeviceCim
|
||||
case cimfs.BlockCIMTypeSingleFile:
|
||||
mountFlags |= cimfs.CimMountSingleFileCim
|
||||
default:
|
||||
return "", fmt.Errorf("invalid BlockCIMType for merged layer: %w", os.ErrInvalid)
|
||||
}
|
||||
|
||||
volumeGUID, err := guid.NewV5(cimMountNamespace, []byte(containerID))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("generated cim mount GUID: %w", err)
|
||||
}
|
||||
|
||||
return cimfs.MountMergedBlockCIMs(mergedLayer, parentLayers, mountFlags, volumeGUID)
|
||||
}
|
||||
|
||||
// Unmounts the cim mounted at the given volume
|
||||
func UnmountCimLayer(ctx context.Context, volume string) error {
|
||||
return cimfs.Unmount(volume)
|
||||
}
|
||||
|
||||
func CleanupContainerMounts(containerID string) error {
|
||||
volumeGUID, err := guid.NewV5(cimMountNamespace, []byte(containerID))
|
||||
if err != nil {
|
||||
return fmt.Errorf("generated cim mount GUID: %w", err)
|
||||
}
|
||||
|
||||
volPath := fmt.Sprintf("\\\\?\\Volume{%s}\\", volumeGUID.String())
|
||||
|
||||
log.L.WithFields(logrus.Fields{
|
||||
"volume": volPath,
|
||||
"containerID": containerID,
|
||||
}).Debug("cleanup container CIM mounts")
|
||||
|
||||
if _, err := os.Stat(volPath); err == nil {
|
||||
err = cimfs.Unmount(volPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LayerID provides a unique GUID for each mounted CIM volume.
|
||||
func LayerID(vol string) (string, error) {
|
||||
// since each mounted volume has a unique GUID, just return the same GUID as ID
|
||||
if !strings.HasPrefix(vol, "\\\\?\\Volume{") || !strings.HasSuffix(vol, "}\\") {
|
||||
return "", fmt.Errorf("volume path %s is not in the expected format", vol)
|
||||
} else {
|
||||
return strings.TrimSuffix(strings.TrimPrefix(vol, "\\\\?\\Volume{"), "}\\"), nil
|
||||
}
|
||||
}
|
||||
75
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/pending.go
generated
vendored
Normal file
75
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/pending.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
//go:build windows
|
||||
|
||||
package cim
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
"github.com/Microsoft/hcsshim/pkg/cimfs"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type pendingCimOp interface {
|
||||
apply(cw *cimfs.CimFsWriter) error
|
||||
}
|
||||
|
||||
type pendingCimOpFunc func(cw *cimfs.CimFsWriter) error
|
||||
|
||||
func (f pendingCimOpFunc) apply(cw *cimfs.CimFsWriter) error {
|
||||
return f(cw)
|
||||
|
||||
}
|
||||
|
||||
// add op represents a pending operation of adding a new file inside the cim
|
||||
type addOp struct {
|
||||
// path inside the cim at which the file should be added
|
||||
pathInCim string
|
||||
// host path where this file was temporarily written.
|
||||
hostPath string
|
||||
// other file metadata fields that were provided during the add call.
|
||||
fileInfo *winio.FileBasicInfo
|
||||
securityDescriptor []byte
|
||||
extendedAttributes []byte
|
||||
reparseData []byte
|
||||
}
|
||||
|
||||
func (o *addOp) apply(cw *cimfs.CimFsWriter) error {
|
||||
f, err := os.Open(o.hostPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open file %s: %w", o.hostPath, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fs, err := f.Stat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("stat file %s: %w", o.hostPath, err)
|
||||
}
|
||||
|
||||
if err := cw.AddFile(o.pathInCim, o.fileInfo, fs.Size(), o.securityDescriptor, o.extendedAttributes, o.reparseData); err != nil {
|
||||
return fmt.Errorf("cim add file %s: %w", o.hostPath, err)
|
||||
}
|
||||
|
||||
if o.fileInfo.FileAttributes != windows.FILE_ATTRIBUTE_DIRECTORY {
|
||||
written, err := io.Copy(cw, f)
|
||||
if err != nil {
|
||||
return fmt.Errorf("write file %s inside cim: %w", o.hostPath, err)
|
||||
} else if written != fs.Size() {
|
||||
return fmt.Errorf("short write to cim for file %s, expected %d bytes wrote %d", o.hostPath, fs.Size(), written)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// linkOp represents a pending link file operation inside the cim
|
||||
type linkOp struct {
|
||||
// old & new paths inside the cim where the link should be created
|
||||
oldPath string
|
||||
newPath string
|
||||
}
|
||||
|
||||
func (o *linkOp) apply(cw *cimfs.CimFsWriter) error {
|
||||
return cw.AddLink(o.oldPath, o.newPath)
|
||||
}
|
||||
138
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/process.go
generated
vendored
Normal file
138
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/process.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
//go:build windows
|
||||
|
||||
package cim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
"github.com/Microsoft/hcsshim/internal/wclayer"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// processUtilityVMLayer will handle processing of UVM specific files when we start
|
||||
// supporting UVM based containers with CimFS in the future.
|
||||
func processUtilityVMLayer(ctx context.Context, layerPath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// processBaseLayerHives make the base layer specific modifications on the hives and emits equivalent the
|
||||
// pendingCimOps that should be applied on the CIM. In base layer we need to create hard links from registry
|
||||
// hives under Files/Windows/Sysetm32/config into Hives/*_BASE. This function creates these links outside so
|
||||
// that the registry hives under Hives/ are available during children layers import. Then we write these hive
|
||||
// files inside the cim and create links inside the cim.
|
||||
func processBaseLayerHives(layerPath string) ([]pendingCimOp, error) {
|
||||
pendingOps := []pendingCimOp{}
|
||||
|
||||
// make hives directory both outside and in the cim
|
||||
if err := os.Mkdir(filepath.Join(layerPath, wclayer.HivesPath), 0755); err != nil {
|
||||
return pendingOps, fmt.Errorf("hives directory creation: %w", err)
|
||||
}
|
||||
|
||||
hivesDirInfo := &winio.FileBasicInfo{
|
||||
FileAttributes: windows.FILE_ATTRIBUTE_DIRECTORY,
|
||||
}
|
||||
pendingOps = append(pendingOps, &addOp{
|
||||
pathInCim: wclayer.HivesPath,
|
||||
hostPath: filepath.Join(layerPath, wclayer.HivesPath),
|
||||
fileInfo: hivesDirInfo,
|
||||
})
|
||||
|
||||
// add hard links from base hive files.
|
||||
for _, hv := range hives {
|
||||
oldHivePathRelative := filepath.Join(wclayer.RegFilesPath, hv.name)
|
||||
newHivePathRelative := filepath.Join(wclayer.HivesPath, hv.base)
|
||||
if err := os.Link(filepath.Join(layerPath, oldHivePathRelative), filepath.Join(layerPath, newHivePathRelative)); err != nil {
|
||||
return pendingOps, fmt.Errorf("hive link creation: %w", err)
|
||||
}
|
||||
|
||||
pendingOps = append(pendingOps, &linkOp{
|
||||
oldPath: oldHivePathRelative,
|
||||
newPath: newHivePathRelative,
|
||||
})
|
||||
}
|
||||
return pendingOps, nil
|
||||
}
|
||||
|
||||
// processLayoutFile creates a file named "layout" in the root of the base layer. This allows certain
|
||||
// container startup related functions to understand that the hives are a part of the container rootfs.
|
||||
func processLayoutFile(layerPath string) ([]pendingCimOp, error) {
|
||||
fileContents := "vhd-with-hives\n"
|
||||
if err := os.WriteFile(filepath.Join(layerPath, "layout"), []byte(fileContents), 0755); err != nil {
|
||||
return []pendingCimOp{}, fmt.Errorf("write layout file: %w", err)
|
||||
}
|
||||
|
||||
layoutFileInfo := &winio.FileBasicInfo{
|
||||
FileAttributes: windows.FILE_ATTRIBUTE_NORMAL,
|
||||
}
|
||||
|
||||
op := &addOp{
|
||||
pathInCim: "layout",
|
||||
hostPath: filepath.Join(layerPath, "layout"),
|
||||
fileInfo: layoutFileInfo,
|
||||
}
|
||||
return []pendingCimOp{op}, nil
|
||||
}
|
||||
|
||||
// Some of the layer files that are generated during the processBaseLayer call must be added back
|
||||
// inside the cim, some registry file links must be updated. This function takes care of all those
|
||||
// steps. This function opens the cim file for writing and updates it.
|
||||
func (cw *cimLayerWriter) processBaseLayer(ctx context.Context, processUtilityVM bool) (err error) {
|
||||
if processUtilityVM {
|
||||
if err = processUtilityVMLayer(ctx, cw.layerPath); err != nil {
|
||||
return fmt.Errorf("process utilityVM layer: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
ops, err := processBaseLayerHives(cw.layerPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cw.pendingOps = append(cw.pendingOps, ops...)
|
||||
|
||||
ops, err = processLayoutFile(cw.layerPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cw.pendingOps = append(cw.pendingOps, ops...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// processNonBaseLayer takes care of the processing required for a non base layer. As of now
|
||||
// the only processing required for non base layer is to merge the delta registry hives of the
|
||||
// non-base layer with it's parent layer.
|
||||
func (cw *cimLayerWriter) processNonBaseLayer(ctx context.Context, processUtilityVM bool) (err error) {
|
||||
for _, hv := range hives {
|
||||
baseHive := filepath.Join(wclayer.HivesPath, hv.base)
|
||||
deltaHive := filepath.Join(wclayer.HivesPath, hv.delta)
|
||||
_, err := os.Stat(filepath.Join(cw.layerPath, deltaHive))
|
||||
// merge with parent layer if delta exists.
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("stat delta hive %s: %w", filepath.Join(cw.layerPath, deltaHive), err)
|
||||
} else if err == nil {
|
||||
// merge base hive of parent layer with the delta hive of this layer and write it as
|
||||
// the base hive of this layer.
|
||||
err = mergeHive(filepath.Join(cw.parentLayerPaths[0], baseHive), filepath.Join(cw.layerPath, deltaHive), filepath.Join(cw.layerPath, baseHive))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// the newly created merged file must be added to the cim
|
||||
cw.pendingOps = append(cw.pendingOps, &addOp{
|
||||
pathInCim: baseHive,
|
||||
hostPath: filepath.Join(cw.layerPath, baseHive),
|
||||
fileInfo: &winio.FileBasicInfo{
|
||||
FileAttributes: windows.FILE_ATTRIBUTE_NORMAL,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if processUtilityVM {
|
||||
return processUtilityVMLayer(ctx, cw.layerPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
49
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/registry.go
generated
vendored
Normal file
49
vendor/github.com/Microsoft/hcsshim/internal/wclayer/cim/registry.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
//go:build windows
|
||||
|
||||
package cim
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Microsoft/hcsshim/internal/winapi"
|
||||
"github.com/Microsoft/hcsshim/osversion"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// mergeHive merges the hive located at parentHivePath with the hive located at deltaHivePath and stores
|
||||
// the result into the file at mergedHivePath. If a file already exists at path `mergedHivePath` then it
|
||||
// throws an error.
|
||||
func mergeHive(parentHivePath, deltaHivePath, mergedHivePath string) (err error) {
|
||||
var baseHive, deltaHive, mergedHive winapi.ORHKey
|
||||
if err := winapi.OROpenHive(parentHivePath, &baseHive); err != nil {
|
||||
return fmt.Errorf("failed to open base hive %s: %w", parentHivePath, err)
|
||||
}
|
||||
defer func() {
|
||||
err2 := winapi.ORCloseHive(baseHive)
|
||||
if err == nil {
|
||||
err = errors.Wrap(err2, "failed to close base hive")
|
||||
}
|
||||
}()
|
||||
if err := winapi.OROpenHive(deltaHivePath, &deltaHive); err != nil {
|
||||
return fmt.Errorf("failed to open delta hive %s: %w", deltaHivePath, err)
|
||||
}
|
||||
defer func() {
|
||||
err2 := winapi.ORCloseHive(deltaHive)
|
||||
if err == nil {
|
||||
err = errors.Wrap(err2, "failed to close delta hive")
|
||||
}
|
||||
}()
|
||||
if err := winapi.ORMergeHives([]winapi.ORHKey{baseHive, deltaHive}, &mergedHive); err != nil {
|
||||
return fmt.Errorf("failed to merge hives: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
err2 := winapi.ORCloseHive(mergedHive)
|
||||
if err == nil {
|
||||
err = errors.Wrap(err2, "failed to close merged hive")
|
||||
}
|
||||
}()
|
||||
if err := winapi.ORSaveHive(mergedHive, mergedHivePath, uint32(osversion.Get().MajorVersion), uint32(osversion.Get().MinorVersion)); err != nil {
|
||||
return fmt.Errorf("failed to save hive: %w", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
542
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/cim_writer_windows.go
generated
vendored
Normal file
542
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/cim_writer_windows.go
generated
vendored
Normal file
@@ -0,0 +1,542 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cimfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
"github.com/Microsoft/hcsshim/internal/log"
|
||||
"github.com/Microsoft/hcsshim/internal/winapi"
|
||||
winapitypes "github.com/Microsoft/hcsshim/internal/winapi/types"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// CimFsWriter represents a writer to a single CimFS filesystem instance. On disk, the
|
||||
// image is composed of a filesystem file and several object ID and region files.
|
||||
// Note: The CimFsWriter isn't thread safe!
|
||||
type CimFsWriter struct {
|
||||
// name of this cim. Usually a <name>.cim file will be created to represent this cim.
|
||||
name string
|
||||
// handle is the CIMFS_IMAGE_HANDLE that must be passed when calling CIMFS APIs.
|
||||
handle winapitypes.FsHandle
|
||||
// name of the active file i.e the file to which we are currently writing.
|
||||
activeName string
|
||||
// stream to currently active file.
|
||||
activeStream winapitypes.StreamHandle
|
||||
// amount of bytes that can be written to the activeStream.
|
||||
activeLeft uint64
|
||||
// if true the CIM will be sealed after the writer is closed.
|
||||
sealOnClose bool
|
||||
}
|
||||
|
||||
// Create creates a new cim image. The CimFsWriter returned can then be used to do
|
||||
// operations on this cim. If `oldFSName` is provided the new image is "forked" from the
|
||||
// CIM with name `oldFSName` located under `imagePath`.
|
||||
func Create(imagePath string, oldFSName string, newFSName string) (_ *CimFsWriter, err error) {
|
||||
var oldNameBytes *uint16
|
||||
// CimCreateImage API call has different behavior if the value of oldNameBytes / newNameBytes
|
||||
// is empty than if it is nil. So we have to convert those strings into *uint16 here.
|
||||
fsName := oldFSName
|
||||
if oldFSName != "" {
|
||||
oldNameBytes, err = windows.UTF16PtrFromString(oldFSName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var newNameBytes *uint16
|
||||
if newFSName != "" {
|
||||
fsName = newFSName
|
||||
newNameBytes, err = windows.UTF16PtrFromString(newFSName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var handle winapitypes.FsHandle
|
||||
if err := winapi.CimCreateImage(imagePath, oldNameBytes, newNameBytes, &handle); err != nil {
|
||||
return nil, fmt.Errorf("failed to create cim image at path %s, oldName: %s, newName: %s: %w", imagePath, oldFSName, newFSName, err)
|
||||
}
|
||||
return &CimFsWriter{handle: handle, name: filepath.Join(imagePath, fsName)}, nil
|
||||
}
|
||||
|
||||
// blockCIMConfig represents options for creating or merging block CIMs
|
||||
type blockCIMConfig struct {
|
||||
// ensures that the generted CIM is identical every time when created from the same source data.
|
||||
// This is mostly required for image layers. Dissabled by default.
|
||||
consistentCIM bool
|
||||
// enables data integrity checking, which means the CIM will be verified and sealed on close.
|
||||
// This is useful for ensuring that the CIM is tamper-proof. Disabled by default.
|
||||
dataIntegrity bool
|
||||
}
|
||||
|
||||
// BlockCIMOpt is a function type for configuring block CIM creation options
|
||||
type BlockCIMOpt func(*blockCIMConfig) error
|
||||
|
||||
// enabled consistent CIM creation, this ensures that CIMs created from identical source data will always be identical (i.e. SHA256 digest of the CIM will remain same)
|
||||
func WithConsistentCIM() BlockCIMOpt {
|
||||
return func(opts *blockCIMConfig) error {
|
||||
opts.consistentCIM = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithDataIntegrity enables data integrity checking (verified CIM with sealing on close)
|
||||
func WithDataIntegrity() BlockCIMOpt {
|
||||
return func(opts *blockCIMConfig) error {
|
||||
opts.dataIntegrity = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// CreateBlockCIMWithOptions creates a new block CIM with the specified options and opens it for writing.
|
||||
// The CimFsWriter returned can then be used to add/remove files to/from this CIM.
|
||||
func CreateBlockCIMWithOptions(ctx context.Context, bCIM *BlockCIM, options ...BlockCIMOpt) (_ *CimFsWriter, err error) {
|
||||
// Apply default options
|
||||
config := &blockCIMConfig{}
|
||||
|
||||
// Apply provided options
|
||||
for _, option := range options {
|
||||
if err = option(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Validate options
|
||||
if bCIM.BlockPath == "" || bCIM.CimName == "" {
|
||||
return nil, fmt.Errorf("both blockPath & name must be non empty: %w", os.ErrInvalid)
|
||||
}
|
||||
|
||||
if bCIM.Type == BlockCIMTypeNone {
|
||||
return nil, fmt.Errorf("invalid block CIM type `%d`: %w", bCIM.Type, os.ErrInvalid)
|
||||
}
|
||||
|
||||
// Check OS support
|
||||
if !IsBlockCimWriteSupported() {
|
||||
return nil, fmt.Errorf("block CIM not supported on this OS version")
|
||||
}
|
||||
|
||||
if config.dataIntegrity && !IsVerifiedCimWriteSupported() {
|
||||
return nil, fmt.Errorf("verified CIMs are not supported on this OS version")
|
||||
}
|
||||
|
||||
// Build create flags based on options
|
||||
var createFlags uint32
|
||||
if config.consistentCIM {
|
||||
createFlags |= CimCreateFlagConsistentCim
|
||||
}
|
||||
if config.dataIntegrity {
|
||||
createFlags |= CimCreateFlagVerifiedCim
|
||||
}
|
||||
|
||||
switch bCIM.Type {
|
||||
case BlockCIMTypeDevice:
|
||||
createFlags |= CimCreateFlagBlockDeviceCim
|
||||
case BlockCIMTypeSingleFile:
|
||||
createFlags |= CimCreateFlagSingleFileCim
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid block CIM type `%d`: %w", bCIM.Type, os.ErrInvalid)
|
||||
}
|
||||
|
||||
winapi.LogCimDLLSupport()
|
||||
|
||||
var newNameUTF16 *uint16
|
||||
newNameUTF16, err = windows.UTF16PtrFromString(bCIM.CimName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var handle winapitypes.FsHandle
|
||||
if err := winapi.CimCreateImage2(bCIM.BlockPath, createFlags, nil, newNameUTF16, &handle); err != nil {
|
||||
return nil, fmt.Errorf("failed to create block CIM at path %s,%s: %w", bCIM.BlockPath, bCIM.CimName, err)
|
||||
}
|
||||
|
||||
return &CimFsWriter{
|
||||
handle: handle,
|
||||
name: filepath.Join(bCIM.BlockPath, bCIM.CimName),
|
||||
sealOnClose: config.dataIntegrity, // Seal on close if data integrity is enabled
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates a new block CIM and opens it for writing. The CimFsWriter
|
||||
// returned can then be used to add/remove files to/from this CIM.
|
||||
func CreateBlockCIM(blockPath, name string, blockType BlockCIMType) (_ *CimFsWriter, err error) {
|
||||
return CreateBlockCIMWithOptions(context.Background(), &BlockCIM{
|
||||
Type: blockType,
|
||||
BlockPath: blockPath,
|
||||
CimName: name,
|
||||
}, WithConsistentCIM())
|
||||
}
|
||||
|
||||
// CreateAlternateStream creates alternate stream of given size at the given path inside the cim. This will
|
||||
// replace the current active stream. Always, finish writing current active stream and then create an
|
||||
// alternate stream.
|
||||
func (c *CimFsWriter) CreateAlternateStream(path string, size uint64) (err error) {
|
||||
err = c.closeStream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = winapi.CimCreateAlternateStream(c.handle, path, size, &c.activeStream)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create alternate stream for path %s: %w", path, err)
|
||||
}
|
||||
c.activeName = path
|
||||
return nil
|
||||
}
|
||||
|
||||
// closes the currently active stream.
|
||||
func (c *CimFsWriter) closeStream() error {
|
||||
if c.activeStream == 0 {
|
||||
return nil
|
||||
}
|
||||
err := winapi.CimCloseStream(c.activeStream)
|
||||
if err == nil && c.activeLeft > 0 {
|
||||
// Validate here because CimCloseStream does not and this improves error
|
||||
// reporting. Otherwise the error will occur in the context of
|
||||
// cimWriteStream.
|
||||
err = fmt.Errorf("incomplete write, %d bytes left in the stream %s", c.activeLeft, c.activeName)
|
||||
}
|
||||
if err != nil {
|
||||
err = &PathError{Cim: c.name, Op: "closeStream", Path: c.activeName, Err: err}
|
||||
}
|
||||
c.activeLeft = 0
|
||||
c.activeStream = 0
|
||||
c.activeName = ""
|
||||
return err
|
||||
}
|
||||
|
||||
// AddFile adds a new file to the image. The file is added at the specified path. After
|
||||
// calling this function, the file is set as the active stream for the image, so data can
|
||||
// be written by calling `Write`.
|
||||
func (c *CimFsWriter) AddFile(path string, info *winio.FileBasicInfo, fileSize int64, securityDescriptor []byte, extendedAttributes []byte, reparseData []byte) error {
|
||||
err := c.closeStream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileMetadata := &winapitypes.CimFsFileMetadata{
|
||||
Attributes: info.FileAttributes,
|
||||
FileSize: fileSize,
|
||||
CreationTime: info.CreationTime,
|
||||
LastWriteTime: info.LastWriteTime,
|
||||
ChangeTime: info.ChangeTime,
|
||||
LastAccessTime: info.LastAccessTime,
|
||||
}
|
||||
if len(securityDescriptor) == 0 {
|
||||
// Passing an empty security descriptor creates a CIM in a weird state.
|
||||
// Pass the NULL DACL.
|
||||
securityDescriptor = nullSd
|
||||
}
|
||||
fileMetadata.SecurityDescriptorBuffer = unsafe.Pointer(&securityDescriptor[0])
|
||||
fileMetadata.SecurityDescriptorSize = uint32(len(securityDescriptor))
|
||||
if len(reparseData) > 0 {
|
||||
fileMetadata.ReparseDataBuffer = unsafe.Pointer(&reparseData[0])
|
||||
fileMetadata.ReparseDataSize = uint32(len(reparseData))
|
||||
}
|
||||
if len(extendedAttributes) > 0 {
|
||||
fileMetadata.ExtendedAttributes = unsafe.Pointer(&extendedAttributes[0])
|
||||
fileMetadata.EACount = uint32(len(extendedAttributes))
|
||||
}
|
||||
// remove the trailing `\` if present, otherwise it trips off the cim writer
|
||||
path = strings.TrimSuffix(path, "\\")
|
||||
err = winapi.CimCreateFile(c.handle, path, fileMetadata, &c.activeStream)
|
||||
if err != nil {
|
||||
return &PathError{Cim: c.name, Op: "addFile", Path: path, Err: err}
|
||||
}
|
||||
c.activeName = path
|
||||
if info.FileAttributes&(windows.FILE_ATTRIBUTE_DIRECTORY) == 0 {
|
||||
c.activeLeft = uint64(fileSize)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes bytes to the active stream.
|
||||
func (c *CimFsWriter) Write(p []byte) (int, error) {
|
||||
if c.activeStream == 0 {
|
||||
return 0, fmt.Errorf("no active stream")
|
||||
}
|
||||
if uint64(len(p)) > c.activeLeft {
|
||||
return 0, &PathError{Cim: c.name, Op: "write", Path: c.activeName, Err: fmt.Errorf("wrote too much")}
|
||||
}
|
||||
err := winapi.CimWriteStream(c.activeStream, uintptr(unsafe.Pointer(&p[0])), uint32(len(p)))
|
||||
if err != nil {
|
||||
err = &PathError{Cim: c.name, Op: "write", Path: c.activeName, Err: err}
|
||||
return 0, err
|
||||
}
|
||||
c.activeLeft -= uint64(len(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// AddLink adds a hard link at `newPath` that points to `oldPath`.
|
||||
func (c *CimFsWriter) AddLink(oldPath string, newPath string) error {
|
||||
err := c.closeStream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = winapi.CimCreateHardLink(c.handle, newPath, oldPath)
|
||||
if err != nil {
|
||||
err = &LinkError{Cim: c.name, Op: "addLink", Old: oldPath, New: newPath, Err: err}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// AddMergedLink adds a hard link at `newPath` that points to `oldPath` in the
|
||||
// image. However unlike AddLink this link is resolved at merge time. This allows us to
|
||||
// create links to files that are in other CIMs.
|
||||
func (c *CimFsWriter) AddMergedLink(oldPath string, newPath string) error {
|
||||
err := c.closeStream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = winapi.CimCreateMergeLink(c.handle, newPath, oldPath)
|
||||
if err != nil {
|
||||
err = &LinkError{Cim: c.name, Op: "addMergedLink", Old: oldPath, New: newPath, Err: err}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Unlink deletes the file at `path` from the image. Note that the file MUST have been
|
||||
// already added to the image.
|
||||
func (c *CimFsWriter) Unlink(path string) error {
|
||||
err := c.closeStream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return winapi.CimDeletePath(c.handle, path)
|
||||
}
|
||||
|
||||
// Adds a tombstone at given path. This ensures that when the the CIMs are merged, the
|
||||
// file at this path from lower layers won't show up in a mounted CIM. In case of Unlink,
|
||||
// the file from the lower layers still shows up after merge.
|
||||
func (c *CimFsWriter) AddTombstone(path string) error {
|
||||
err := c.closeStream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return winapi.CimTombstoneFile(c.handle, path)
|
||||
}
|
||||
|
||||
func (c *CimFsWriter) commit() error {
|
||||
err := c.closeStream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = winapi.CimCommitImage(c.handle)
|
||||
if err != nil {
|
||||
err = &OpError{Cim: c.name, Op: "commit", Err: err}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Close closes the CimFS filesystem.
|
||||
func (c *CimFsWriter) Close() (err error) {
|
||||
if c.handle == 0 {
|
||||
return fmt.Errorf("invalid writer")
|
||||
}
|
||||
if err = c.commit(); err != nil {
|
||||
return &OpError{Cim: c.name, Op: "commit", Err: err}
|
||||
}
|
||||
err = winapi.CimCloseImage(c.handle)
|
||||
c.handle = 0
|
||||
if err != nil {
|
||||
return &OpError{Cim: c.name, Op: "close", Err: err}
|
||||
}
|
||||
if c.sealOnClose {
|
||||
if err = sealBlockCIM(filepath.Dir(c.name)); err != nil {
|
||||
return &OpError{Cim: filepath.Dir(c.name), Op: "seal", Err: err}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DestroyCim finds out the region files, object files of this cim and then delete the
|
||||
// region files, object files and the <layer-id>.cim file itself. Note that any other
|
||||
// CIMs that were forked off of this CIM would become unusable after this operation. This
|
||||
// should not be used for block CIMs, os.Remove is sufficient for block CIMs.
|
||||
func DestroyCim(ctx context.Context, cimPath string) (retErr error) {
|
||||
regionFilePaths, err := getRegionFilePaths(ctx, cimPath)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Warnf("get region files for cim %s", cimPath)
|
||||
if retErr == nil { //nolint:govet // nilness: consistency with below
|
||||
retErr = err
|
||||
}
|
||||
}
|
||||
objectFilePaths, err := getObjectIDFilePaths(ctx, cimPath)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Warnf("get objectid file for cim %s", cimPath)
|
||||
if retErr == nil {
|
||||
retErr = err
|
||||
}
|
||||
}
|
||||
|
||||
log.G(ctx).WithFields(logrus.Fields{
|
||||
"cimPath": cimPath,
|
||||
"regionFiles": regionFilePaths,
|
||||
"objectFiles": objectFilePaths,
|
||||
}).Debug("destroy cim")
|
||||
|
||||
for _, regFilePath := range regionFilePaths {
|
||||
if err := os.Remove(regFilePath); err != nil {
|
||||
log.G(ctx).WithError(err).Warnf("remove file %s", regFilePath)
|
||||
if retErr == nil {
|
||||
retErr = err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, objFilePath := range objectFilePaths {
|
||||
if err := os.Remove(objFilePath); err != nil {
|
||||
log.G(ctx).WithError(err).Warnf("remove file %s", objFilePath)
|
||||
if retErr == nil {
|
||||
retErr = err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Remove(cimPath); err != nil {
|
||||
log.G(ctx).WithError(err).Warnf("remove file %s", cimPath)
|
||||
if retErr == nil {
|
||||
retErr = err
|
||||
}
|
||||
}
|
||||
return retErr
|
||||
}
|
||||
|
||||
// GetCimUsage returns the total disk usage in bytes by the cim at path `cimPath`.
|
||||
func GetCimUsage(ctx context.Context, cimPath string) (uint64, error) {
|
||||
regionFilePaths, err := getRegionFilePaths(ctx, cimPath)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("get region file paths for cim %s: %w", cimPath, err)
|
||||
}
|
||||
objectFilePaths, err := getObjectIDFilePaths(ctx, cimPath)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("get objectid file for cim %s: %w", cimPath, err)
|
||||
}
|
||||
|
||||
var totalUsage uint64
|
||||
for _, f := range append(regionFilePaths, objectFilePaths...) {
|
||||
fi, err := os.Stat(f)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("stat file %s: %w", f, err)
|
||||
}
|
||||
totalUsage += uint64(fi.Size())
|
||||
}
|
||||
return totalUsage, nil
|
||||
}
|
||||
|
||||
// MergeBlockCIMs creates a new merged BlockCIM from the provided source BlockCIMs. CIM
|
||||
// at index 0 is considered to be topmost CIM and the CIM at index `length-1` is
|
||||
// considered the base CIM. (i.e file with the same path in CIM at index 0 will shadow
|
||||
// files with the same path at all other CIMs) When mounting this merged CIM the source
|
||||
// CIMs MUST be provided in the exact same order.
|
||||
func MergeBlockCIMsWithOpts(ctx context.Context, mergedCIM *BlockCIM, sourceCIMs []*BlockCIM, opts ...BlockCIMOpt) (err error) {
|
||||
if !IsMergedCimWriteSupported() {
|
||||
return fmt.Errorf("merged CIMs aren't supported on this OS version")
|
||||
} else if len(sourceCIMs) < 2 {
|
||||
return fmt.Errorf("need at least 2 source CIMs, got %d: %w", len(sourceCIMs), os.ErrInvalid)
|
||||
}
|
||||
|
||||
// Apply default options
|
||||
config := &blockCIMConfig{}
|
||||
|
||||
// Apply provided options
|
||||
for _, opt := range opts {
|
||||
if err = opt(config); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, sCIM := range sourceCIMs {
|
||||
if sCIM.Type != mergedCIM.Type {
|
||||
return fmt.Errorf("source CIM (%s) type MUST match with merged CIM type: %w", sCIM.String(), os.ErrInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
var mergeFlags uint32
|
||||
switch mergedCIM.Type {
|
||||
case BlockCIMTypeDevice:
|
||||
mergeFlags = CimMergeFlagBlockDevice
|
||||
case BlockCIMTypeSingleFile:
|
||||
mergeFlags = CimMergeFlagSingleFile
|
||||
default:
|
||||
return fmt.Errorf("invalid block CIM type `%d`: %w", mergedCIM.Type, os.ErrInvalid)
|
||||
}
|
||||
|
||||
if config.dataIntegrity {
|
||||
mergeFlags |= CimMergeFlagVerifiedCim
|
||||
}
|
||||
|
||||
cim, err := CreateBlockCIMWithOptions(ctx, mergedCIM, opts...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create merged CIM: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
cErr := cim.Close()
|
||||
if err == nil {
|
||||
err = cErr
|
||||
}
|
||||
}()
|
||||
|
||||
// CimAddFsToMergedImage expects that topmost CIM is added first and the bottom
|
||||
// most CIM is added last.
|
||||
for _, sCIM := range sourceCIMs {
|
||||
fullPath := filepath.Join(sCIM.BlockPath, sCIM.CimName)
|
||||
if err := winapi.CimAddFsToMergedImage2(cim.handle, fullPath, mergeFlags); err != nil {
|
||||
return fmt.Errorf("add cim to merged image: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MergeBlockCIMs creates a new merged BlockCIM from the provided source BlockCIMs. CIM
|
||||
// at index 0 is considered to be topmost CIM and the CIM at index `length-1` is
|
||||
// considered the base CIM. (i.e file with the same path in CIM at index 0 will shadow
|
||||
// files with the same path at all other CIMs) When mounting this merged CIM the source
|
||||
// CIMs MUST be provided in the exact same order.
|
||||
func MergeBlockCIMs(mergedCIM *BlockCIM, sourceCIMs []*BlockCIM) (err error) {
|
||||
return MergeBlockCIMsWithOpts(context.Background(), mergedCIM, sourceCIMs, WithConsistentCIM())
|
||||
}
|
||||
|
||||
// sealBlockCIM seals a blockCIM at the given path so that no further modifications are allowed on it. This also writes a
|
||||
// root hash in the block header so that in future any reads happening on the CIM can be easily verified against this root hash
|
||||
// to detect tampering.
|
||||
func sealBlockCIM(blockPath string) error {
|
||||
var hashSize, fixedHeaderSize uint64
|
||||
hashBuf := make([]byte, cimHashSize)
|
||||
|
||||
// the blockPath could be a path to a block device or a file. In either case there should be no trailing backslash.
|
||||
blockPath = strings.TrimSuffix(blockPath, "\\")
|
||||
|
||||
if err := winapi.CimSealImage(blockPath, &hashSize, &fixedHeaderSize, &hashBuf[0]); err != nil {
|
||||
return fmt.Errorf("failed to seal block CIM: %w", err)
|
||||
} else if hashSize != cimHashSize {
|
||||
return fmt.Errorf("unexpected cim hash size %d", hashSize)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetVerificationInfo returns the root digest of the given block CIM. This is only
|
||||
// applicable for CIMs that are sealed after writing.
|
||||
func GetVerificationInfo(blockPath string) ([]byte, error) {
|
||||
var (
|
||||
isSealed uint32
|
||||
hashSize uint64
|
||||
signatureSize uint64
|
||||
fixedHeaderSize uint64
|
||||
hash = make([]byte, cimHashSize)
|
||||
)
|
||||
if err := winapi.CimGetVerificationInformation(blockPath, &isSealed, &hashSize, &signatureSize, &fixedHeaderSize, &hash[0], nil); err != nil {
|
||||
return nil, fmt.Errorf("failed to get verification info from the CIM: %w", err)
|
||||
} else if hashSize != cimHashSize {
|
||||
return nil, fmt.Errorf("unexpected cim hash size %d", hashSize)
|
||||
} else if isSealed == 0 {
|
||||
return nil, fmt.Errorf("cim is not sealed")
|
||||
}
|
||||
return hash, nil
|
||||
}
|
||||
161
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/cimfs.go
generated
vendored
Normal file
161
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/cimfs.go
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cimfs
|
||||
|
||||
import (
|
||||
"github.com/Microsoft/hcsshim/internal/winapi/cimfs"
|
||||
"github.com/Microsoft/hcsshim/internal/winapi/cimwriter"
|
||||
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Microsoft/hcsshim/osversion"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func IsCimFSSupported() bool {
|
||||
rv, err := osversion.BuildRevision()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Warn("get build revision")
|
||||
}
|
||||
build := osversion.Build()
|
||||
// CimFS support is backported to LTSC2022 starting with revision 2031 and should
|
||||
// otherwise be available on all builds >= V25H1Server
|
||||
return (build >= osversion.V25H1Server || (build == osversion.V21H2Server && rv >= 2031)) && cimfs.Supported()
|
||||
}
|
||||
|
||||
// IsBlockCimSupported returns true if block formatted CIMs (i.e block device CIM &
|
||||
// single file CIM) are supported on the current OS build.
|
||||
func IsBlockCimSupported() bool {
|
||||
build := osversion.Build()
|
||||
// TODO(ambarve): Currently we are checking against a higher build number since there is no
|
||||
// official build with block CIM support yet. Once we have that build, we should
|
||||
// update the build number here.
|
||||
return build >= 27766 && cimfs.Supported()
|
||||
}
|
||||
|
||||
// IsBlockCimWriteSupported returns true if block formatted CIMs (i.e block device CIM &
|
||||
// single file CIM) are supported on the current OS build or if CimWriter is present.
|
||||
func IsBlockCimWriteSupported() bool {
|
||||
// TODO(ambarve): Currently we are checking against a higher build number since there is no
|
||||
// official build with block CIM support yet. Once we have that build, we should
|
||||
// update the build number here.
|
||||
return IsBlockCimSupported() || cimwriter.Supported()
|
||||
}
|
||||
|
||||
// IsBlockCimMountSupported returns true if block formatted CIMs (i.e block device CIM &
|
||||
// single file CIM) are supported on the current OS build.
|
||||
func IsBlockCimMountSupported() bool {
|
||||
// TODO(ambarve): Currently we are checking against a higher build number since there is no
|
||||
// official build with block CIM support yet. Once we have that build, we should
|
||||
// update the build number here.
|
||||
return IsBlockCimSupported()
|
||||
}
|
||||
|
||||
// IsVerifiedCimSupported returns true if block CIM format supports also writing verification information in the CIM.
|
||||
func IsVerifiedCimSupported() bool {
|
||||
build := osversion.Build()
|
||||
// TODO(ambarve): Currently we are checking against a higher build number since there is no
|
||||
// official build with block CIM support yet. Once we have that build, we should
|
||||
// update the build number here.
|
||||
return build >= 27800 && cimfs.Supported()
|
||||
}
|
||||
|
||||
// IsVerifiedCimWriteSupported returns true if block CIM format supports also writing verification information in the CIM.
|
||||
func IsVerifiedCimWriteSupported() bool {
|
||||
// TODO(ambarve): Currently we are checking against a higher build number since there is no
|
||||
// official build with block CIM support yet. Once we have that build, we should
|
||||
// update the build number here.
|
||||
return IsVerifiedCimSupported() || cimwriter.Supported()
|
||||
}
|
||||
|
||||
// IsVerifiedCimMountSupported returns true if block CIM format supports mounting.
|
||||
func IsVerifiedCimMountSupported() bool {
|
||||
// TODO(ambarve): Currently we are checking against a higher build number since there is no
|
||||
// official build with block CIM support yet. Once we have that build, we should
|
||||
// update the build number here.
|
||||
return IsVerifiedCimSupported()
|
||||
}
|
||||
|
||||
func IsMergedCimSupported() bool {
|
||||
// The merged CIM support was originally added before block CIM support. However,
|
||||
// some of the merged CIM features that we use (e.g. merged hard links) were added
|
||||
// later along with block CIM support. So use the same check as block CIM here.
|
||||
return IsBlockCimSupported()
|
||||
}
|
||||
|
||||
func IsMergedCimWriteSupported() bool {
|
||||
// The merged CIM support was originally added before block CIM support. However,
|
||||
// some of the merged CIM features that we use (e.g. merged hard links) were added
|
||||
// later along with block CIM support. So use the same check as block CIM here.
|
||||
return IsBlockCimWriteSupported()
|
||||
}
|
||||
|
||||
func IsMergedCimMountSupported() bool {
|
||||
// The merged CIM support was originally added before block CIM support. However,
|
||||
// some of the merged CIM features that we use (e.g. merged hard links) were added
|
||||
// later along with block CIM support. So use the same check as block CIM here.
|
||||
return IsBlockCimMountSupported()
|
||||
}
|
||||
|
||||
type BlockCIMType uint32
|
||||
|
||||
const (
|
||||
BlockCIMTypeNone BlockCIMType = iota
|
||||
BlockCIMTypeSingleFile
|
||||
BlockCIMTypeDevice
|
||||
|
||||
CimMountFlagNone uint32 = 0x0
|
||||
CimMountFlagEnableDax uint32 = 0x2
|
||||
CimMountBlockDeviceCim uint32 = 0x10
|
||||
CimMountSingleFileCim uint32 = 0x20
|
||||
CimMountVerifiedCim uint32 = 0x80
|
||||
|
||||
CimCreateFlagNone uint32 = 0x0
|
||||
CimCreateFlagDoNotExpandPEImages uint32 = 0x1
|
||||
CimCreateFlagFixedSizeChunks uint32 = 0x2
|
||||
CimCreateFlagBlockDeviceCim uint32 = 0x4
|
||||
CimCreateFlagSingleFileCim uint32 = 0x8
|
||||
CimCreateFlagConsistentCim uint32 = 0x10
|
||||
CimCreateFlagVerifiedCim uint32 = 0x40
|
||||
|
||||
CimMergeFlagNone uint32 = 0x0
|
||||
CimMergeFlagSingleFile uint32 = 0x1
|
||||
CimMergeFlagBlockDevice uint32 = 0x2
|
||||
CimMergeFlagVerifiedCim uint32 = 0x4
|
||||
)
|
||||
|
||||
// BlockCIM represents a CIM stored in a block formatted way.
|
||||
//
|
||||
// A CIM usually is made up of a .cim file and multiple region & objectID
|
||||
// files. Currently, all of these files are stored together in the same directory. To
|
||||
// refer to such a CIM, we provide the path to the `.cim` file and the corresponding
|
||||
// region & objectID files are assumed to be present right next to it. In this case the
|
||||
// directory on the host's filesystem which holds one or more such CIMs is the container
|
||||
// for those CIMs.
|
||||
//
|
||||
// Using multiple files for a single CIM can be very limiting. (For example, if you want
|
||||
// to do a remote mount for a CIM layer, you now need to mount multiple files for a single
|
||||
// layer). In such cases having a single container which contains all of the CIM related
|
||||
// data is a great option. For this reason, CimFS has added support for a new type of a
|
||||
// CIM named BlockCIM. A BlockCIM is a CIM for which the container used to store all of
|
||||
// the CIM files is a block device or a binary file formatted like a block device. Such a
|
||||
// block device (or a binary file) doesn't have a separate filesystem (like NTFS or FAT32)
|
||||
// on it. Instead it is formatted in such a way that CimFS driver can read the blocks and
|
||||
// find out which CIMs are present on that block device. The CIMs stored on a raw block
|
||||
// device are sometimes referred to as block device CIMs and CIMs stored on the block
|
||||
// formatted single file are referred as single file CIMs.
|
||||
type BlockCIM struct {
|
||||
Type BlockCIMType
|
||||
// BlockPath is a path to the block device or the single file which contains the
|
||||
// CIM.
|
||||
BlockPath string
|
||||
// Since a block device CIM or a single file CIM can container multiple CIMs, we
|
||||
// refer to an individual CIM using its name.
|
||||
CimName string
|
||||
}
|
||||
|
||||
// added for logging convenience
|
||||
func (b *BlockCIM) String() string {
|
||||
return filepath.Join(b.BlockPath, b.CimName)
|
||||
}
|
||||
138
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/common.go
generated
vendored
Normal file
138
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/common.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cimfs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Microsoft/hcsshim/internal/log"
|
||||
"github.com/Microsoft/hcsshim/pkg/cimfs/format"
|
||||
)
|
||||
|
||||
const (
|
||||
cimHashSize = 32 // size of a hash of a verified CIM in bytes
|
||||
)
|
||||
|
||||
var (
|
||||
// Equivalent to SDDL of "D:NO_ACCESS_CONTROL".
|
||||
nullSd = []byte{1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
)
|
||||
|
||||
type OpError struct {
|
||||
Cim string
|
||||
Op string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *OpError) Error() string {
|
||||
s := "cim " + e.Op + " " + e.Cim
|
||||
s += ": " + e.Err.Error()
|
||||
return s
|
||||
}
|
||||
|
||||
// PathError is the error type returned by most functions in this package.
|
||||
type PathError struct {
|
||||
Cim string
|
||||
Op string
|
||||
Path string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *PathError) Error() string {
|
||||
s := "cim " + e.Op + " " + e.Cim
|
||||
s += ":" + e.Path
|
||||
s += ": " + e.Err.Error()
|
||||
return s
|
||||
}
|
||||
|
||||
type LinkError struct {
|
||||
Cim string
|
||||
Op string
|
||||
Old string
|
||||
New string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *LinkError) Error() string {
|
||||
return "cim " + e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error()
|
||||
}
|
||||
|
||||
func validateHeader(h *format.CommonHeader) error {
|
||||
if !bytes.Equal(h.Magic[:], format.MagicValue[:]) {
|
||||
return fmt.Errorf("not a cim file")
|
||||
}
|
||||
if h.Version.Major > format.CurrentVersion.Major || h.Version.Major < format.MinSupportedVersion.Major {
|
||||
return fmt.Errorf("unsupported cim version. cim version %v must be between %v & %v", h.Version, format.MinSupportedVersion, format.CurrentVersion)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readFilesystemHeader(f *os.File) (format.FilesystemHeader, error) {
|
||||
var fsh format.FilesystemHeader
|
||||
|
||||
if err := binary.Read(f, binary.LittleEndian, &fsh); err != nil {
|
||||
return fsh, fmt.Errorf("reading filesystem header: %w", err)
|
||||
}
|
||||
|
||||
if err := validateHeader(&fsh.Common); err != nil {
|
||||
return fsh, fmt.Errorf("validating filesystem header: %w", err)
|
||||
}
|
||||
return fsh, nil
|
||||
}
|
||||
|
||||
// Returns the paths of all the objectID files associated with the cim at `cimPath`.
|
||||
func getObjectIDFilePaths(ctx context.Context, cimPath string) ([]string, error) {
|
||||
f, err := os.Open(cimPath)
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("open cim file %s: %w", cimPath, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fsh, err := readFilesystemHeader(f)
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("readingp cim header: %w", err)
|
||||
}
|
||||
|
||||
paths := []string{}
|
||||
for i := 0; i < int(fsh.Regions.Count); i++ {
|
||||
path := filepath.Join(filepath.Dir(cimPath), fmt.Sprintf("%s_%v_%d", format.ObjectIDFileName, fsh.Regions.ID, i))
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
paths = append(paths, path)
|
||||
} else {
|
||||
log.G(ctx).WithError(err).Warnf("stat for object file %s", path)
|
||||
}
|
||||
|
||||
}
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
// Returns the paths of all the region files associated with the cim at `cimPath`.
|
||||
func getRegionFilePaths(ctx context.Context, cimPath string) ([]string, error) {
|
||||
f, err := os.Open(cimPath)
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("open cim file %s: %w", cimPath, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fsh, err := readFilesystemHeader(f)
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("reading cim header: %w", err)
|
||||
}
|
||||
|
||||
paths := []string{}
|
||||
for i := 0; i < int(fsh.Regions.Count); i++ {
|
||||
path := filepath.Join(filepath.Dir(cimPath), fmt.Sprintf("%s_%v_%d", format.RegionFileName, fsh.Regions.ID, i))
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
paths = append(paths, path)
|
||||
} else {
|
||||
log.G(ctx).WithError(err).Warnf("stat for region file %s", path)
|
||||
}
|
||||
}
|
||||
return paths, nil
|
||||
}
|
||||
99
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/doc.go
generated
vendored
Normal file
99
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/doc.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
This package provides simple go wrappers on top of the win32 CIMFS APIs.
|
||||
|
||||
Details about CimFS & related win32 APIs can be found here:
|
||||
https://learn.microsoft.com/en-us/windows/win32/api/_cimfs/
|
||||
|
||||
Details about how CimFS is being used in containerd can be found here:
|
||||
https://github.com/containerd/containerd/issues/8346
|
||||
|
||||
CIM types:
|
||||
Currently we support 2 types of CIMs:
|
||||
- Standard/classic (for the lack of a better term) CIMs.
|
||||
- Block CIMs.
|
||||
|
||||
Standard CIMs store all the contents of a CIM in one or more region & objectID files. This
|
||||
means a single CIM is made up of a `.cim` file, one or more region files and one or more
|
||||
objectID files. All of these files MUST be present in the same directory in order for that
|
||||
CIM to work. Block CIMs store all the data of a CIM in a single block device. A VHD can be
|
||||
such a block device. For convenience CimFS also allows using a block formatted file as a
|
||||
block device.
|
||||
|
||||
Standard CIMs can be created with the `func Create(imagePath string, oldFSName string,
|
||||
newFSName string) (_ *CimFsWriter, err error)` function defined in this package, whereas
|
||||
block CIMs can be created with the `func CreateBlockCIM(blockPath, oldName, newName
|
||||
string, blockType BlockCIMType) (_ *CimFsWriter, err error)` function.
|
||||
|
||||
Verified CIMs:
|
||||
A block CIM can also provide integrity checking (via a hash/Merkel tree,
|
||||
similar to dm-verity on Linux). If a CIM is written and sealed, it generates a
|
||||
root hash of all of its contents and shares it back with the client. Any
|
||||
verified CIM can be mounted by passing a hash that we expect to be its root
|
||||
hash. All read operations on such a mounted CIM will then validate that the
|
||||
generated root hash matches with the one that was provided at mount time. If it
|
||||
doesn't match the read fails. This allows us to guarantee that the CIM based
|
||||
layered aren't being modified underneath us.
|
||||
|
||||
Forking & Merging CIMs:
|
||||
In container world, CIMs are used for storing container image layers. Usually, one layer
|
||||
is stored in one CIM. This means we need a way to combine multiple CIMs to create the
|
||||
rootfs of a container. This can be achieved either by forking the CIMs or merging the
|
||||
CIMs.
|
||||
|
||||
Forking CIMs:
|
||||
Forking means every time a CIM is created for a non-base layer, we fork it off of a parent
|
||||
layer CIM. This ensures that contents that are written to this CIM are merged with that of
|
||||
parent layer CIMs at the time of CIM creation itself. When such a CIM is mounted we get a
|
||||
combined view of the contents of this CIM as well as the parent CIM from which this CIM
|
||||
was forked. However, this means that all the CIMs MUST be stored in the same directory in
|
||||
order for forked CIMs to work. And every non-base layer CIM is dependent on all of its
|
||||
parent layer CIMs.
|
||||
|
||||
Merging CIMs:
|
||||
If we create one or more CIMs without forking them at the time of creation, we can still
|
||||
merge those CIMs later to create a new special type of CIM called merged CIM. When
|
||||
mounted, this merged CIM provides a view of the combined contents of all the layers that
|
||||
were merged. The advantage of this approach is that each layer CIM (also referred to as
|
||||
source CIMs in the context of merging CIMs) can be created & stored independent of its
|
||||
parent CIMs. (Currently we only support merging block CIMs).
|
||||
|
||||
In order to create a merged CIM we need at least 2 non-forked block CIMs (we can not merge
|
||||
forked & non-forked CIMs), these CIMs are also referred to as source CIMs. We first create
|
||||
a new CIM (for storing the merge) via the `CreateBlockCIM` API, then call
|
||||
`CimAddFsToMergedImage2` repeatedly to add the source CIMs one by one to the merged
|
||||
CIM. Closing the handle on this new CIM commits it automatically. The order in which
|
||||
source CIMs are added matters. A source CIM that was added before another source CIM takes
|
||||
precedence when merging the CIM contents. Crating this merged CIM only combines the
|
||||
metadata of all the source CIMs, however the actual data isn't copied to the merged
|
||||
CIM. This is why when mounting the merged CIM, we still need to provide paths to the
|
||||
source CIMs.
|
||||
|
||||
`CimMergeMountImage` is used to mount a merged CIM. This API expects an array of paths of
|
||||
the merged CIM and all the source CIMs. Note that the array MUST include the merged CIM
|
||||
path at the 0th index and all the source CIMs in the same order in which they were added
|
||||
at the time of creation of the merged CIM. For example, if we merged CIMs 1.cim & 2.cim by
|
||||
first adding 1.cim (via CimAddFsToMergedImage) and then adding 2.cim, then the array
|
||||
should be [merged.cim, 1.cim, 2.cim]
|
||||
|
||||
Merged CIM specific APIs.
|
||||
|
||||
`CimTombstoneFile`: is used for creating a tombstone file in a CIM. Tombstone file is
|
||||
similar to a whiteout file used in case of overlayFS. A tombstone's primary use case is
|
||||
for merged CIMs. When multiple source CIMs are merged, a tombstone file/directory ensures
|
||||
that any files with the same path in the lower layers (i.e source CIMs that are added
|
||||
after the CIM that has a tombstone) do not show up in the mounted filesystem view. For
|
||||
example, imagine 1.cim has a file at path `foo/bar.txt` and 2.cim has a tombstone at path
|
||||
`foo/bar.txt`. If a merged CIM is created by first adding 2.cim (via
|
||||
CimAddFsToMergedImage) and then adding 1.cim and then when that merged CIM is mounted,
|
||||
`foo/bar.txt` will not show up in the mounted filesystem. A tombstone isn't required when
|
||||
using forked CIMs, because we can just call `CimDeletePath` to remove a file from the
|
||||
lower layers in that case. However, that doesn't work for merged CIMs since at the time of
|
||||
writing one of the source CIMs, we can't delete files from other source CIMs.
|
||||
|
||||
`CimCreateMergeLink`: is used to create a file link that is resolved at the time of
|
||||
merging CIMs. This is required if we want to create a hardlink in one source CIM that
|
||||
points to a file in another source CIM. Such a hardlink can not be resolved at the time of
|
||||
writing the source CIM. It can only be resolved at the time of merge. This API allows us
|
||||
to create such cross layer hard links.
|
||||
*/
|
||||
package cimfs
|
||||
4
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/format/doc.go
generated
vendored
Normal file
4
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/format/doc.go
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
// format package maintains some basic structures to allows us to read header of a cim file. This is mostly
|
||||
// required to understand the region & objectid files associated with a particular cim. Otherwise, we don't
|
||||
// need to parse the cim format.
|
||||
package format
|
||||
61
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/format/format.go
generated
vendored
Normal file
61
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/format/format.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package format
|
||||
|
||||
import "github.com/Microsoft/go-winio/pkg/guid"
|
||||
|
||||
const (
|
||||
RegionFileName = "region"
|
||||
ObjectIDFileName = "objectid"
|
||||
)
|
||||
|
||||
// Magic specifies the magic number at the beginning of a file.
|
||||
type Magic [8]uint8
|
||||
|
||||
var MagicValue = Magic([8]uint8{'c', 'i', 'm', 'f', 'i', 'l', 'e', '0'})
|
||||
|
||||
type Version struct {
|
||||
Major, Minor uint32
|
||||
}
|
||||
|
||||
var CurrentVersion = Version{3, 0}
|
||||
|
||||
var MinSupportedVersion = Version{2, 0}
|
||||
|
||||
type FileType uint8
|
||||
|
||||
// RegionOffset encodes an offset to objects as index of the region file
|
||||
// containing the object and the byte offset within that file.
|
||||
type RegionOffset uint64
|
||||
|
||||
// CommonHeader is the common header for all CIM-related files.
|
||||
type CommonHeader struct {
|
||||
Magic Magic
|
||||
HeaderLength uint32
|
||||
Type FileType
|
||||
Reserved uint8
|
||||
Reserved2 uint16
|
||||
Version Version
|
||||
Reserved3 uint64
|
||||
}
|
||||
|
||||
type RegionSet struct {
|
||||
ID guid.GUID
|
||||
Count uint16
|
||||
Reserved uint16
|
||||
Reserved1 uint32
|
||||
}
|
||||
|
||||
// FilesystemHeader is the header for a filesystem file.
|
||||
//
|
||||
// The filesystem file points to the filesystem object inside a region
|
||||
// file and specifies regions sets.
|
||||
type FilesystemHeader struct {
|
||||
Common CommonHeader
|
||||
Regions RegionSet
|
||||
FilesystemOffset RegionOffset
|
||||
Reserved uint32
|
||||
Reserved1 uint16
|
||||
ParentCount uint16
|
||||
}
|
||||
214
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/mount_cim.go
generated
vendored
Normal file
214
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/mount_cim.go
generated
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cimfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Microsoft/go-winio/pkg/guid"
|
||||
"github.com/Microsoft/hcsshim/internal/winapi"
|
||||
winapitypes "github.com/Microsoft/hcsshim/internal/winapi/types"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type MountError struct {
|
||||
Cim string
|
||||
Op string
|
||||
VolumeGUID guid.GUID
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *MountError) Error() string {
|
||||
s := "cim " + e.Op
|
||||
if e.Cim != "" {
|
||||
s += " " + e.Cim
|
||||
}
|
||||
s += " " + e.VolumeGUID.String() + ": " + e.Err.Error()
|
||||
return s
|
||||
}
|
||||
|
||||
const (
|
||||
VolumePathFormat = "\\\\?\\Volume{%s}\\"
|
||||
)
|
||||
|
||||
// Mount mounts the given cim at a volume with given GUID. Returns the full volume
|
||||
// path if mount is successful.
|
||||
func Mount(cimPath string, volumeGUID guid.GUID, mountFlags uint32) (string, error) {
|
||||
if err := winapi.CimMountImage(filepath.Dir(cimPath), filepath.Base(cimPath), mountFlags, &volumeGUID); err != nil {
|
||||
return "", &MountError{Cim: cimPath, Op: "Mount", VolumeGUID: volumeGUID, Err: err}
|
||||
}
|
||||
return fmt.Sprintf(VolumePathFormat, volumeGUID.String()), nil
|
||||
}
|
||||
|
||||
// Unmount unmounts the cim at mounted at path `volumePath`.
|
||||
func Unmount(volumePath string) error {
|
||||
// The path is expected to be in the \\?\Volume{GUID}\ format
|
||||
if volumePath[len(volumePath)-1] != '\\' {
|
||||
volumePath += "\\"
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(volumePath, "\\\\?\\Volume{") || !strings.HasSuffix(volumePath, "}\\") {
|
||||
return errors.Errorf("volume path %s is not in the expected format", volumePath)
|
||||
}
|
||||
|
||||
trimmedStr := strings.TrimPrefix(volumePath, "\\\\?\\Volume{")
|
||||
trimmedStr = strings.TrimSuffix(trimmedStr, "}\\")
|
||||
|
||||
volGUID, err := guid.FromString(trimmedStr)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "guid parsing failed for %s", trimmedStr)
|
||||
}
|
||||
|
||||
if err := winapi.CimDismountImage(&volGUID); err != nil {
|
||||
return &MountError{VolumeGUID: volGUID, Op: "Unmount", Err: err}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MountMergedBlockCIMs mounts the given merged BlockCIM (usually created with
|
||||
// `MergeBlockCIMs`) at a volume with given GUID. The `sourceCIMs` MUST be identical
|
||||
// to the `sourceCIMs` passed to `MergeBlockCIMs` when creating this merged CIM.
|
||||
func MountMergedBlockCIMs(mergedCIM *BlockCIM, sourceCIMs []*BlockCIM, mountFlags uint32, volumeGUID guid.GUID) (string, error) {
|
||||
if !IsMergedCimMountSupported() {
|
||||
return "", fmt.Errorf("merged CIMs aren't supported on this OS version")
|
||||
} else if len(sourceCIMs) < 2 {
|
||||
return "", fmt.Errorf("need at least 2 source CIMs, got %d: %w", len(sourceCIMs), os.ErrInvalid)
|
||||
}
|
||||
|
||||
switch mergedCIM.Type {
|
||||
case BlockCIMTypeDevice:
|
||||
mountFlags |= CimMountBlockDeviceCim
|
||||
case BlockCIMTypeSingleFile:
|
||||
mountFlags |= CimMountSingleFileCim
|
||||
default:
|
||||
return "", fmt.Errorf("invalid block CIM type `%d`", mergedCIM.Type)
|
||||
}
|
||||
|
||||
for _, sCIM := range sourceCIMs {
|
||||
if sCIM.Type != mergedCIM.Type {
|
||||
return "", fmt.Errorf("source CIM (%s) type doesn't match with merged CIM type: %w", sCIM.String(), os.ErrInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
// win32 mount merged CIM API expects an array of all CIMs. 0th entry in the array
|
||||
// should be the merged CIM. All remaining entries should be the source CIM paths
|
||||
// in the same order that was used while creating the merged CIM.
|
||||
allcims := append([]*BlockCIM{mergedCIM}, sourceCIMs...)
|
||||
cimsToMerge := []winapitypes.CimFsImagePath{}
|
||||
for _, bcim := range allcims {
|
||||
// Trailing backslashes cause problems-remove those
|
||||
imageDir, err := windows.UTF16PtrFromString(strings.TrimRight(bcim.BlockPath, `\`))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("convert string to utf16: %w", err)
|
||||
}
|
||||
cimName, err := windows.UTF16PtrFromString(bcim.CimName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("convert string to utf16: %w", err)
|
||||
}
|
||||
|
||||
cimsToMerge = append(cimsToMerge, winapitypes.CimFsImagePath{
|
||||
ImageDir: imageDir,
|
||||
ImageName: cimName,
|
||||
})
|
||||
}
|
||||
|
||||
if err := winapi.CimMergeMountImage(uint32(len(cimsToMerge)), &cimsToMerge[0], mountFlags, &volumeGUID); err != nil {
|
||||
return "", &MountError{Cim: filepath.Join(mergedCIM.BlockPath, mergedCIM.CimName), Op: "MountMerged", Err: err}
|
||||
}
|
||||
return fmt.Sprintf(VolumePathFormat, volumeGUID.String()), nil
|
||||
}
|
||||
|
||||
// Mounts a verified block CIM with the provided root hash. The root hash is usually
|
||||
// returned when the CIM is sealed or the root hash can be queried from a block CIM.
|
||||
// Every read on the mounted volume will be verified to match against the provided root
|
||||
// hash if it doesn't, the read will fail. The CIM MUST have been created with the
|
||||
// verified creation flag.
|
||||
func MountVerifiedBlockCIM(bCIM *BlockCIM, mountFlags uint32, volumeGUID guid.GUID, rootHash []byte) (string, error) {
|
||||
if len(rootHash) != cimHashSize {
|
||||
return "", fmt.Errorf("unexpected root hash size %d, expected size is %d", len(rootHash), cimHashSize)
|
||||
}
|
||||
|
||||
// The CimMountVerifiedCim flag should only be used when using the regular mount
|
||||
// CIM API. That flag is required to tell that API that this is a verified
|
||||
// CIM. This API doesn't need that flag as it is already assumed that the CIM is
|
||||
// verified.
|
||||
switch bCIM.Type {
|
||||
case BlockCIMTypeDevice:
|
||||
mountFlags |= CimMountBlockDeviceCim
|
||||
case BlockCIMTypeSingleFile:
|
||||
mountFlags |= CimMountSingleFileCim
|
||||
default:
|
||||
return "", fmt.Errorf("invalid block CIM type `%d`: %w", bCIM.Type, os.ErrInvalid)
|
||||
}
|
||||
|
||||
if err := winapi.CimMountVerifiedImage(bCIM.BlockPath, bCIM.CimName, mountFlags, &volumeGUID, cimHashSize, &rootHash[0]); err != nil {
|
||||
return "", &MountError{Cim: bCIM.String(), Op: "MountVerifiedCIM", Err: err}
|
||||
}
|
||||
return fmt.Sprintf("\\\\?\\Volume{%s}\\", volumeGUID.String()), nil
|
||||
}
|
||||
|
||||
// MountMergedVerifiedBlockCIMs mounts the given merged verified BlockCIM (usually created
|
||||
// with `MergeBlockCIMs`) at a volume with given GUID, with the given root hash. The
|
||||
// `sourceCIMs` MUST be identical to the `sourceCIMs` passed to `MergeBlockCIMs` when
|
||||
// creating this merged CIM. The root hash is usually returned when the CIM is sealed or
|
||||
// the root hash can be queried from a block CIM. In case of merged CIMs, the root hash of
|
||||
// the merged CIM should be passed here. Every read on the mounted volume will be verified
|
||||
// to match against the provided root hash if it doesn't, the read will fail. The source
|
||||
// CIMs and the merged CIM MUST have been created with the verified creation flag.
|
||||
func MountMergedVerifiedBlockCIMs(mergedCIM *BlockCIM, sourceCIMs []*BlockCIM, mountFlags uint32, volumeGUID guid.GUID, rootHash []byte) (string, error) {
|
||||
if !IsVerifiedCimMountSupported() {
|
||||
return "", fmt.Errorf("verified CIMs aren't supported on this OS version")
|
||||
} else if len(sourceCIMs) < 2 {
|
||||
return "", fmt.Errorf("need at least 2 source CIMs, got %d: %w", len(sourceCIMs), os.ErrInvalid)
|
||||
} else if len(rootHash) != cimHashSize {
|
||||
return "", fmt.Errorf("unexpected root hash size %d, expected size is %d", len(rootHash), cimHashSize)
|
||||
}
|
||||
|
||||
switch mergedCIM.Type {
|
||||
case BlockCIMTypeDevice:
|
||||
mountFlags |= CimMountBlockDeviceCim
|
||||
case BlockCIMTypeSingleFile:
|
||||
mountFlags |= CimMountSingleFileCim
|
||||
default:
|
||||
return "", fmt.Errorf("invalid block CIM type `%d`", mergedCIM.Type)
|
||||
}
|
||||
|
||||
for _, sCIM := range sourceCIMs {
|
||||
if sCIM.Type != mergedCIM.Type {
|
||||
return "", fmt.Errorf("source CIM (%s) type doesn't match with merged CIM type: %w", sCIM.String(), os.ErrInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
// win32 mount merged CIM API expects an array of all CIMs. 0th entry in the array
|
||||
// should be the merged CIM. All remaining entries should be the source CIM paths
|
||||
// in the same order that was used while creating the merged CIM.
|
||||
allcims := append([]*BlockCIM{mergedCIM}, sourceCIMs...)
|
||||
cimsToMerge := []winapitypes.CimFsImagePath{}
|
||||
for _, bcim := range allcims {
|
||||
// Trailing backslashes cause problems-remove those
|
||||
imageDir, err := windows.UTF16PtrFromString(strings.TrimRight(bcim.BlockPath, `\`))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("convert string to utf16: %w", err)
|
||||
}
|
||||
cimName, err := windows.UTF16PtrFromString(bcim.CimName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("convert string to utf16: %w", err)
|
||||
}
|
||||
|
||||
cimsToMerge = append(cimsToMerge, winapitypes.CimFsImagePath{
|
||||
ImageDir: imageDir,
|
||||
ImageName: cimName,
|
||||
})
|
||||
}
|
||||
|
||||
if err := winapi.CimMergeMountVerifiedImage(uint32(len(cimsToMerge)), &cimsToMerge[0], mountFlags, &volumeGUID, cimHashSize, &rootHash[0]); err != nil {
|
||||
return "", &MountError{Cim: filepath.Join(mergedCIM.BlockPath, mergedCIM.CimName), Op: "MountMergedVerified", Err: err}
|
||||
}
|
||||
return fmt.Sprintf(VolumePathFormat, volumeGUID.String()), nil
|
||||
}
|
||||
372
vendor/github.com/Microsoft/hcsshim/pkg/ociwclayer/cim/import.go
generated
vendored
Normal file
372
vendor/github.com/Microsoft/hcsshim/pkg/ociwclayer/cim/import.go
generated
vendored
Normal file
@@ -0,0 +1,372 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cim
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Microsoft/go-winio/backuptar"
|
||||
"github.com/Microsoft/hcsshim/ext4/tar2ext4"
|
||||
"github.com/Microsoft/hcsshim/internal/log"
|
||||
"github.com/Microsoft/hcsshim/internal/wclayer/cim"
|
||||
"github.com/Microsoft/hcsshim/pkg/cimfs"
|
||||
"github.com/Microsoft/hcsshim/pkg/ociwclayer"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// ImportCimLayerFromTar reads a layer from an OCI layer tar stream and extracts it into
|
||||
// the CIM format at the specified path.
|
||||
// `layerPath` is the directory which can be used to store intermediate files generated during layer extraction (and these file are also used when extracting children layers of this layer)
|
||||
// `cimPath` is the path to the CIM in which layer files must be stored. Note that region & object files are created when writing to a CIM, these files will be created next to the `cimPath`.
|
||||
// `parentLayerCimPaths` are paths to the parent layer CIMs, ordered from highest to lowest, i.e the CIM at `parentLayerCimPaths[0]` will be the immediate parent of the layer that is being extracted here.
|
||||
// `parentLayerPaths` are paths to the parent layer directories. Ordered from highest to lowest.
|
||||
//
|
||||
// This function returns the total size of the layer's files, in bytes.
|
||||
func ImportCimLayerFromTar(ctx context.Context, r io.Reader, layerPath, cimPath string, parentLayerPaths, parentLayerCimPaths []string) (_ int64, err error) {
|
||||
log.G(ctx).WithFields(logrus.Fields{
|
||||
"layer path": layerPath,
|
||||
"layer cim path": cimPath,
|
||||
"parent layer paths": strings.Join(parentLayerPaths, ", "),
|
||||
"parent layer CIM paths": strings.Join(parentLayerCimPaths, ", "),
|
||||
}).Debug("Importing cim layer from tar")
|
||||
|
||||
err = os.MkdirAll(layerPath, 0755)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
w, err := cim.NewForkedCimLayerWriter(ctx, layerPath, cimPath, parentLayerPaths, parentLayerCimPaths)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
n, err := writeCimLayerFromTar(ctx, r, w)
|
||||
cerr := w.Close(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if cerr != nil {
|
||||
return 0, cerr
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
type blockCIMLayerImportConfig struct {
|
||||
// import layers with integrity enabled CIMs
|
||||
dataIntegrity bool
|
||||
// parent layers
|
||||
parentLayers []*cimfs.BlockCIM
|
||||
// append VHD footer to the import CIMs
|
||||
appendVHDFooter bool
|
||||
}
|
||||
|
||||
// BlockCIMOpt is a function type for configuring block CIM creation options
|
||||
type BlockCIMLayerImportOpt func(*blockCIMLayerImportConfig) error
|
||||
|
||||
func WithLayerIntegrity() BlockCIMLayerImportOpt {
|
||||
return func(opts *blockCIMLayerImportConfig) error {
|
||||
opts.dataIntegrity = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithVHDFooter() BlockCIMLayerImportOpt {
|
||||
return func(opts *blockCIMLayerImportConfig) error {
|
||||
opts.appendVHDFooter = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithParentLayers(parentLayers []*cimfs.BlockCIM) BlockCIMLayerImportOpt {
|
||||
return func(opts *blockCIMLayerImportConfig) error {
|
||||
opts.parentLayers = parentLayers
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetIntegrityChecksum(ctx context.Context, blockPath string, pathName string) (string, error) {
|
||||
log.G(ctx).Debugf("writing integrity checksum file for block CIM `%s`", blockPath)
|
||||
// for convenience write a file that has the hex encoded root digest of the generated verified CIM.
|
||||
// this same hex string can be used in the confidential policy.
|
||||
// also return the integrity checksum as a string for integrity-vhd tooling.
|
||||
digest, err := cimfs.GetVerificationInfo(blockPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to query verified info of the CIM layer: %w", err)
|
||||
}
|
||||
|
||||
digestStr := hex.EncodeToString(digest)
|
||||
|
||||
// only create a file if a path name is provided
|
||||
if pathName != "" {
|
||||
digestFile, err := os.Create(filepath.Join(filepath.Dir(blockPath), pathName))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create verification info file: %w", err)
|
||||
}
|
||||
defer digestFile.Close()
|
||||
|
||||
if wn, err := digestFile.WriteString(digestStr); err != nil {
|
||||
return "", fmt.Errorf("failed to write verification info: %w", err)
|
||||
} else if wn != len(digestStr) {
|
||||
return "", fmt.Errorf("incomplete write of verification info: %w", err)
|
||||
}
|
||||
}
|
||||
return digestStr, nil
|
||||
}
|
||||
|
||||
func ImportBlockCIMLayerWithOpts(ctx context.Context, r io.Reader, layer *cimfs.BlockCIM, opts ...BlockCIMLayerImportOpt) (_ int64, err error) {
|
||||
log.G(ctx).WithField("layer", layer).Debug("Importing block CIM layer from tar")
|
||||
|
||||
err = os.MkdirAll(filepath.Dir(layer.BlockPath), 0755)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
config := &blockCIMLayerImportConfig{}
|
||||
for _, opt := range opts {
|
||||
if err := opt(config); err != nil {
|
||||
return 0, fmt.Errorf("block CIM import config option failure: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.G(ctx).WithField("config", *config).Debug("layer import config")
|
||||
|
||||
bcimWriterOpts := []cimfs.BlockCIMOpt{}
|
||||
if config.dataIntegrity {
|
||||
bcimWriterOpts = append(bcimWriterOpts, cimfs.WithDataIntegrity())
|
||||
}
|
||||
|
||||
w, err := cim.NewBlockCIMLayerWriterWithOpts(ctx, layer, config.parentLayers, bcimWriterOpts...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
n, err := writeCimLayerFromTar(ctx, r, w)
|
||||
cerr := w.Close(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if cerr != nil {
|
||||
return 0, cerr
|
||||
}
|
||||
|
||||
if config.appendVHDFooter {
|
||||
log.G(ctx).Debugf("appending VHD footer to block CIM at `%s`", layer.BlockPath)
|
||||
if err = tar2ext4.ConvertFileToVhd(layer.BlockPath); err != nil {
|
||||
return 0, fmt.Errorf("append VHD footer to block CIM: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if config.dataIntegrity {
|
||||
if _, err = GetIntegrityChecksum(ctx, layer.BlockPath, "integrity_checksum"); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// ImportSingleFileCimLayerFromTar reads a layer from an OCI layer tar stream and extracts
|
||||
// it into the SingleFileCIM format.
|
||||
func ImportSingleFileCimLayerFromTar(ctx context.Context, r io.Reader, layer *cimfs.BlockCIM, parentLayers []*cimfs.BlockCIM) (_ int64, err error) {
|
||||
return ImportBlockCIMLayerWithOpts(ctx, r, layer, WithParentLayers(parentLayers))
|
||||
}
|
||||
|
||||
func writeCimLayerFromTar(ctx context.Context, r io.Reader, w cim.CIMLayerWriter) (int64, error) {
|
||||
tr := tar.NewReader(r)
|
||||
buf := bufio.NewWriter(w)
|
||||
size := int64(0)
|
||||
|
||||
// Iterate through the files in the archive.
|
||||
hdr, loopErr := tr.Next()
|
||||
for loopErr == nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return 0, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
// Note: path is used instead of filepath to prevent OS specific handling
|
||||
// of the tar path
|
||||
base := path.Base(hdr.Name)
|
||||
if strings.HasPrefix(base, ociwclayer.WhiteoutPrefix) {
|
||||
name := path.Join(path.Dir(hdr.Name), base[len(ociwclayer.WhiteoutPrefix):])
|
||||
if rErr := w.Remove(filepath.FromSlash(name)); rErr != nil {
|
||||
return 0, rErr
|
||||
}
|
||||
hdr, loopErr = tr.Next()
|
||||
} else if hdr.Typeflag == tar.TypeLink {
|
||||
if linkErr := w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname)); linkErr != nil {
|
||||
return 0, linkErr
|
||||
}
|
||||
hdr, loopErr = tr.Next()
|
||||
} else {
|
||||
name, fileSize, fileInfo, err := backuptar.FileInfoFromHeader(hdr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
sddl, err := backuptar.SecurityDescriptorFromTarHeader(hdr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
eadata, err := backuptar.ExtendedAttributesFromTarHeader(hdr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var reparse []byte
|
||||
// As of now the only valid reparse data in a layer will be for a symlink. If file is
|
||||
// a symlink set reparse attribute and ensure reparse data buffer isn't
|
||||
// empty. Otherwise remove the reparse attributed.
|
||||
fileInfo.FileAttributes &^= uint32(windows.FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
if hdr.Typeflag == tar.TypeSymlink {
|
||||
reparse = backuptar.EncodeReparsePointFromTarHeader(hdr)
|
||||
if len(reparse) > 0 {
|
||||
fileInfo.FileAttributes |= uint32(windows.FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
}
|
||||
}
|
||||
|
||||
if addErr := w.Add(filepath.FromSlash(name), fileInfo, fileSize, sddl, eadata, reparse); addErr != nil {
|
||||
return 0, addErr
|
||||
}
|
||||
if hdr.Typeflag == tar.TypeReg {
|
||||
if _, cpErr := io.Copy(buf, tr); cpErr != nil {
|
||||
return 0, cpErr
|
||||
}
|
||||
}
|
||||
size += fileSize
|
||||
|
||||
// Copy all the alternate data streams and return the next non-ADS header.
|
||||
var ahdr *tar.Header
|
||||
for {
|
||||
ahdr, loopErr = tr.Next()
|
||||
if loopErr != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if ahdr.Typeflag != tar.TypeReg || !strings.HasPrefix(ahdr.Name, hdr.Name+":") {
|
||||
hdr = ahdr
|
||||
break
|
||||
}
|
||||
|
||||
// stream names have following format: '<filename>:<stream name>:$DATA'
|
||||
// $DATA is one of the valid types of streams. We currently only support
|
||||
// data streams so fail if this is some other type of stream.
|
||||
if !strings.HasSuffix(ahdr.Name, ":$DATA") {
|
||||
return 0, fmt.Errorf("stream types other than $DATA are not supported, found: %s", ahdr.Name)
|
||||
}
|
||||
|
||||
if addErr := w.AddAlternateStream(filepath.FromSlash(ahdr.Name), uint64(ahdr.Size)); addErr != nil {
|
||||
return 0, addErr
|
||||
}
|
||||
|
||||
if _, cpErr := io.Copy(buf, tr); cpErr != nil {
|
||||
return 0, cpErr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if flushErr := buf.Flush(); flushErr != nil {
|
||||
if loopErr == nil {
|
||||
loopErr = flushErr
|
||||
} else {
|
||||
log.G(ctx).WithError(flushErr).Warn("flush buffer during layer write failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
if !errors.Is(loopErr, io.EOF) {
|
||||
return 0, loopErr
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
|
||||
// MergeBlockCIMLayersWithOpts create a new block CIM at mergedCIM.BlockPath and then
|
||||
// creates a new CIM inside that block CIM that merges all the contents of the provided
|
||||
// sourceCIMs. Note that this is only a metadata merge, so when this merged CIM is being
|
||||
// mounted, all the sourceCIMs must be provided too and they MUST be provided in the same
|
||||
// order. Expected order of sourceCIMs is that the base layer should be at the last index
|
||||
// and the topmost layer should be at 0'th index. Merge operation can take a long time in
|
||||
// certain situations, this function respects context deadlines in such cases. This
|
||||
// function is NOT thread safe, it is caller's responsibility to handle thread safety.
|
||||
func MergeBlockCIMLayersWithOpts(ctx context.Context, sourceCIMs []*cimfs.BlockCIM, mergedCIM *cimfs.BlockCIM, opts ...BlockCIMLayerImportOpt) (retErr error) {
|
||||
log.G(ctx).WithFields(logrus.Fields{
|
||||
"source CIMs": sourceCIMs,
|
||||
"merged CIM": mergedCIM,
|
||||
}).Debug("Merging block CIM layers")
|
||||
|
||||
// check if a merged CIM already exists
|
||||
_, err := os.Stat(mergedCIM.BlockPath)
|
||||
if err == nil {
|
||||
return os.ErrExist
|
||||
}
|
||||
|
||||
// Apply configuration options
|
||||
config := &blockCIMLayerImportConfig{}
|
||||
for _, opt := range opts {
|
||||
if err := opt(config); err != nil {
|
||||
return fmt.Errorf("apply merge option: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare options for the underlying cimfs.MergeBlockCIMsWithOpts call
|
||||
cimfsOpts := []cimfs.BlockCIMOpt{cimfs.WithConsistentCIM()}
|
||||
if config.dataIntegrity {
|
||||
cimfsOpts = append(cimfsOpts, cimfs.WithDataIntegrity())
|
||||
}
|
||||
|
||||
// Ensure the directory for the merged CIM exists
|
||||
if err := os.MkdirAll(filepath.Dir(mergedCIM.BlockPath), 0755); err != nil {
|
||||
return fmt.Errorf("create directory for merged CIM: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
if rmErr := os.Remove(mergedCIM.BlockPath); rmErr != nil {
|
||||
log.G(ctx).WithError(retErr).Warnf("error in cleanup on failure: %s", rmErr)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Run the merge operation in a goroutine to handle context cancellation
|
||||
// The merge operation can take a long time, so we need to respect context deadlines
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
defer close(errCh)
|
||||
err := cimfs.MergeBlockCIMsWithOpts(ctx, mergedCIM, sourceCIMs, cimfsOpts...)
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
// Wait for either the merge to complete or context to be cancelled
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
case err = <-errCh:
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("merge block CIMs failed: %w", err)
|
||||
}
|
||||
|
||||
// Handle VHD footer if requested
|
||||
if config.appendVHDFooter {
|
||||
log.G(ctx).Debugf("appending VHD footer to block CIM at `%s`", mergedCIM.BlockPath)
|
||||
if err = tar2ext4.ConvertFileToVhd(mergedCIM.BlockPath); err != nil {
|
||||
return fmt.Errorf("append VHD footer to block CIM: %w", err)
|
||||
}
|
||||
}
|
||||
if config.dataIntegrity {
|
||||
if _, err = GetIntegrityChecksum(ctx, mergedCIM.BlockPath, "merged_integrity_checksum"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
201
vendor/github.com/checkpoint-restore/checkpointctl/LICENSE
generated
vendored
Normal file
201
vendor/github.com/checkpoint-restore/checkpointctl/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
65
vendor/github.com/checkpoint-restore/checkpointctl/lib/annotations.go
generated
vendored
Normal file
65
vendor/github.com/checkpoint-restore/checkpointctl/lib/annotations.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
package metadata
|
||||
|
||||
const (
|
||||
// CheckpointAnnotationEngine specifies the name of the container engine (e.g., podman, cri-o).
|
||||
CheckpointAnnotationEngine = "org.criu.checkpoint.engine.name"
|
||||
|
||||
// CheckpointAnnotationEngineVersion specifies the version of the container engine.
|
||||
CheckpointAnnotationEngineVersion = "org.criu.checkpoint.engine.version"
|
||||
|
||||
// CheckpointAnnotationName specifies the name of the container associated with the checkpoint.
|
||||
CheckpointAnnotationName = "org.criu.checkpoint.container.name"
|
||||
|
||||
// CheckpointAnnotationPod specifies the name of the pod associated with the checkpoint.
|
||||
CheckpointAnnotationPod = "org.criu.checkpoint.pod.name"
|
||||
|
||||
// CheckpointAnnotationPodID specifies the ID of the pod associated with the checkpoint.
|
||||
CheckpointAnnotationPodID = "org.criu.checkpoint.pod.id"
|
||||
|
||||
// CheckpointAnnotationNamespace specifies the namespace of the pod associated with the checkpoint.
|
||||
CheckpointAnnotationNamespace = "org.criu.checkpoint.pod.namespace"
|
||||
|
||||
// CheckpointAnnotationRootfsImageName specifies the name of the root filesystem image associated with the checkpoint.
|
||||
CheckpointAnnotationRootfsImageName = "org.criu.checkpoint.rootfsImageName"
|
||||
|
||||
// CheckpointAnnotationRootfsImageUserRequested specifies the name of the root filesystem image requested by the user.
|
||||
CheckpointAnnotationRootfsImageUserRequested = "org.criu.checkpoint.rootfsImageUserRequested"
|
||||
|
||||
// CheckpointAnnotationRootfsImageSha specifies the SHA hash of the root filesystem image associated with the checkpoint.
|
||||
CheckpointAnnotationRootfsImageSha = "org.criu.checkpoint.rootfsImageSha"
|
||||
|
||||
// CheckpointAnnotationRootfsImageID specifies the ID of the root filesystem image associated with the checkpoint.
|
||||
CheckpointAnnotationRootfsImageID = "org.criu.checkpoint.rootfsImageID"
|
||||
|
||||
// CheckpointAnnotationRawImageName specifies the original unprocessed name of the image used to create the container.
|
||||
CheckpointAnnotationRawImageName = "org.criu.checkpoint.rawImageName"
|
||||
|
||||
// CheckpointAnnotationRuntimeName specifies the runtime used on the host where the checkpoint was created.
|
||||
CheckpointAnnotationRuntimeName = "org.criu.checkpoint.runtime.name"
|
||||
|
||||
// CheckpointAnnotationRuntimeVersion specifies the version of the runtime used on the host where the checkpoint was created.
|
||||
CheckpointAnnotationRuntimeVersion = "org.criu.checkpoint.runtime.version"
|
||||
|
||||
// CheckpointAnnotationCriuVersion specifies the version of CRIU used on the host where the checkpoint was created.
|
||||
CheckpointAnnotationCriuVersion = "org.criu.checkpoint.criu.version"
|
||||
|
||||
// CheckpointAnnotationConmonVersion specifies the version of conmon used on the host where the checkpoint was created.
|
||||
CheckpointAnnotationConmonVersion = "org.criu.checkpoint.conmon.version"
|
||||
|
||||
// CheckpointAnnotationHostArch specifies the CPU architecture of the host where the checkpoint was created.
|
||||
CheckpointAnnotationHostArch = "org.criu.checkpoint.host.arch"
|
||||
|
||||
// CheckpointAnnotationHostKernel specifies the kernel version used by the host where the checkpoint was created.
|
||||
CheckpointAnnotationHostKernel = "org.criu.checkpoint.host.kernel"
|
||||
|
||||
// CheckpointAnnotationCgroupVersion specifies the cgroup version used by the host where the checkpoint was created.
|
||||
CheckpointAnnotationCgroupVersion = "org.criu.checkpoint.cgroup.version"
|
||||
|
||||
// CheckpointAnnotationDistributionVersion specifies the name of the host distribution on which the checkpoint was created.
|
||||
// This annotation is particularly useful because some distributions may include non-upstream patches
|
||||
// that can cause CRIU (Checkpoint/Restore in Userspace) to fail.
|
||||
CheckpointAnnotationDistributionName = "org.criu.checkpoint.distribution.name"
|
||||
|
||||
// CheckpointAnnotationDistributionVersion specifies the version of the host distribution on which the checkpoint was created.
|
||||
CheckpointAnnotationDistributionVersion = "org.criu.checkpoint.distribution.version"
|
||||
)
|
||||
150
vendor/github.com/checkpoint-restore/checkpointctl/lib/metadata.go
generated
vendored
Normal file
150
vendor/github.com/checkpoint-restore/checkpointctl/lib/metadata.go
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
const (
|
||||
// container archive
|
||||
ConfigDumpFile = "config.dump"
|
||||
SpecDumpFile = "spec.dump"
|
||||
StatusDumpFile = "status.dump"
|
||||
NetworkStatusFile = "network.status"
|
||||
CheckpointDirectory = "checkpoint"
|
||||
CheckpointVolumesDirectory = "volumes"
|
||||
DevShmCheckpointTar = "devshm-checkpoint.tar"
|
||||
RootFsDiffTar = "rootfs-diff.tar"
|
||||
DeletedFilesFile = "deleted.files"
|
||||
DumpLogFile = "dump.log"
|
||||
RestoreLogFile = "restore.log"
|
||||
// pod archive
|
||||
PodOptionsFile = "pod.options"
|
||||
PodDumpFile = "pod.dump"
|
||||
// containerd only
|
||||
StatusFile = "status"
|
||||
// CRIU Images
|
||||
PagesPrefix = "pages-"
|
||||
AmdgpuPagesPrefix = "amdgpu-pages-"
|
||||
)
|
||||
|
||||
// This is a reduced copy of what Podman uses to store checkpoint metadata
|
||||
type ContainerConfig struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
RootfsImage string `json:"rootfsImage,omitempty"`
|
||||
RootfsImageRef string `json:"rootfsImageRef,omitempty"`
|
||||
RootfsImageName string `json:"rootfsImageName,omitempty"`
|
||||
OCIRuntime string `json:"runtime,omitempty"`
|
||||
CreatedTime time.Time `json:"createdTime"`
|
||||
CheckpointedAt time.Time `json:"checkpointedTime"`
|
||||
RestoredAt time.Time `json:"restoredTime"`
|
||||
Restored bool `json:"restored"`
|
||||
}
|
||||
|
||||
type Spec struct {
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
type ContainerdStatus struct {
|
||||
CreatedAt int64
|
||||
StartedAt int64
|
||||
FinishedAt int64
|
||||
ExitCode int32
|
||||
Pid uint32
|
||||
Reason string
|
||||
Message string
|
||||
}
|
||||
|
||||
// This structure is used by the KubernetesContainerCheckpointMetadata structure
|
||||
type KubernetesCheckpoint struct {
|
||||
Archive string `json:"archive,omitempty"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
Timestamp int64 `json:"timestamp,omitempty"`
|
||||
}
|
||||
|
||||
// This structure is the basis for Kubernetes to track how many checkpoints
|
||||
// for a certain container have been created.
|
||||
type KubernetesContainerCheckpointMetadata struct {
|
||||
PodFullName string `json:"podFullName,omitempty"`
|
||||
ContainerName string `json:"containerName,omitempty"`
|
||||
TotalSize int64 `json:"totalSize,omitempty"`
|
||||
Checkpoints []KubernetesCheckpoint `json:"checkpoints"`
|
||||
}
|
||||
|
||||
func ReadContainerCheckpointSpecDump(checkpointDirectory string) (*spec.Spec, string, error) {
|
||||
var specDump spec.Spec
|
||||
specDumpFile, err := ReadJSONFile(&specDump, checkpointDirectory, SpecDumpFile)
|
||||
|
||||
return &specDump, specDumpFile, err
|
||||
}
|
||||
|
||||
func ReadContainerCheckpointConfigDump(checkpointDirectory string) (*ContainerConfig, string, error) {
|
||||
var containerConfig ContainerConfig
|
||||
configDumpFile, err := ReadJSONFile(&containerConfig, checkpointDirectory, ConfigDumpFile)
|
||||
|
||||
return &containerConfig, configDumpFile, err
|
||||
}
|
||||
|
||||
func ReadContainerCheckpointDeletedFiles(checkpointDirectory string) ([]string, string, error) {
|
||||
var deletedFiles []string
|
||||
deletedFilesFile, err := ReadJSONFile(&deletedFiles, checkpointDirectory, DeletedFilesFile)
|
||||
|
||||
return deletedFiles, deletedFilesFile, err
|
||||
}
|
||||
|
||||
func ReadContainerCheckpointStatusFile(checkpointDirectory string) (*ContainerdStatus, string, error) {
|
||||
var containerdStatus ContainerdStatus
|
||||
statusFile, err := ReadJSONFile(&containerdStatus, checkpointDirectory, StatusFile)
|
||||
|
||||
return &containerdStatus, statusFile, err
|
||||
}
|
||||
|
||||
// WriteJSONFile marshalls and writes the given data to a JSON file
|
||||
func WriteJSONFile(v interface{}, dir, file string) (string, error) {
|
||||
fileJSON, err := json.MarshalIndent(v, "", " ")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error marshalling JSON: %w", err)
|
||||
}
|
||||
file = filepath.Join(dir, file)
|
||||
if err := os.WriteFile(file, fileJSON, 0o600); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func ReadJSONFile(v interface{}, dir, file string) (string, error) {
|
||||
file = filepath.Join(dir, file)
|
||||
content, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err = json.Unmarshal(content, v); err != nil {
|
||||
return "", fmt.Errorf("failed to unmarshal %s: %w", file, err)
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func ByteToString(b int64) string {
|
||||
const unit = 1024
|
||||
if b < unit {
|
||||
return fmt.Sprintf("%d B", b)
|
||||
}
|
||||
div, exp := int64(unit), 0
|
||||
for n := b / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%.1f %ciB",
|
||||
float64(b)/float64(div), "KMGTPE"[exp])
|
||||
}
|
||||
621
vendor/github.com/containerd/containerd/api/runtime/bootstrap/v1/bootstrap.pb.go
generated
vendored
Normal file
621
vendor/github.com/containerd/containerd/api/runtime/bootstrap/v1/bootstrap.pb.go
generated
vendored
Normal file
@@ -0,0 +1,621 @@
|
||||
//
|
||||
//Copyright The containerd Authors.
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
//http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
// Bootstrap Protocol
|
||||
//
|
||||
// This protocol defines the interface between containerd and shims at startup.
|
||||
// It replaces the previous scattered configuration mechanisms (CLI args, env vars,
|
||||
// stdin JSON, spec.json annotations) with a single, versioned, extensible protocol.
|
||||
//
|
||||
// Flow:
|
||||
// 1. containerd spawns the shim process
|
||||
// 2. containerd writes BootstrapParams as JSON to shim's stdin
|
||||
// 3. shim initializes and writes BootstrapResult as JSON to stdout
|
||||
// 4. containerd connects to the address provided in BootstrapResult
|
||||
//
|
||||
// This design enables:
|
||||
// - Forward/backward compatibility via version field
|
||||
// - Typed extensibility via google.protobuf.Any and Extension
|
||||
// - Clear capability negotiation between containerd and shims
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc (unknown)
|
||||
// source: runtime/bootstrap/v1/bootstrap.proto
|
||||
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
anypb "google.golang.org/protobuf/types/known/anypb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// LogLevel defines log verbosity. INFO = 0, more critical levels are
|
||||
// positive, and more verbose levels are negative. This is an open enum;
|
||||
// unknown numeric values are preserved on the wire.
|
||||
type LogLevel int32
|
||||
|
||||
const (
|
||||
LogLevel_LOG_LEVEL_INFO LogLevel = 0
|
||||
LogLevel_LOG_LEVEL_TRACE LogLevel = -8
|
||||
LogLevel_LOG_LEVEL_DEBUG LogLevel = -4
|
||||
LogLevel_LOG_LEVEL_WARN LogLevel = 4
|
||||
LogLevel_LOG_LEVEL_ERROR LogLevel = 8
|
||||
LogLevel_LOG_LEVEL_FATAL LogLevel = 10
|
||||
LogLevel_LOG_LEVEL_PANIC LogLevel = 12
|
||||
)
|
||||
|
||||
// Enum value maps for LogLevel.
|
||||
var (
|
||||
LogLevel_name = map[int32]string{
|
||||
0: "LOG_LEVEL_INFO",
|
||||
-8: "LOG_LEVEL_TRACE",
|
||||
-4: "LOG_LEVEL_DEBUG",
|
||||
4: "LOG_LEVEL_WARN",
|
||||
8: "LOG_LEVEL_ERROR",
|
||||
10: "LOG_LEVEL_FATAL",
|
||||
12: "LOG_LEVEL_PANIC",
|
||||
}
|
||||
LogLevel_value = map[string]int32{
|
||||
"LOG_LEVEL_INFO": 0,
|
||||
"LOG_LEVEL_TRACE": -8,
|
||||
"LOG_LEVEL_DEBUG": -4,
|
||||
"LOG_LEVEL_WARN": 4,
|
||||
"LOG_LEVEL_ERROR": 8,
|
||||
"LOG_LEVEL_FATAL": 10,
|
||||
"LOG_LEVEL_PANIC": 12,
|
||||
}
|
||||
)
|
||||
|
||||
func (x LogLevel) Enum() *LogLevel {
|
||||
p := new(LogLevel)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x LogLevel) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (LogLevel) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_runtime_bootstrap_v1_bootstrap_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (LogLevel) Type() protoreflect.EnumType {
|
||||
return &file_runtime_bootstrap_v1_bootstrap_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x LogLevel) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use LogLevel.Descriptor instead.
|
||||
func (LogLevel) EnumDescriptor() ([]byte, []int) {
|
||||
return file_runtime_bootstrap_v1_bootstrap_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
// Capability defines optional features that can be negotiated between
|
||||
// containerd and shims.
|
||||
type Capability int32
|
||||
|
||||
const (
|
||||
Capability_CAPABILITY_UNSPECIFIED Capability = 0
|
||||
)
|
||||
|
||||
// Enum value maps for Capability.
|
||||
var (
|
||||
Capability_name = map[int32]string{
|
||||
0: "CAPABILITY_UNSPECIFIED",
|
||||
}
|
||||
Capability_value = map[string]int32{
|
||||
"CAPABILITY_UNSPECIFIED": 0,
|
||||
}
|
||||
)
|
||||
|
||||
func (x Capability) Enum() *Capability {
|
||||
p := new(Capability)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x Capability) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (Capability) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_runtime_bootstrap_v1_bootstrap_proto_enumTypes[1].Descriptor()
|
||||
}
|
||||
|
||||
func (Capability) Type() protoreflect.EnumType {
|
||||
return &file_runtime_bootstrap_v1_bootstrap_proto_enumTypes[1]
|
||||
}
|
||||
|
||||
func (x Capability) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Capability.Descriptor instead.
|
||||
func (Capability) EnumDescriptor() ([]byte, []int) {
|
||||
return file_runtime_bootstrap_v1_bootstrap_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
// BootstrapParams contains all configuration passed from containerd to shim at startup.
|
||||
type BootstrapParams struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Container/sandbox ID
|
||||
InstanceID string `protobuf:"bytes,1,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"`
|
||||
// Namespace for the container
|
||||
Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"`
|
||||
// Requested shim log level.
|
||||
// INFO = 0, more critical levels are positive, and more verbose
|
||||
// levels are negative. This is an open enum; shims should treat
|
||||
// unrecognized values by mapping to the nearest known level.
|
||||
LogLevel LogLevel `protobuf:"varint,3,opt,name=log_level,json=logLevel,proto3,enum=containerd.runtime.bootstrap.v1.LogLevel" json:"log_level,omitempty"`
|
||||
// containerd daemon version that is launching this shim.
|
||||
ContainerdVersion string `protobuf:"bytes,4,opt,name=containerd_version,json=containerdVersion,proto3" json:"containerd_version,omitempty"`
|
||||
// Containerd's TTRPC API address (e.g., "unix:///run/containerd/containerd.sock.ttrpc")
|
||||
ContainerdTtrpcAddress string `protobuf:"bytes,5,opt,name=containerd_ttrpc_address,json=containerdTtrpcAddress,proto3" json:"containerd_ttrpc_address,omitempty"`
|
||||
// Containerd's gRPC API address (e.g., "unix:///run/containerd/containerd.sock")
|
||||
ContainerdGrpcAddress string `protobuf:"bytes,6,opt,name=containerd_grpc_address,json=containerdGrpcAddress,proto3" json:"containerd_grpc_address,omitempty"`
|
||||
// Path to containerd binary for event publishing
|
||||
ContainerdBinary string `protobuf:"bytes,7,opt,name=containerd_binary,json=containerdBinary,proto3" json:"containerd_binary,omitempty"`
|
||||
// Extensible configuration sections for new features
|
||||
// Each section can contain arbitrary structured data identified by type URL
|
||||
// Examples: CRI config, NRI config, sandbox config, etc.
|
||||
Extensions []*Extension `protobuf:"bytes,8,rep,name=extensions,proto3" json:"extensions,omitempty"`
|
||||
// Optional directory for the shim to place its unix socket.
|
||||
// If empty, the shim defaults to a short, well-known path
|
||||
// (e.g., /run/containerd/s). The path must be kept short because
|
||||
// the socket filename is a 64-character SHA256 hash and unix
|
||||
// socket paths are limited to 104-108 bytes depending on platform.
|
||||
SocketDir *string `protobuf:"bytes,9,opt,name=socket_dir,json=socketDir,proto3,oneof" json:"socket_dir,omitempty"`
|
||||
}
|
||||
|
||||
func (x *BootstrapParams) Reset() {
|
||||
*x = BootstrapParams{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_runtime_bootstrap_v1_bootstrap_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *BootstrapParams) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*BootstrapParams) ProtoMessage() {}
|
||||
|
||||
func (x *BootstrapParams) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_runtime_bootstrap_v1_bootstrap_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use BootstrapParams.ProtoReflect.Descriptor instead.
|
||||
func (*BootstrapParams) Descriptor() ([]byte, []int) {
|
||||
return file_runtime_bootstrap_v1_bootstrap_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *BootstrapParams) GetInstanceID() string {
|
||||
if x != nil {
|
||||
return x.InstanceID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *BootstrapParams) GetNamespace() string {
|
||||
if x != nil {
|
||||
return x.Namespace
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *BootstrapParams) GetLogLevel() LogLevel {
|
||||
if x != nil {
|
||||
return x.LogLevel
|
||||
}
|
||||
return LogLevel_LOG_LEVEL_INFO
|
||||
}
|
||||
|
||||
func (x *BootstrapParams) GetContainerdVersion() string {
|
||||
if x != nil {
|
||||
return x.ContainerdVersion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *BootstrapParams) GetContainerdTtrpcAddress() string {
|
||||
if x != nil {
|
||||
return x.ContainerdTtrpcAddress
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *BootstrapParams) GetContainerdGrpcAddress() string {
|
||||
if x != nil {
|
||||
return x.ContainerdGrpcAddress
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *BootstrapParams) GetContainerdBinary() string {
|
||||
if x != nil {
|
||||
return x.ContainerdBinary
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *BootstrapParams) GetExtensions() []*Extension {
|
||||
if x != nil {
|
||||
return x.Extensions
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *BootstrapParams) GetSocketDir() string {
|
||||
if x != nil && x.SocketDir != nil {
|
||||
return *x.SocketDir
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Extension provides extensibility for new configuration types
|
||||
// without changing the core BootstrapParams protocol
|
||||
type Extension struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Configuration data with embedded type URL
|
||||
// Examples of type URLs:
|
||||
// - "containerd.io/cri.v1.PodSandboxConfig"
|
||||
// - "containerd.io/nri.v1.PluginConfig"
|
||||
// - "containerd.io/sandbox.v1.SandboxConfig"
|
||||
Value *anypb.Any `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Extension) Reset() {
|
||||
*x = Extension{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_runtime_bootstrap_v1_bootstrap_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Extension) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Extension) ProtoMessage() {}
|
||||
|
||||
func (x *Extension) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_runtime_bootstrap_v1_bootstrap_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Extension.ProtoReflect.Descriptor instead.
|
||||
func (*Extension) Descriptor() ([]byte, []int) {
|
||||
return file_runtime_bootstrap_v1_bootstrap_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Extension) GetValue() *anypb.Any {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BootstrapResult is returned by shim via stdout after successful startup
|
||||
type BootstrapResult struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Version of shim parameters (expected 2 for shim v2)
|
||||
Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
|
||||
// Address where shim is listening (e.g., "unix:///run/containerd/shim.sock")
|
||||
// Containerd will connect to this address for task operations
|
||||
Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
|
||||
// Protocol used by shim: "ttrpc" or "grpc"
|
||||
Protocol string `protobuf:"bytes,3,opt,name=protocol,proto3" json:"protocol,omitempty"`
|
||||
// Optional: Capabilities supported by this shim instance.
|
||||
// Reserved for future use to allow optional capability negotiation
|
||||
// between the daemon and shim.
|
||||
Capabilities []Capability `protobuf:"varint,4,rep,packed,name=capabilities,proto3,enum=containerd.runtime.bootstrap.v1.Capability" json:"capabilities,omitempty"`
|
||||
// Optional: Additional metadata from shim
|
||||
Metadata map[string]string `protobuf:"bytes,5,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
}
|
||||
|
||||
func (x *BootstrapResult) Reset() {
|
||||
*x = BootstrapResult{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_runtime_bootstrap_v1_bootstrap_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *BootstrapResult) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*BootstrapResult) ProtoMessage() {}
|
||||
|
||||
func (x *BootstrapResult) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_runtime_bootstrap_v1_bootstrap_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use BootstrapResult.ProtoReflect.Descriptor instead.
|
||||
func (*BootstrapResult) Descriptor() ([]byte, []int) {
|
||||
return file_runtime_bootstrap_v1_bootstrap_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *BootstrapResult) GetVersion() int32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *BootstrapResult) GetAddress() string {
|
||||
if x != nil {
|
||||
return x.Address
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *BootstrapResult) GetProtocol() string {
|
||||
if x != nil {
|
||||
return x.Protocol
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *BootstrapResult) GetCapabilities() []Capability {
|
||||
if x != nil {
|
||||
return x.Capabilities
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *BootstrapResult) GetMetadata() map[string]string {
|
||||
if x != nil {
|
||||
return x.Metadata
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_runtime_bootstrap_v1_bootstrap_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_runtime_bootstrap_v1_bootstrap_proto_rawDesc = []byte{
|
||||
0x0a, 0x24, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74,
|
||||
0x72, 0x61, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
|
||||
0x72, 0x64, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x62, 0x6f, 0x6f, 0x74, 0x73,
|
||||
0x74, 0x72, 0x61, 0x70, 0x2e, 0x76, 0x31, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x22, 0xe5, 0x03, 0x0a, 0x0f, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70,
|
||||
0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e,
|
||||
0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73,
|
||||
0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73,
|
||||
0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x46, 0x0a, 0x09, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76,
|
||||
0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61,
|
||||
0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x62, 0x6f,
|
||||
0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65,
|
||||
0x76, 0x65, 0x6c, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x2d, 0x0a,
|
||||
0x12, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61,
|
||||
0x69, 0x6e, 0x65, 0x72, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x18,
|
||||
0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x5f, 0x74, 0x74, 0x72, 0x70, 0x63,
|
||||
0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16,
|
||||
0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x54, 0x74, 0x72, 0x70, 0x63, 0x41,
|
||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69,
|
||||
0x6e, 0x65, 0x72, 0x64, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
|
||||
0x65, 0x72, 0x64, 0x47, 0x72, 0x70, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2b,
|
||||
0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x5f, 0x62, 0x69, 0x6e,
|
||||
0x61, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x61,
|
||||
0x69, 0x6e, 0x65, 0x72, 0x64, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x12, 0x4a, 0x0a, 0x0a, 0x65,
|
||||
0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x72, 0x75, 0x6e,
|
||||
0x74, 0x69, 0x6d, 0x65, 0x2e, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x65, 0x78, 0x74,
|
||||
0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x0a, 0x0a, 0x73, 0x6f, 0x63, 0x6b, 0x65,
|
||||
0x74, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x73,
|
||||
0x6f, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x69, 0x72, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f,
|
||||
0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x22, 0x37, 0x0a, 0x09, 0x45, 0x78,
|
||||
0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x22, 0xcb, 0x02, 0x0a, 0x0f, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61,
|
||||
0x70, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x4f, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62,
|
||||
0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x2b, 0x2e,
|
||||
0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x2e, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x2e, 0x76, 0x31, 0x2e,
|
||||
0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61,
|
||||
0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61,
|
||||
0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x63, 0x6f, 0x6e,
|
||||
0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e,
|
||||
0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x6f,
|
||||
0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x4d, 0x65, 0x74,
|
||||
0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61,
|
||||
0x64, 0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
|
||||
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
|
||||
0x01, 0x2a, 0xad, 0x01, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x12,
|
||||
0x0a, 0x0e, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x49, 0x4e, 0x46, 0x4f,
|
||||
0x10, 0x00, 0x12, 0x1c, 0x0a, 0x0f, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f,
|
||||
0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01,
|
||||
0x12, 0x1c, 0x0a, 0x0f, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x44, 0x45,
|
||||
0x42, 0x55, 0x47, 0x10, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x12, 0x12,
|
||||
0x0a, 0x0e, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x57, 0x41, 0x52, 0x4e,
|
||||
0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f,
|
||||
0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x08, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x47, 0x5f, 0x4c,
|
||||
0x45, 0x56, 0x45, 0x4c, 0x5f, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x0a, 0x12, 0x13, 0x0a, 0x0f,
|
||||
0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x50, 0x41, 0x4e, 0x49, 0x43, 0x10,
|
||||
0x0c, 0x2a, 0x28, 0x0a, 0x0a, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12,
|
||||
0x1a, 0x0a, 0x16, 0x43, 0x41, 0x50, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x55, 0x4e,
|
||||
0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x42, 0x45, 0x5a, 0x43, 0x67,
|
||||
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69,
|
||||
0x6e, 0x65, 0x72, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f,
|
||||
0x61, 0x70, 0x69, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x62, 0x6f, 0x6f, 0x74,
|
||||
0x73, 0x74, 0x72, 0x61, 0x70, 0x2f, 0x76, 0x31, 0x3b, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72,
|
||||
0x61, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_runtime_bootstrap_v1_bootstrap_proto_rawDescOnce sync.Once
|
||||
file_runtime_bootstrap_v1_bootstrap_proto_rawDescData = file_runtime_bootstrap_v1_bootstrap_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_runtime_bootstrap_v1_bootstrap_proto_rawDescGZIP() []byte {
|
||||
file_runtime_bootstrap_v1_bootstrap_proto_rawDescOnce.Do(func() {
|
||||
file_runtime_bootstrap_v1_bootstrap_proto_rawDescData = protoimpl.X.CompressGZIP(file_runtime_bootstrap_v1_bootstrap_proto_rawDescData)
|
||||
})
|
||||
return file_runtime_bootstrap_v1_bootstrap_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_runtime_bootstrap_v1_bootstrap_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
||||
var file_runtime_bootstrap_v1_bootstrap_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_runtime_bootstrap_v1_bootstrap_proto_goTypes = []interface{}{
|
||||
(LogLevel)(0), // 0: containerd.runtime.bootstrap.v1.LogLevel
|
||||
(Capability)(0), // 1: containerd.runtime.bootstrap.v1.Capability
|
||||
(*BootstrapParams)(nil), // 2: containerd.runtime.bootstrap.v1.BootstrapParams
|
||||
(*Extension)(nil), // 3: containerd.runtime.bootstrap.v1.Extension
|
||||
(*BootstrapResult)(nil), // 4: containerd.runtime.bootstrap.v1.BootstrapResult
|
||||
nil, // 5: containerd.runtime.bootstrap.v1.BootstrapResult.MetadataEntry
|
||||
(*anypb.Any)(nil), // 6: google.protobuf.Any
|
||||
}
|
||||
var file_runtime_bootstrap_v1_bootstrap_proto_depIdxs = []int32{
|
||||
0, // 0: containerd.runtime.bootstrap.v1.BootstrapParams.log_level:type_name -> containerd.runtime.bootstrap.v1.LogLevel
|
||||
3, // 1: containerd.runtime.bootstrap.v1.BootstrapParams.extensions:type_name -> containerd.runtime.bootstrap.v1.Extension
|
||||
6, // 2: containerd.runtime.bootstrap.v1.Extension.value:type_name -> google.protobuf.Any
|
||||
1, // 3: containerd.runtime.bootstrap.v1.BootstrapResult.capabilities:type_name -> containerd.runtime.bootstrap.v1.Capability
|
||||
5, // 4: containerd.runtime.bootstrap.v1.BootstrapResult.metadata:type_name -> containerd.runtime.bootstrap.v1.BootstrapResult.MetadataEntry
|
||||
5, // [5:5] is the sub-list for method output_type
|
||||
5, // [5:5] is the sub-list for method input_type
|
||||
5, // [5:5] is the sub-list for extension type_name
|
||||
5, // [5:5] is the sub-list for extension extendee
|
||||
0, // [0:5] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_runtime_bootstrap_v1_bootstrap_proto_init() }
|
||||
func file_runtime_bootstrap_v1_bootstrap_proto_init() {
|
||||
if File_runtime_bootstrap_v1_bootstrap_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_runtime_bootstrap_v1_bootstrap_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*BootstrapParams); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_runtime_bootstrap_v1_bootstrap_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Extension); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_runtime_bootstrap_v1_bootstrap_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*BootstrapResult); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_runtime_bootstrap_v1_bootstrap_proto_msgTypes[0].OneofWrappers = []interface{}{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_runtime_bootstrap_v1_bootstrap_proto_rawDesc,
|
||||
NumEnums: 2,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_runtime_bootstrap_v1_bootstrap_proto_goTypes,
|
||||
DependencyIndexes: file_runtime_bootstrap_v1_bootstrap_proto_depIdxs,
|
||||
EnumInfos: file_runtime_bootstrap_v1_bootstrap_proto_enumTypes,
|
||||
MessageInfos: file_runtime_bootstrap_v1_bootstrap_proto_msgTypes,
|
||||
}.Build()
|
||||
File_runtime_bootstrap_v1_bootstrap_proto = out.File
|
||||
file_runtime_bootstrap_v1_bootstrap_proto_rawDesc = nil
|
||||
file_runtime_bootstrap_v1_bootstrap_proto_goTypes = nil
|
||||
file_runtime_bootstrap_v1_bootstrap_proto_depIdxs = nil
|
||||
}
|
||||
130
vendor/github.com/containerd/containerd/api/runtime/bootstrap/v1/bootstrap.proto
generated
vendored
Normal file
130
vendor/github.com/containerd/containerd/api/runtime/bootstrap/v1/bootstrap.proto
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Bootstrap Protocol
|
||||
//
|
||||
// This protocol defines the interface between containerd and shims at startup.
|
||||
// It replaces the previous scattered configuration mechanisms (CLI args, env vars,
|
||||
// stdin JSON, spec.json annotations) with a single, versioned, extensible protocol.
|
||||
//
|
||||
// Flow:
|
||||
// 1. containerd spawns the shim process
|
||||
// 2. containerd writes BootstrapParams as JSON to shim's stdin
|
||||
// 3. shim initializes and writes BootstrapResult as JSON to stdout
|
||||
// 4. containerd connects to the address provided in BootstrapResult
|
||||
//
|
||||
// This design enables:
|
||||
// - Forward/backward compatibility via version field
|
||||
// - Typed extensibility via google.protobuf.Any and Extension
|
||||
// - Clear capability negotiation between containerd and shims
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.runtime.bootstrap.v1;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/runtime/bootstrap/v1;bootstrap";
|
||||
|
||||
// BootstrapParams contains all configuration passed from containerd to shim at startup.
|
||||
message BootstrapParams {
|
||||
// Container/sandbox ID
|
||||
string instance_id = 1;
|
||||
|
||||
// Namespace for the container
|
||||
string namespace = 2;
|
||||
|
||||
// Requested shim log level.
|
||||
// INFO = 0, more critical levels are positive, and more verbose
|
||||
// levels are negative. This is an open enum; shims should treat
|
||||
// unrecognized values by mapping to the nearest known level.
|
||||
LogLevel log_level = 3;
|
||||
|
||||
// containerd daemon version that is launching this shim.
|
||||
string containerd_version = 4;
|
||||
|
||||
// Containerd's TTRPC API address (e.g., "unix:///run/containerd/containerd.sock.ttrpc")
|
||||
string containerd_ttrpc_address = 5;
|
||||
|
||||
// Containerd's gRPC API address (e.g., "unix:///run/containerd/containerd.sock")
|
||||
string containerd_grpc_address = 6;
|
||||
|
||||
// Path to containerd binary for event publishing
|
||||
string containerd_binary = 7;
|
||||
|
||||
// Extensible configuration sections for new features
|
||||
// Each section can contain arbitrary structured data identified by type URL
|
||||
// Examples: CRI config, NRI config, sandbox config, etc.
|
||||
repeated Extension extensions = 8;
|
||||
|
||||
// Optional directory for the shim to place its unix socket.
|
||||
// If empty, the shim defaults to a short, well-known path
|
||||
// (e.g., /run/containerd/s). The path must be kept short because
|
||||
// the socket filename is a 64-character SHA256 hash and unix
|
||||
// socket paths are limited to 104-108 bytes depending on platform.
|
||||
optional string socket_dir = 9;
|
||||
}
|
||||
|
||||
// Extension provides extensibility for new configuration types
|
||||
// without changing the core BootstrapParams protocol
|
||||
message Extension {
|
||||
// Configuration data with embedded type URL
|
||||
// Examples of type URLs:
|
||||
// - "containerd.io/cri.v1.PodSandboxConfig"
|
||||
// - "containerd.io/nri.v1.PluginConfig"
|
||||
// - "containerd.io/sandbox.v1.SandboxConfig"
|
||||
google.protobuf.Any value = 1;
|
||||
}
|
||||
|
||||
// BootstrapResult is returned by shim via stdout after successful startup
|
||||
message BootstrapResult {
|
||||
// Version of shim parameters (expected 2 for shim v2)
|
||||
int32 version = 1;
|
||||
|
||||
// Address where shim is listening (e.g., "unix:///run/containerd/shim.sock")
|
||||
// Containerd will connect to this address for task operations
|
||||
string address = 2;
|
||||
|
||||
// Protocol used by shim: "ttrpc" or "grpc"
|
||||
string protocol = 3;
|
||||
|
||||
// Optional: Capabilities supported by this shim instance.
|
||||
// Reserved for future use to allow optional capability negotiation
|
||||
// between the daemon and shim.
|
||||
repeated Capability capabilities = 4;
|
||||
|
||||
// Optional: Additional metadata from shim
|
||||
map<string, string> metadata = 5;
|
||||
}
|
||||
|
||||
// LogLevel defines log verbosity. INFO = 0, more critical levels are
|
||||
// positive, and more verbose levels are negative. This is an open enum;
|
||||
// unknown numeric values are preserved on the wire.
|
||||
enum LogLevel {
|
||||
LOG_LEVEL_INFO = 0;
|
||||
LOG_LEVEL_TRACE = -8;
|
||||
LOG_LEVEL_DEBUG = -4;
|
||||
LOG_LEVEL_WARN = 4;
|
||||
LOG_LEVEL_ERROR = 8;
|
||||
LOG_LEVEL_FATAL = 10;
|
||||
LOG_LEVEL_PANIC = 12;
|
||||
}
|
||||
|
||||
// Capability defines optional features that can be negotiated between
|
||||
// containerd and shims.
|
||||
enum Capability {
|
||||
CAPABILITY_UNSPECIFIED = 0;
|
||||
}
|
||||
17
vendor/github.com/containerd/containerd/api/runtime/bootstrap/v1/doc.go
generated
vendored
Normal file
17
vendor/github.com/containerd/containerd/api/runtime/bootstrap/v1/doc.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package bootstrap
|
||||
91
vendor/github.com/containerd/containerd/api/runtime/bootstrap/v1/helpers.go
generated
vendored
Normal file
91
vendor/github.com/containerd/containerd/api/runtime/bootstrap/v1/helpers.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
// LogLevelFromString converts a log level string (e.g. "debug", "info") to a
|
||||
// LogLevel enum value. The accepted strings are compatible with logrus level names.
|
||||
func LogLevelFromString(s string) LogLevel {
|
||||
switch s {
|
||||
case "trace":
|
||||
return LogLevel_LOG_LEVEL_TRACE
|
||||
case "debug":
|
||||
return LogLevel_LOG_LEVEL_DEBUG
|
||||
case "info":
|
||||
return LogLevel_LOG_LEVEL_INFO
|
||||
case "warn", "warning":
|
||||
return LogLevel_LOG_LEVEL_WARN
|
||||
case "error":
|
||||
return LogLevel_LOG_LEVEL_ERROR
|
||||
case "fatal":
|
||||
return LogLevel_LOG_LEVEL_FATAL
|
||||
case "panic":
|
||||
return LogLevel_LOG_LEVEL_PANIC
|
||||
default:
|
||||
if v, err := strconv.ParseInt(s, 10, 32); err == nil {
|
||||
return LogLevel(v)
|
||||
}
|
||||
return LogLevel_LOG_LEVEL_INFO
|
||||
}
|
||||
}
|
||||
|
||||
// AddExtension adds a new extension to the BootstrapParams.
|
||||
// The message is wrapped in a google.protobuf.Any with its type URL automatically set.
|
||||
// If the message is already an *anypb.Any, it is used directly without double-wrapping.
|
||||
func (p *BootstrapParams) AddExtension(msg proto.Message) error {
|
||||
var anyVal *anypb.Any
|
||||
if a, ok := msg.(*anypb.Any); ok {
|
||||
// Already an Any, use it directly
|
||||
anyVal = a
|
||||
} else {
|
||||
var err error
|
||||
anyVal, err = anypb.New(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
p.Extensions = append(p.Extensions, &Extension{Value: anyVal})
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindExtension finds an extension matching the type of dst and unmarshals it.
|
||||
func (p *BootstrapParams) FindExtension(dst proto.Message) (bool, error) {
|
||||
if p == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
name := dst.ProtoReflect().Descriptor().FullName()
|
||||
|
||||
for _, ext := range p.Extensions {
|
||||
if ext.GetValue().MessageIs(dst) {
|
||||
if err := ext.GetValue().UnmarshalTo(dst); err != nil {
|
||||
return false, fmt.Errorf("failed to unmarshal extension %q: %w", name, err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
17
vendor/github.com/containerd/containerd/api/runtime/task/v2/doc.go
generated
vendored
Normal file
17
vendor/github.com/containerd/containerd/api/runtime/task/v2/doc.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package task
|
||||
2331
vendor/github.com/containerd/containerd/api/runtime/task/v2/shim.pb.go
generated
vendored
Normal file
2331
vendor/github.com/containerd/containerd/api/runtime/task/v2/shim.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
200
vendor/github.com/containerd/containerd/api/runtime/task/v2/shim.proto
generated
vendored
Normal file
200
vendor/github.com/containerd/containerd/api/runtime/task/v2/shim.proto
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.task.v2;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "types/mount.proto";
|
||||
import "types/task/task.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/runtime/task/v2;task";
|
||||
|
||||
// Shim service is launched for each container and is responsible for owning the IO
|
||||
// for the container and its additional processes. The shim is also the parent of
|
||||
// each container and allows reattaching to the IO and receiving the exit status
|
||||
// for the container processes.
|
||||
service Task {
|
||||
rpc State(StateRequest) returns (StateResponse);
|
||||
rpc Create(CreateTaskRequest) returns (CreateTaskResponse);
|
||||
rpc Start(StartRequest) returns (StartResponse);
|
||||
rpc Delete(DeleteRequest) returns (DeleteResponse);
|
||||
rpc Pids(PidsRequest) returns (PidsResponse);
|
||||
rpc Pause(PauseRequest) returns (google.protobuf.Empty);
|
||||
rpc Resume(ResumeRequest) returns (google.protobuf.Empty);
|
||||
rpc Checkpoint(CheckpointTaskRequest) returns (google.protobuf.Empty);
|
||||
rpc Kill(KillRequest) returns (google.protobuf.Empty);
|
||||
rpc Exec(ExecProcessRequest) returns (google.protobuf.Empty);
|
||||
rpc ResizePty(ResizePtyRequest) returns (google.protobuf.Empty);
|
||||
rpc CloseIO(CloseIORequest) returns (google.protobuf.Empty);
|
||||
rpc Update(UpdateTaskRequest) returns (google.protobuf.Empty);
|
||||
rpc Wait(WaitRequest) returns (WaitResponse);
|
||||
rpc Stats(StatsRequest) returns (StatsResponse);
|
||||
rpc Connect(ConnectRequest) returns (ConnectResponse);
|
||||
rpc Shutdown(ShutdownRequest) returns (google.protobuf.Empty);
|
||||
}
|
||||
|
||||
message CreateTaskRequest {
|
||||
string id = 1;
|
||||
string bundle = 2;
|
||||
repeated containerd.types.Mount rootfs = 3;
|
||||
bool terminal = 4;
|
||||
string stdin = 5;
|
||||
string stdout = 6;
|
||||
string stderr = 7;
|
||||
string checkpoint = 8;
|
||||
string parent_checkpoint = 9;
|
||||
google.protobuf.Any options = 10;
|
||||
}
|
||||
|
||||
message CreateTaskResponse {
|
||||
uint32 pid = 1;
|
||||
}
|
||||
|
||||
message DeleteRequest {
|
||||
string id = 1;
|
||||
string exec_id = 2;
|
||||
}
|
||||
|
||||
message DeleteResponse {
|
||||
uint32 pid = 1;
|
||||
uint32 exit_status = 2;
|
||||
google.protobuf.Timestamp exited_at = 3;
|
||||
}
|
||||
|
||||
message ExecProcessRequest {
|
||||
string id = 1;
|
||||
string exec_id = 2;
|
||||
bool terminal = 3;
|
||||
string stdin = 4;
|
||||
string stdout = 5;
|
||||
string stderr = 6;
|
||||
google.protobuf.Any spec = 7;
|
||||
}
|
||||
|
||||
message ExecProcessResponse {}
|
||||
|
||||
message ResizePtyRequest {
|
||||
string id = 1;
|
||||
string exec_id = 2;
|
||||
uint32 width = 3;
|
||||
uint32 height = 4;
|
||||
}
|
||||
|
||||
message StateRequest {
|
||||
string id = 1;
|
||||
string exec_id = 2;
|
||||
}
|
||||
|
||||
message StateResponse {
|
||||
string id = 1;
|
||||
string bundle = 2;
|
||||
uint32 pid = 3;
|
||||
containerd.v1.types.Status status = 4;
|
||||
string stdin = 5;
|
||||
string stdout = 6;
|
||||
string stderr = 7;
|
||||
bool terminal = 8;
|
||||
uint32 exit_status = 9;
|
||||
google.protobuf.Timestamp exited_at = 10;
|
||||
string exec_id = 11;
|
||||
}
|
||||
|
||||
message KillRequest {
|
||||
string id = 1;
|
||||
string exec_id = 2;
|
||||
uint32 signal = 3;
|
||||
bool all = 4;
|
||||
}
|
||||
|
||||
message CloseIORequest {
|
||||
string id = 1;
|
||||
string exec_id = 2;
|
||||
bool stdin = 3;
|
||||
}
|
||||
|
||||
message PidsRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message PidsResponse {
|
||||
repeated containerd.v1.types.ProcessInfo processes = 1;
|
||||
}
|
||||
|
||||
message CheckpointTaskRequest {
|
||||
string id = 1;
|
||||
string path = 2;
|
||||
google.protobuf.Any options = 3;
|
||||
}
|
||||
|
||||
message UpdateTaskRequest {
|
||||
string id = 1;
|
||||
google.protobuf.Any resources = 2;
|
||||
map<string, string> annotations = 3;
|
||||
}
|
||||
|
||||
message StartRequest {
|
||||
string id = 1;
|
||||
string exec_id = 2;
|
||||
}
|
||||
|
||||
message StartResponse {
|
||||
uint32 pid = 1;
|
||||
}
|
||||
|
||||
message WaitRequest {
|
||||
string id = 1;
|
||||
string exec_id = 2;
|
||||
}
|
||||
|
||||
message WaitResponse {
|
||||
uint32 exit_status = 1;
|
||||
google.protobuf.Timestamp exited_at = 2;
|
||||
}
|
||||
|
||||
message StatsRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message StatsResponse {
|
||||
google.protobuf.Any stats = 1;
|
||||
}
|
||||
|
||||
message ConnectRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message ConnectResponse {
|
||||
uint32 shim_pid = 1;
|
||||
uint32 task_pid = 2;
|
||||
string version = 3;
|
||||
}
|
||||
|
||||
message ShutdownRequest {
|
||||
string id = 1;
|
||||
bool now = 2;
|
||||
}
|
||||
|
||||
message PauseRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message ResumeRequest {
|
||||
string id = 1;
|
||||
}
|
||||
301
vendor/github.com/containerd/containerd/api/runtime/task/v2/shim_ttrpc.pb.go
generated
vendored
Normal file
301
vendor/github.com/containerd/containerd/api/runtime/task/v2/shim_ttrpc.pb.go
generated
vendored
Normal file
@@ -0,0 +1,301 @@
|
||||
// Code generated by protoc-gen-go-ttrpc. DO NOT EDIT.
|
||||
// source: runtime/task/v2/shim.proto
|
||||
package task
|
||||
|
||||
import (
|
||||
context "context"
|
||||
ttrpc "github.com/containerd/ttrpc"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
type TTRPCTaskService interface {
|
||||
State(context.Context, *StateRequest) (*StateResponse, error)
|
||||
Create(context.Context, *CreateTaskRequest) (*CreateTaskResponse, error)
|
||||
Start(context.Context, *StartRequest) (*StartResponse, error)
|
||||
Delete(context.Context, *DeleteRequest) (*DeleteResponse, error)
|
||||
Pids(context.Context, *PidsRequest) (*PidsResponse, error)
|
||||
Pause(context.Context, *PauseRequest) (*emptypb.Empty, error)
|
||||
Resume(context.Context, *ResumeRequest) (*emptypb.Empty, error)
|
||||
Checkpoint(context.Context, *CheckpointTaskRequest) (*emptypb.Empty, error)
|
||||
Kill(context.Context, *KillRequest) (*emptypb.Empty, error)
|
||||
Exec(context.Context, *ExecProcessRequest) (*emptypb.Empty, error)
|
||||
ResizePty(context.Context, *ResizePtyRequest) (*emptypb.Empty, error)
|
||||
CloseIO(context.Context, *CloseIORequest) (*emptypb.Empty, error)
|
||||
Update(context.Context, *UpdateTaskRequest) (*emptypb.Empty, error)
|
||||
Wait(context.Context, *WaitRequest) (*WaitResponse, error)
|
||||
Stats(context.Context, *StatsRequest) (*StatsResponse, error)
|
||||
Connect(context.Context, *ConnectRequest) (*ConnectResponse, error)
|
||||
Shutdown(context.Context, *ShutdownRequest) (*emptypb.Empty, error)
|
||||
}
|
||||
|
||||
func RegisterTTRPCTaskService(srv *ttrpc.Server, svc TTRPCTaskService) {
|
||||
srv.RegisterService("containerd.task.v2.Task", &ttrpc.ServiceDesc{
|
||||
Methods: map[string]ttrpc.Method{
|
||||
"State": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req StateRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.State(ctx, &req)
|
||||
},
|
||||
"Create": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req CreateTaskRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Create(ctx, &req)
|
||||
},
|
||||
"Start": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req StartRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Start(ctx, &req)
|
||||
},
|
||||
"Delete": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req DeleteRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Delete(ctx, &req)
|
||||
},
|
||||
"Pids": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req PidsRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Pids(ctx, &req)
|
||||
},
|
||||
"Pause": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req PauseRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Pause(ctx, &req)
|
||||
},
|
||||
"Resume": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req ResumeRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Resume(ctx, &req)
|
||||
},
|
||||
"Checkpoint": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req CheckpointTaskRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Checkpoint(ctx, &req)
|
||||
},
|
||||
"Kill": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req KillRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Kill(ctx, &req)
|
||||
},
|
||||
"Exec": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req ExecProcessRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Exec(ctx, &req)
|
||||
},
|
||||
"ResizePty": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req ResizePtyRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.ResizePty(ctx, &req)
|
||||
},
|
||||
"CloseIO": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req CloseIORequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.CloseIO(ctx, &req)
|
||||
},
|
||||
"Update": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req UpdateTaskRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Update(ctx, &req)
|
||||
},
|
||||
"Wait": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req WaitRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Wait(ctx, &req)
|
||||
},
|
||||
"Stats": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req StatsRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Stats(ctx, &req)
|
||||
},
|
||||
"Connect": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req ConnectRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Connect(ctx, &req)
|
||||
},
|
||||
"Shutdown": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req ShutdownRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Shutdown(ctx, &req)
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
type ttrpctaskClient struct {
|
||||
client *ttrpc.Client
|
||||
}
|
||||
|
||||
func NewTTRPCTaskClient(client *ttrpc.Client) TTRPCTaskService {
|
||||
return &ttrpctaskClient{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) State(ctx context.Context, req *StateRequest) (*StateResponse, error) {
|
||||
var resp StateResponse
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "State", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Create(ctx context.Context, req *CreateTaskRequest) (*CreateTaskResponse, error) {
|
||||
var resp CreateTaskResponse
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "Create", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Start(ctx context.Context, req *StartRequest) (*StartResponse, error) {
|
||||
var resp StartResponse
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "Start", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Delete(ctx context.Context, req *DeleteRequest) (*DeleteResponse, error) {
|
||||
var resp DeleteResponse
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "Delete", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Pids(ctx context.Context, req *PidsRequest) (*PidsResponse, error) {
|
||||
var resp PidsResponse
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "Pids", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Pause(ctx context.Context, req *PauseRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "Pause", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Resume(ctx context.Context, req *ResumeRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "Resume", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Checkpoint(ctx context.Context, req *CheckpointTaskRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "Checkpoint", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Kill(ctx context.Context, req *KillRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "Kill", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Exec(ctx context.Context, req *ExecProcessRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "Exec", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) ResizePty(ctx context.Context, req *ResizePtyRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "ResizePty", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) CloseIO(ctx context.Context, req *CloseIORequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "CloseIO", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Update(ctx context.Context, req *UpdateTaskRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "Update", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Wait(ctx context.Context, req *WaitRequest) (*WaitResponse, error) {
|
||||
var resp WaitResponse
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "Wait", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Stats(ctx context.Context, req *StatsRequest) (*StatsResponse, error) {
|
||||
var resp StatsResponse
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "Stats", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Connect(ctx context.Context, req *ConnectRequest) (*ConnectResponse, error) {
|
||||
var resp ConnectResponse
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "Connect", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Shutdown(ctx context.Context, req *ShutdownRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v2.Task", "Shutdown", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
17
vendor/github.com/containerd/containerd/api/runtime/task/v3/doc.go
generated
vendored
Normal file
17
vendor/github.com/containerd/containerd/api/runtime/task/v3/doc.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package task
|
||||
2331
vendor/github.com/containerd/containerd/api/runtime/task/v3/shim.pb.go
generated
vendored
Normal file
2331
vendor/github.com/containerd/containerd/api/runtime/task/v3/shim.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
200
vendor/github.com/containerd/containerd/api/runtime/task/v3/shim.proto
generated
vendored
Normal file
200
vendor/github.com/containerd/containerd/api/runtime/task/v3/shim.proto
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.task.v3;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "types/mount.proto";
|
||||
import "types/task/task.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/runtime/task/v3;task";
|
||||
|
||||
// Shim service is launched for each container and is responsible for owning the IO
|
||||
// for the container and its additional processes. The shim is also the parent of
|
||||
// each container and allows reattaching to the IO and receiving the exit status
|
||||
// for the container processes.
|
||||
service Task {
|
||||
rpc State(StateRequest) returns (StateResponse);
|
||||
rpc Create(CreateTaskRequest) returns (CreateTaskResponse);
|
||||
rpc Start(StartRequest) returns (StartResponse);
|
||||
rpc Delete(DeleteRequest) returns (DeleteResponse);
|
||||
rpc Pids(PidsRequest) returns (PidsResponse);
|
||||
rpc Pause(PauseRequest) returns (google.protobuf.Empty);
|
||||
rpc Resume(ResumeRequest) returns (google.protobuf.Empty);
|
||||
rpc Checkpoint(CheckpointTaskRequest) returns (google.protobuf.Empty);
|
||||
rpc Kill(KillRequest) returns (google.protobuf.Empty);
|
||||
rpc Exec(ExecProcessRequest) returns (google.protobuf.Empty);
|
||||
rpc ResizePty(ResizePtyRequest) returns (google.protobuf.Empty);
|
||||
rpc CloseIO(CloseIORequest) returns (google.protobuf.Empty);
|
||||
rpc Update(UpdateTaskRequest) returns (google.protobuf.Empty);
|
||||
rpc Wait(WaitRequest) returns (WaitResponse);
|
||||
rpc Stats(StatsRequest) returns (StatsResponse);
|
||||
rpc Connect(ConnectRequest) returns (ConnectResponse);
|
||||
rpc Shutdown(ShutdownRequest) returns (google.protobuf.Empty);
|
||||
}
|
||||
|
||||
message CreateTaskRequest {
|
||||
string id = 1;
|
||||
string bundle = 2;
|
||||
repeated containerd.types.Mount rootfs = 3;
|
||||
bool terminal = 4;
|
||||
string stdin = 5;
|
||||
string stdout = 6;
|
||||
string stderr = 7;
|
||||
string checkpoint = 8;
|
||||
string parent_checkpoint = 9;
|
||||
google.protobuf.Any options = 10;
|
||||
}
|
||||
|
||||
message CreateTaskResponse {
|
||||
uint32 pid = 1;
|
||||
}
|
||||
|
||||
message DeleteRequest {
|
||||
string id = 1;
|
||||
string exec_id = 2;
|
||||
}
|
||||
|
||||
message DeleteResponse {
|
||||
uint32 pid = 1;
|
||||
uint32 exit_status = 2;
|
||||
google.protobuf.Timestamp exited_at = 3;
|
||||
}
|
||||
|
||||
message ExecProcessRequest {
|
||||
string id = 1;
|
||||
string exec_id = 2;
|
||||
bool terminal = 3;
|
||||
string stdin = 4;
|
||||
string stdout = 5;
|
||||
string stderr = 6;
|
||||
google.protobuf.Any spec = 7;
|
||||
}
|
||||
|
||||
message ExecProcessResponse {}
|
||||
|
||||
message ResizePtyRequest {
|
||||
string id = 1;
|
||||
string exec_id = 2;
|
||||
uint32 width = 3;
|
||||
uint32 height = 4;
|
||||
}
|
||||
|
||||
message StateRequest {
|
||||
string id = 1;
|
||||
string exec_id = 2;
|
||||
}
|
||||
|
||||
message StateResponse {
|
||||
string id = 1;
|
||||
string bundle = 2;
|
||||
uint32 pid = 3;
|
||||
containerd.v1.types.Status status = 4;
|
||||
string stdin = 5;
|
||||
string stdout = 6;
|
||||
string stderr = 7;
|
||||
bool terminal = 8;
|
||||
uint32 exit_status = 9;
|
||||
google.protobuf.Timestamp exited_at = 10;
|
||||
string exec_id = 11;
|
||||
}
|
||||
|
||||
message KillRequest {
|
||||
string id = 1;
|
||||
string exec_id = 2;
|
||||
uint32 signal = 3;
|
||||
bool all = 4;
|
||||
}
|
||||
|
||||
message CloseIORequest {
|
||||
string id = 1;
|
||||
string exec_id = 2;
|
||||
bool stdin = 3;
|
||||
}
|
||||
|
||||
message PidsRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message PidsResponse {
|
||||
repeated containerd.v1.types.ProcessInfo processes = 1;
|
||||
}
|
||||
|
||||
message CheckpointTaskRequest {
|
||||
string id = 1;
|
||||
string path = 2;
|
||||
google.protobuf.Any options = 3;
|
||||
}
|
||||
|
||||
message UpdateTaskRequest {
|
||||
string id = 1;
|
||||
google.protobuf.Any resources = 2;
|
||||
map<string, string> annotations = 3;
|
||||
}
|
||||
|
||||
message StartRequest {
|
||||
string id = 1;
|
||||
string exec_id = 2;
|
||||
}
|
||||
|
||||
message StartResponse {
|
||||
uint32 pid = 1;
|
||||
}
|
||||
|
||||
message WaitRequest {
|
||||
string id = 1;
|
||||
string exec_id = 2;
|
||||
}
|
||||
|
||||
message WaitResponse {
|
||||
uint32 exit_status = 1;
|
||||
google.protobuf.Timestamp exited_at = 2;
|
||||
}
|
||||
|
||||
message StatsRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message StatsResponse {
|
||||
google.protobuf.Any stats = 1;
|
||||
}
|
||||
|
||||
message ConnectRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message ConnectResponse {
|
||||
uint32 shim_pid = 1;
|
||||
uint32 task_pid = 2;
|
||||
string version = 3;
|
||||
}
|
||||
|
||||
message ShutdownRequest {
|
||||
string id = 1;
|
||||
bool now = 2;
|
||||
}
|
||||
|
||||
message PauseRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message ResumeRequest {
|
||||
string id = 1;
|
||||
}
|
||||
684
vendor/github.com/containerd/containerd/api/runtime/task/v3/shim_grpc.pb.go
generated
vendored
Normal file
684
vendor/github.com/containerd/containerd/api/runtime/task/v3/shim_grpc.pb.go
generated
vendored
Normal file
@@ -0,0 +1,684 @@
|
||||
//go:build !no_grpc
|
||||
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc (unknown)
|
||||
// source: runtime/task/v3/shim.proto
|
||||
|
||||
package task
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// TaskClient is the client API for Task service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type TaskClient interface {
|
||||
State(ctx context.Context, in *StateRequest, opts ...grpc.CallOption) (*StateResponse, error)
|
||||
Create(ctx context.Context, in *CreateTaskRequest, opts ...grpc.CallOption) (*CreateTaskResponse, error)
|
||||
Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error)
|
||||
Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*DeleteResponse, error)
|
||||
Pids(ctx context.Context, in *PidsRequest, opts ...grpc.CallOption) (*PidsResponse, error)
|
||||
Pause(ctx context.Context, in *PauseRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
Resume(ctx context.Context, in *ResumeRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
Checkpoint(ctx context.Context, in *CheckpointTaskRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
Kill(ctx context.Context, in *KillRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
Exec(ctx context.Context, in *ExecProcessRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
ResizePty(ctx context.Context, in *ResizePtyRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
CloseIO(ctx context.Context, in *CloseIORequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
Update(ctx context.Context, in *UpdateTaskRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
Wait(ctx context.Context, in *WaitRequest, opts ...grpc.CallOption) (*WaitResponse, error)
|
||||
Stats(ctx context.Context, in *StatsRequest, opts ...grpc.CallOption) (*StatsResponse, error)
|
||||
Connect(ctx context.Context, in *ConnectRequest, opts ...grpc.CallOption) (*ConnectResponse, error)
|
||||
Shutdown(ctx context.Context, in *ShutdownRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
}
|
||||
|
||||
type taskClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewTaskClient(cc grpc.ClientConnInterface) TaskClient {
|
||||
return &taskClient{cc}
|
||||
}
|
||||
|
||||
func (c *taskClient) State(ctx context.Context, in *StateRequest, opts ...grpc.CallOption) (*StateResponse, error) {
|
||||
out := new(StateResponse)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/State", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *taskClient) Create(ctx context.Context, in *CreateTaskRequest, opts ...grpc.CallOption) (*CreateTaskResponse, error) {
|
||||
out := new(CreateTaskResponse)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/Create", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *taskClient) Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error) {
|
||||
out := new(StartResponse)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/Start", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *taskClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*DeleteResponse, error) {
|
||||
out := new(DeleteResponse)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/Delete", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *taskClient) Pids(ctx context.Context, in *PidsRequest, opts ...grpc.CallOption) (*PidsResponse, error) {
|
||||
out := new(PidsResponse)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/Pids", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *taskClient) Pause(ctx context.Context, in *PauseRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/Pause", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *taskClient) Resume(ctx context.Context, in *ResumeRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/Resume", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *taskClient) Checkpoint(ctx context.Context, in *CheckpointTaskRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/Checkpoint", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *taskClient) Kill(ctx context.Context, in *KillRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/Kill", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *taskClient) Exec(ctx context.Context, in *ExecProcessRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/Exec", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *taskClient) ResizePty(ctx context.Context, in *ResizePtyRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/ResizePty", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *taskClient) CloseIO(ctx context.Context, in *CloseIORequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/CloseIO", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *taskClient) Update(ctx context.Context, in *UpdateTaskRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/Update", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *taskClient) Wait(ctx context.Context, in *WaitRequest, opts ...grpc.CallOption) (*WaitResponse, error) {
|
||||
out := new(WaitResponse)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/Wait", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *taskClient) Stats(ctx context.Context, in *StatsRequest, opts ...grpc.CallOption) (*StatsResponse, error) {
|
||||
out := new(StatsResponse)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/Stats", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *taskClient) Connect(ctx context.Context, in *ConnectRequest, opts ...grpc.CallOption) (*ConnectResponse, error) {
|
||||
out := new(ConnectResponse)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/Connect", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *taskClient) Shutdown(ctx context.Context, in *ShutdownRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, "/containerd.task.v3.Task/Shutdown", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// TaskServer is the server API for Task service.
|
||||
// All implementations must embed UnimplementedTaskServer
|
||||
// for forward compatibility
|
||||
type TaskServer interface {
|
||||
State(context.Context, *StateRequest) (*StateResponse, error)
|
||||
Create(context.Context, *CreateTaskRequest) (*CreateTaskResponse, error)
|
||||
Start(context.Context, *StartRequest) (*StartResponse, error)
|
||||
Delete(context.Context, *DeleteRequest) (*DeleteResponse, error)
|
||||
Pids(context.Context, *PidsRequest) (*PidsResponse, error)
|
||||
Pause(context.Context, *PauseRequest) (*emptypb.Empty, error)
|
||||
Resume(context.Context, *ResumeRequest) (*emptypb.Empty, error)
|
||||
Checkpoint(context.Context, *CheckpointTaskRequest) (*emptypb.Empty, error)
|
||||
Kill(context.Context, *KillRequest) (*emptypb.Empty, error)
|
||||
Exec(context.Context, *ExecProcessRequest) (*emptypb.Empty, error)
|
||||
ResizePty(context.Context, *ResizePtyRequest) (*emptypb.Empty, error)
|
||||
CloseIO(context.Context, *CloseIORequest) (*emptypb.Empty, error)
|
||||
Update(context.Context, *UpdateTaskRequest) (*emptypb.Empty, error)
|
||||
Wait(context.Context, *WaitRequest) (*WaitResponse, error)
|
||||
Stats(context.Context, *StatsRequest) (*StatsResponse, error)
|
||||
Connect(context.Context, *ConnectRequest) (*ConnectResponse, error)
|
||||
Shutdown(context.Context, *ShutdownRequest) (*emptypb.Empty, error)
|
||||
mustEmbedUnimplementedTaskServer()
|
||||
}
|
||||
|
||||
// UnimplementedTaskServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedTaskServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedTaskServer) State(context.Context, *StateRequest) (*StateResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method State not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) Create(context.Context, *CreateTaskRequest) (*CreateTaskResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Create not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) Start(context.Context, *StartRequest) (*StartResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Start not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) Delete(context.Context, *DeleteRequest) (*DeleteResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) Pids(context.Context, *PidsRequest) (*PidsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Pids not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) Pause(context.Context, *PauseRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Pause not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) Resume(context.Context, *ResumeRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Resume not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) Checkpoint(context.Context, *CheckpointTaskRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Checkpoint not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) Kill(context.Context, *KillRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Kill not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) Exec(context.Context, *ExecProcessRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Exec not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) ResizePty(context.Context, *ResizePtyRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ResizePty not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) CloseIO(context.Context, *CloseIORequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CloseIO not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) Update(context.Context, *UpdateTaskRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Update not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) Wait(context.Context, *WaitRequest) (*WaitResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Wait not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) Stats(context.Context, *StatsRequest) (*StatsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Stats not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) Connect(context.Context, *ConnectRequest) (*ConnectResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Connect not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) Shutdown(context.Context, *ShutdownRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Shutdown not implemented")
|
||||
}
|
||||
func (UnimplementedTaskServer) mustEmbedUnimplementedTaskServer() {}
|
||||
|
||||
// UnsafeTaskServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to TaskServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeTaskServer interface {
|
||||
mustEmbedUnimplementedTaskServer()
|
||||
}
|
||||
|
||||
func RegisterTaskServer(s grpc.ServiceRegistrar, srv TaskServer) {
|
||||
s.RegisterService(&Task_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _Task_State_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(StateRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).State(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/State",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).State(ctx, req.(*StateRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Task_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreateTaskRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).Create(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/Create",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).Create(ctx, req.(*CreateTaskRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Task_Start_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(StartRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).Start(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/Start",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).Start(ctx, req.(*StartRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Task_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeleteRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).Delete(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/Delete",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).Delete(ctx, req.(*DeleteRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Task_Pids_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PidsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).Pids(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/Pids",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).Pids(ctx, req.(*PidsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Task_Pause_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PauseRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).Pause(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/Pause",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).Pause(ctx, req.(*PauseRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Task_Resume_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ResumeRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).Resume(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/Resume",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).Resume(ctx, req.(*ResumeRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Task_Checkpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CheckpointTaskRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).Checkpoint(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/Checkpoint",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).Checkpoint(ctx, req.(*CheckpointTaskRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Task_Kill_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(KillRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).Kill(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/Kill",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).Kill(ctx, req.(*KillRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Task_Exec_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ExecProcessRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).Exec(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/Exec",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).Exec(ctx, req.(*ExecProcessRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Task_ResizePty_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ResizePtyRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).ResizePty(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/ResizePty",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).ResizePty(ctx, req.(*ResizePtyRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Task_CloseIO_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CloseIORequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).CloseIO(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/CloseIO",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).CloseIO(ctx, req.(*CloseIORequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Task_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateTaskRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).Update(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/Update",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).Update(ctx, req.(*UpdateTaskRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Task_Wait_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(WaitRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).Wait(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/Wait",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).Wait(ctx, req.(*WaitRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Task_Stats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(StatsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).Stats(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/Stats",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).Stats(ctx, req.(*StatsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Task_Connect_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ConnectRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).Connect(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/Connect",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).Connect(ctx, req.(*ConnectRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Task_Shutdown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ShutdownRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TaskServer).Shutdown(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/containerd.task.v3.Task/Shutdown",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TaskServer).Shutdown(ctx, req.(*ShutdownRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// Task_ServiceDesc is the grpc.ServiceDesc for Task service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var Task_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "containerd.task.v3.Task",
|
||||
HandlerType: (*TaskServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "State",
|
||||
Handler: _Task_State_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Create",
|
||||
Handler: _Task_Create_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Start",
|
||||
Handler: _Task_Start_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Delete",
|
||||
Handler: _Task_Delete_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Pids",
|
||||
Handler: _Task_Pids_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Pause",
|
||||
Handler: _Task_Pause_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Resume",
|
||||
Handler: _Task_Resume_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Checkpoint",
|
||||
Handler: _Task_Checkpoint_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Kill",
|
||||
Handler: _Task_Kill_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Exec",
|
||||
Handler: _Task_Exec_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ResizePty",
|
||||
Handler: _Task_ResizePty_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CloseIO",
|
||||
Handler: _Task_CloseIO_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Update",
|
||||
Handler: _Task_Update_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Wait",
|
||||
Handler: _Task_Wait_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Stats",
|
||||
Handler: _Task_Stats_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Connect",
|
||||
Handler: _Task_Connect_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Shutdown",
|
||||
Handler: _Task_Shutdown_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "runtime/task/v3/shim.proto",
|
||||
}
|
||||
301
vendor/github.com/containerd/containerd/api/runtime/task/v3/shim_ttrpc.pb.go
generated
vendored
Normal file
301
vendor/github.com/containerd/containerd/api/runtime/task/v3/shim_ttrpc.pb.go
generated
vendored
Normal file
@@ -0,0 +1,301 @@
|
||||
// Code generated by protoc-gen-go-ttrpc. DO NOT EDIT.
|
||||
// source: runtime/task/v3/shim.proto
|
||||
package task
|
||||
|
||||
import (
|
||||
context "context"
|
||||
ttrpc "github.com/containerd/ttrpc"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
type TTRPCTaskService interface {
|
||||
State(context.Context, *StateRequest) (*StateResponse, error)
|
||||
Create(context.Context, *CreateTaskRequest) (*CreateTaskResponse, error)
|
||||
Start(context.Context, *StartRequest) (*StartResponse, error)
|
||||
Delete(context.Context, *DeleteRequest) (*DeleteResponse, error)
|
||||
Pids(context.Context, *PidsRequest) (*PidsResponse, error)
|
||||
Pause(context.Context, *PauseRequest) (*emptypb.Empty, error)
|
||||
Resume(context.Context, *ResumeRequest) (*emptypb.Empty, error)
|
||||
Checkpoint(context.Context, *CheckpointTaskRequest) (*emptypb.Empty, error)
|
||||
Kill(context.Context, *KillRequest) (*emptypb.Empty, error)
|
||||
Exec(context.Context, *ExecProcessRequest) (*emptypb.Empty, error)
|
||||
ResizePty(context.Context, *ResizePtyRequest) (*emptypb.Empty, error)
|
||||
CloseIO(context.Context, *CloseIORequest) (*emptypb.Empty, error)
|
||||
Update(context.Context, *UpdateTaskRequest) (*emptypb.Empty, error)
|
||||
Wait(context.Context, *WaitRequest) (*WaitResponse, error)
|
||||
Stats(context.Context, *StatsRequest) (*StatsResponse, error)
|
||||
Connect(context.Context, *ConnectRequest) (*ConnectResponse, error)
|
||||
Shutdown(context.Context, *ShutdownRequest) (*emptypb.Empty, error)
|
||||
}
|
||||
|
||||
func RegisterTTRPCTaskService(srv *ttrpc.Server, svc TTRPCTaskService) {
|
||||
srv.RegisterService("containerd.task.v3.Task", &ttrpc.ServiceDesc{
|
||||
Methods: map[string]ttrpc.Method{
|
||||
"State": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req StateRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.State(ctx, &req)
|
||||
},
|
||||
"Create": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req CreateTaskRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Create(ctx, &req)
|
||||
},
|
||||
"Start": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req StartRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Start(ctx, &req)
|
||||
},
|
||||
"Delete": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req DeleteRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Delete(ctx, &req)
|
||||
},
|
||||
"Pids": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req PidsRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Pids(ctx, &req)
|
||||
},
|
||||
"Pause": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req PauseRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Pause(ctx, &req)
|
||||
},
|
||||
"Resume": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req ResumeRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Resume(ctx, &req)
|
||||
},
|
||||
"Checkpoint": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req CheckpointTaskRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Checkpoint(ctx, &req)
|
||||
},
|
||||
"Kill": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req KillRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Kill(ctx, &req)
|
||||
},
|
||||
"Exec": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req ExecProcessRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Exec(ctx, &req)
|
||||
},
|
||||
"ResizePty": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req ResizePtyRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.ResizePty(ctx, &req)
|
||||
},
|
||||
"CloseIO": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req CloseIORequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.CloseIO(ctx, &req)
|
||||
},
|
||||
"Update": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req UpdateTaskRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Update(ctx, &req)
|
||||
},
|
||||
"Wait": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req WaitRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Wait(ctx, &req)
|
||||
},
|
||||
"Stats": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req StatsRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Stats(ctx, &req)
|
||||
},
|
||||
"Connect": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req ConnectRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Connect(ctx, &req)
|
||||
},
|
||||
"Shutdown": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req ShutdownRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Shutdown(ctx, &req)
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
type ttrpctaskClient struct {
|
||||
client *ttrpc.Client
|
||||
}
|
||||
|
||||
func NewTTRPCTaskClient(client *ttrpc.Client) TTRPCTaskService {
|
||||
return &ttrpctaskClient{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) State(ctx context.Context, req *StateRequest) (*StateResponse, error) {
|
||||
var resp StateResponse
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "State", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Create(ctx context.Context, req *CreateTaskRequest) (*CreateTaskResponse, error) {
|
||||
var resp CreateTaskResponse
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "Create", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Start(ctx context.Context, req *StartRequest) (*StartResponse, error) {
|
||||
var resp StartResponse
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "Start", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Delete(ctx context.Context, req *DeleteRequest) (*DeleteResponse, error) {
|
||||
var resp DeleteResponse
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "Delete", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Pids(ctx context.Context, req *PidsRequest) (*PidsResponse, error) {
|
||||
var resp PidsResponse
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "Pids", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Pause(ctx context.Context, req *PauseRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "Pause", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Resume(ctx context.Context, req *ResumeRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "Resume", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Checkpoint(ctx context.Context, req *CheckpointTaskRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "Checkpoint", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Kill(ctx context.Context, req *KillRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "Kill", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Exec(ctx context.Context, req *ExecProcessRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "Exec", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) ResizePty(ctx context.Context, req *ResizePtyRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "ResizePty", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) CloseIO(ctx context.Context, req *CloseIORequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "CloseIO", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Update(ctx context.Context, req *UpdateTaskRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "Update", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Wait(ctx context.Context, req *WaitRequest) (*WaitResponse, error) {
|
||||
var resp WaitResponse
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "Wait", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Stats(ctx context.Context, req *StatsRequest) (*StatsResponse, error) {
|
||||
var resp StatsResponse
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "Stats", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Connect(ctx context.Context, req *ConnectRequest) (*ConnectResponse, error) {
|
||||
var resp ConnectResponse
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "Connect", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *ttrpctaskClient) Shutdown(ctx context.Context, req *ShutdownRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.task.v3.Task", "Shutdown", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
23
vendor/github.com/containerd/containerd/api/services/ttrpc/events/v1/doc.go
generated
vendored
Normal file
23
vendor/github.com/containerd/containerd/api/services/ttrpc/events/v1/doc.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package events defines the ttrpc event service.
|
||||
package events
|
||||
|
||||
import types "github.com/containerd/containerd/api/types"
|
||||
|
||||
// Deprecated: Use [types.Envelope].
|
||||
type Envelope = types.Envelope
|
||||
182
vendor/github.com/containerd/containerd/api/services/ttrpc/events/v1/events.pb.go
generated
vendored
Normal file
182
vendor/github.com/containerd/containerd/api/services/ttrpc/events/v1/events.pb.go
generated
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
//
|
||||
//Copyright The containerd Authors.
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
//http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc (unknown)
|
||||
// source: services/ttrpc/events/v1/events.proto
|
||||
|
||||
package events
|
||||
|
||||
import (
|
||||
types "github.com/containerd/containerd/api/types"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type ForwardRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Envelope *types.Envelope `protobuf:"bytes,1,opt,name=envelope,proto3" json:"envelope,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ForwardRequest) Reset() {
|
||||
*x = ForwardRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_services_ttrpc_events_v1_events_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ForwardRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ForwardRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ForwardRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_services_ttrpc_events_v1_events_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ForwardRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ForwardRequest) Descriptor() ([]byte, []int) {
|
||||
return file_services_ttrpc_events_v1_events_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ForwardRequest) GetEnvelope() *types.Envelope {
|
||||
if x != nil {
|
||||
return x.Envelope
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_services_ttrpc_events_v1_events_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_services_ttrpc_events_v1_events_proto_rawDesc = []byte{
|
||||
0x0a, 0x25, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x74, 0x74, 0x72, 0x70, 0x63,
|
||||
0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74,
|
||||
0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
|
||||
0x65, 0x72, 0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x65, 0x76, 0x65,
|
||||
0x6e, 0x74, 0x73, 0x2e, 0x74, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x76, 0x31, 0x1a, 0x1b, 0x67, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x11, 0x74, 0x79, 0x70, 0x65, 0x73,
|
||||
0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x48, 0x0a, 0x0e,
|
||||
0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36,
|
||||
0x0a, 0x08, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x74, 0x79,
|
||||
0x70, 0x65, 0x73, 0x2e, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x08, 0x65, 0x6e,
|
||||
0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x32, 0x60, 0x0a, 0x06, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73,
|
||||
0x12, 0x56, 0x0a, 0x07, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x12, 0x33, 0x2e, 0x63, 0x6f,
|
||||
0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
||||
0x73, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x74, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||
0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x46, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72,
|
||||
0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x61, 0x70, 0x69,
|
||||
0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x74, 0x74, 0x72, 0x70, 0x63, 0x2f,
|
||||
0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_services_ttrpc_events_v1_events_proto_rawDescOnce sync.Once
|
||||
file_services_ttrpc_events_v1_events_proto_rawDescData = file_services_ttrpc_events_v1_events_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_services_ttrpc_events_v1_events_proto_rawDescGZIP() []byte {
|
||||
file_services_ttrpc_events_v1_events_proto_rawDescOnce.Do(func() {
|
||||
file_services_ttrpc_events_v1_events_proto_rawDescData = protoimpl.X.CompressGZIP(file_services_ttrpc_events_v1_events_proto_rawDescData)
|
||||
})
|
||||
return file_services_ttrpc_events_v1_events_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_services_ttrpc_events_v1_events_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_services_ttrpc_events_v1_events_proto_goTypes = []interface{}{
|
||||
(*ForwardRequest)(nil), // 0: containerd.services.events.ttrpc.v1.ForwardRequest
|
||||
(*types.Envelope)(nil), // 1: containerd.types.Envelope
|
||||
(*emptypb.Empty)(nil), // 2: google.protobuf.Empty
|
||||
}
|
||||
var file_services_ttrpc_events_v1_events_proto_depIdxs = []int32{
|
||||
1, // 0: containerd.services.events.ttrpc.v1.ForwardRequest.envelope:type_name -> containerd.types.Envelope
|
||||
0, // 1: containerd.services.events.ttrpc.v1.Events.Forward:input_type -> containerd.services.events.ttrpc.v1.ForwardRequest
|
||||
2, // 2: containerd.services.events.ttrpc.v1.Events.Forward:output_type -> google.protobuf.Empty
|
||||
2, // [2:3] is the sub-list for method output_type
|
||||
1, // [1:2] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_services_ttrpc_events_v1_events_proto_init() }
|
||||
func file_services_ttrpc_events_v1_events_proto_init() {
|
||||
if File_services_ttrpc_events_v1_events_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_services_ttrpc_events_v1_events_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ForwardRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_services_ttrpc_events_v1_events_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_services_ttrpc_events_v1_events_proto_goTypes,
|
||||
DependencyIndexes: file_services_ttrpc_events_v1_events_proto_depIdxs,
|
||||
MessageInfos: file_services_ttrpc_events_v1_events_proto_msgTypes,
|
||||
}.Build()
|
||||
File_services_ttrpc_events_v1_events_proto = out.File
|
||||
file_services_ttrpc_events_v1_events_proto_rawDesc = nil
|
||||
file_services_ttrpc_events_v1_events_proto_goTypes = nil
|
||||
file_services_ttrpc_events_v1_events_proto_depIdxs = nil
|
||||
}
|
||||
37
vendor/github.com/containerd/containerd/api/services/ttrpc/events/v1/events.proto
generated
vendored
Normal file
37
vendor/github.com/containerd/containerd/api/services/ttrpc/events/v1/events.proto
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.services.events.ttrpc.v1;
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
import "types/event.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/ttrpc/events/v1;events";
|
||||
|
||||
service Events {
|
||||
// Forward sends an event that has already been packaged into an envelope
|
||||
// with a timestamp and namespace.
|
||||
//
|
||||
// This is useful if earlier timestamping is required or when forwarding on
|
||||
// behalf of another component, namespace or publisher.
|
||||
rpc Forward(ForwardRequest) returns (google.protobuf.Empty);
|
||||
}
|
||||
|
||||
message ForwardRequest {
|
||||
containerd.types.Envelope envelope = 1;
|
||||
}
|
||||
45
vendor/github.com/containerd/containerd/api/services/ttrpc/events/v1/events_ttrpc.pb.go
generated
vendored
Normal file
45
vendor/github.com/containerd/containerd/api/services/ttrpc/events/v1/events_ttrpc.pb.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// Code generated by protoc-gen-go-ttrpc. DO NOT EDIT.
|
||||
// source: services/ttrpc/events/v1/events.proto
|
||||
package events
|
||||
|
||||
import (
|
||||
context "context"
|
||||
ttrpc "github.com/containerd/ttrpc"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
type TTRPCEventsService interface {
|
||||
Forward(context.Context, *ForwardRequest) (*emptypb.Empty, error)
|
||||
}
|
||||
|
||||
func RegisterTTRPCEventsService(srv *ttrpc.Server, svc TTRPCEventsService) {
|
||||
srv.RegisterService("containerd.services.events.ttrpc.v1.Events", &ttrpc.ServiceDesc{
|
||||
Methods: map[string]ttrpc.Method{
|
||||
"Forward": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
|
||||
var req ForwardRequest
|
||||
if err := unmarshal(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.Forward(ctx, &req)
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
type ttrpceventsClient struct {
|
||||
client *ttrpc.Client
|
||||
}
|
||||
|
||||
func NewTTRPCEventsClient(client *ttrpc.Client) TTRPCEventsService {
|
||||
return &ttrpceventsClient{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ttrpceventsClient) Forward(ctx context.Context, req *ForwardRequest) (*emptypb.Empty, error) {
|
||||
var resp emptypb.Empty
|
||||
if err := c.client.Call(ctx, "containerd.services.events.ttrpc.v1.Events", "Forward", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
210
vendor/github.com/containerd/containerd/v2/core/diff/apply/apply.go
generated
vendored
Normal file
210
vendor/github.com/containerd/containerd/v2/core/diff/apply/apply.go
generated
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apply
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/content"
|
||||
"github.com/containerd/containerd/v2/core/diff"
|
||||
"github.com/containerd/containerd/v2/core/mount"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/containerd/log"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// NewFileSystemApplier returns an applier which simply mounts
|
||||
// and applies diff onto the mounted filesystem.
|
||||
func NewFileSystemApplier(cs content.Provider) diff.Applier {
|
||||
return NewFileSystemApplierWithMountManager(cs, nil)
|
||||
}
|
||||
|
||||
// NewFileSystemApplierWithMountManager returns an applier which simply mounts and
|
||||
// applies diff onto the mounted filesystem.
|
||||
// An optional mount manager can be specified and it will take effect when applying.
|
||||
func NewFileSystemApplierWithMountManager(cs content.Provider, mm mount.Manager) diff.Applier {
|
||||
return &fsApplier{
|
||||
store: cs,
|
||||
mount: mm,
|
||||
}
|
||||
}
|
||||
|
||||
type fsApplier struct {
|
||||
store content.Provider
|
||||
mount mount.Manager
|
||||
}
|
||||
|
||||
var emptyDesc = ocispec.Descriptor{}
|
||||
|
||||
// Apply applies the content associated with the provided digests onto the
|
||||
// provided mounts. Archive content will be extracted and decompressed if
|
||||
// necessary.
|
||||
func (s *fsApplier) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []mount.Mount, opts ...diff.ApplyOpt) (d ocispec.Descriptor, err error) {
|
||||
t1 := time.Now()
|
||||
defer func() {
|
||||
if err == nil {
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
"d": time.Since(t1),
|
||||
"digest": desc.Digest,
|
||||
"size": desc.Size,
|
||||
"media": desc.MediaType,
|
||||
}).Debugf("diff applied")
|
||||
}
|
||||
}()
|
||||
|
||||
var config diff.ApplyConfig
|
||||
for _, o := range opts {
|
||||
if err := o(ctx, desc, &config); err != nil {
|
||||
return emptyDesc, fmt.Errorf("failed to apply config opt: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
ra, err := s.store.ReaderAt(ctx, desc)
|
||||
if err != nil {
|
||||
return emptyDesc, fmt.Errorf("failed to get reader from content store: %w", err)
|
||||
}
|
||||
var r io.ReadCloser
|
||||
if config.Progress != nil {
|
||||
r = newProgressReader(ra, config.Progress)
|
||||
} else {
|
||||
r = newReadCloser(ra)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
var processors []diff.StreamProcessor
|
||||
processor := diff.NewProcessorChain(desc.MediaType, r)
|
||||
processors = append(processors, processor)
|
||||
for {
|
||||
if processor, err = diff.GetProcessor(ctx, processor, config.ProcessorPayloads); err != nil {
|
||||
return emptyDesc, fmt.Errorf("failed to get stream processor for %s: %w", desc.MediaType, err)
|
||||
}
|
||||
processors = append(processors, processor)
|
||||
if processor.MediaType() == ocispec.MediaTypeImageLayer {
|
||||
break
|
||||
}
|
||||
}
|
||||
defer processor.Close()
|
||||
|
||||
digester := digest.Canonical.Digester()
|
||||
rc := &readCounter{
|
||||
r: io.TeeReader(processor, digester.Hash()),
|
||||
}
|
||||
|
||||
// The number of `mounts` that need to be parsed by the mount manager
|
||||
// will be more than 1 in reality; this is needed to work around some
|
||||
// overlayfs/bind shortcuts in core/diff/apply/apply_linux.go
|
||||
if s.mount != nil && len(mounts) > 1 {
|
||||
var b [3]byte
|
||||
// Ignore read failures, just decreases uniqueness
|
||||
rand.Read(b[:])
|
||||
id := fmt.Sprintf("fs-diffapply-%d-%s", t1.Nanosecond(), base64.URLEncoding.EncodeToString(b[:]))
|
||||
info, err := s.mount.Activate(ctx, id, mounts)
|
||||
if err == nil {
|
||||
defer s.mount.Deactivate(ctx, id)
|
||||
mounts = info.System
|
||||
} else if !errdefs.IsNotImplemented(err) {
|
||||
return emptyDesc, fmt.Errorf("failed to activate mounts: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := apply(ctx, mounts, rc, config.SyncFs); err != nil {
|
||||
return emptyDesc, err
|
||||
}
|
||||
|
||||
// Read any trailing data
|
||||
if _, err := io.Copy(io.Discard, rc); err != nil {
|
||||
return emptyDesc, err
|
||||
}
|
||||
|
||||
for _, p := range processors {
|
||||
if ep, ok := p.(interface {
|
||||
Err() error
|
||||
}); ok {
|
||||
if err := ep.Err(); err != nil {
|
||||
return emptyDesc, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ocispec.Descriptor{
|
||||
MediaType: ocispec.MediaTypeImageLayer,
|
||||
Size: rc.c,
|
||||
Digest: digester.Digest(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type readCounter struct {
|
||||
r io.Reader
|
||||
c int64
|
||||
}
|
||||
|
||||
func (rc *readCounter) Read(p []byte) (n int, err error) {
|
||||
n, err = rc.r.Read(p)
|
||||
if n > 0 {
|
||||
rc.c += int64(n)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type progressReader struct {
|
||||
rc *readCounter
|
||||
c io.Closer
|
||||
p func(int64)
|
||||
}
|
||||
|
||||
func newProgressReader(ra content.ReaderAt, p func(int64)) io.ReadCloser {
|
||||
return &progressReader{
|
||||
rc: &readCounter{
|
||||
r: content.NewReader(ra),
|
||||
c: 0,
|
||||
},
|
||||
c: ra,
|
||||
p: p,
|
||||
}
|
||||
}
|
||||
|
||||
func (pr *progressReader) Read(p []byte) (n int, err error) {
|
||||
// Call the progress function with the current count, indicating
|
||||
// the previously read content has been processed. Initial
|
||||
// progress of 0 indicates start of processing.
|
||||
pr.p(pr.rc.c)
|
||||
n, err = pr.rc.Read(p)
|
||||
return
|
||||
}
|
||||
|
||||
func (pr *progressReader) Close() error {
|
||||
pr.p(pr.rc.c)
|
||||
return pr.c.Close()
|
||||
}
|
||||
|
||||
type readCloser struct {
|
||||
io.Reader
|
||||
io.Closer
|
||||
}
|
||||
|
||||
func newReadCloser(ra content.ReaderAt) io.ReadCloser {
|
||||
return &readCloser{
|
||||
Reader: content.NewReader(ra),
|
||||
Closer: ra,
|
||||
}
|
||||
}
|
||||
105
vendor/github.com/containerd/containerd/v2/core/diff/apply/apply_linux.go
generated
vendored
Normal file
105
vendor/github.com/containerd/containerd/v2/core/diff/apply/apply_linux.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apply
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/moby/sys/userns"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/mount"
|
||||
"github.com/containerd/containerd/v2/pkg/archive"
|
||||
"github.com/containerd/errdefs"
|
||||
)
|
||||
|
||||
func apply(ctx context.Context, mounts []mount.Mount, r io.Reader, sync bool) (retErr error) {
|
||||
switch {
|
||||
case len(mounts) == 1 && mounts[0].Type == "overlay":
|
||||
// OverlayConvertWhiteout (mknod c 0 0) doesn't work in userns.
|
||||
// https://github.com/containerd/containerd/issues/3762
|
||||
if userns.RunningInUserNS() {
|
||||
break
|
||||
}
|
||||
path, parents, err := getOverlayPath(mounts[0].Options)
|
||||
if err != nil {
|
||||
if errdefs.IsInvalidArgument(err) {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
opts := []archive.ApplyOpt{
|
||||
archive.WithConvertWhiteout(archive.OverlayConvertWhiteout),
|
||||
}
|
||||
if len(parents) > 0 {
|
||||
opts = append(opts, archive.WithParents(parents))
|
||||
}
|
||||
_, err = archive.Apply(ctx, path, r, opts...)
|
||||
if err == nil && sync {
|
||||
err = doSyncFs(path)
|
||||
}
|
||||
return err
|
||||
case sync && len(mounts) == 1 && mounts[0].Type == "bind":
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
retErr = doSyncFs(mounts[0].Source)
|
||||
}()
|
||||
}
|
||||
return mount.WithTempMount(ctx, mounts, func(root string) error {
|
||||
_, err := archive.Apply(ctx, root, r)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func getOverlayPath(options []string) (upper string, lower []string, err error) {
|
||||
const upperdirPrefix = "upperdir="
|
||||
const lowerdirPrefix = "lowerdir="
|
||||
|
||||
for _, o := range options {
|
||||
if after, ok := strings.CutPrefix(o, upperdirPrefix); ok {
|
||||
upper = after
|
||||
} else if after, ok := strings.CutPrefix(o, lowerdirPrefix); ok {
|
||||
lower = strings.Split(after, ":")
|
||||
}
|
||||
}
|
||||
if upper == "" {
|
||||
return "", nil, fmt.Errorf("upperdir not found: %w", errdefs.ErrInvalidArgument)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func doSyncFs(file string) error {
|
||||
fd, err := os.Open(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open %s: %w", file, err)
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
err = unix.Syncfs(int(fd.Fd()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to syncfs for %s: %w", file, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
51
vendor/github.com/containerd/containerd/v2/core/diff/apply/apply_other.go
generated
vendored
Normal file
51
vendor/github.com/containerd/containerd/v2/core/diff/apply/apply_other.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
//go:build !linux
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apply
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/mount"
|
||||
"github.com/containerd/containerd/v2/pkg/archive"
|
||||
)
|
||||
|
||||
func apply(ctx context.Context, mounts []mount.Mount, r io.Reader, _sync bool) error {
|
||||
// TODO: for windows, how to sync?
|
||||
|
||||
if !mount.HasBindMounts && len(mounts) == 1 && mounts[0].Type == "bind" {
|
||||
opts := []archive.ApplyOpt{}
|
||||
|
||||
if os.Getuid() != 0 {
|
||||
opts = append(opts, archive.WithNoSameOwner())
|
||||
}
|
||||
|
||||
path := mounts[0].Source
|
||||
_, err := archive.Apply(ctx, path, r, opts...)
|
||||
return err
|
||||
|
||||
// TODO: Do we need to sync all the filesystems?
|
||||
}
|
||||
|
||||
return mount.WithTempMount(ctx, mounts, func(root string) error {
|
||||
_, err := archive.Apply(ctx, root, r)
|
||||
return err
|
||||
})
|
||||
}
|
||||
248
vendor/github.com/containerd/containerd/v2/core/events/exchange/exchange.go
generated
vendored
Normal file
248
vendor/github.com/containerd/containerd/v2/core/events/exchange/exchange.go
generated
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package exchange
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/events"
|
||||
"github.com/containerd/containerd/v2/pkg/filters"
|
||||
"github.com/containerd/containerd/v2/pkg/identifiers"
|
||||
"github.com/containerd/containerd/v2/pkg/namespaces"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/containerd/log"
|
||||
"github.com/containerd/typeurl/v2"
|
||||
goevents "github.com/docker/go-events"
|
||||
)
|
||||
|
||||
// Exchange broadcasts events
|
||||
type Exchange struct {
|
||||
broadcaster *goevents.Broadcaster
|
||||
}
|
||||
|
||||
// NewExchange returns a new event Exchange
|
||||
func NewExchange() *Exchange {
|
||||
return &Exchange{
|
||||
broadcaster: goevents.NewBroadcaster(),
|
||||
}
|
||||
}
|
||||
|
||||
var _ events.Publisher = &Exchange{}
|
||||
var _ events.Forwarder = &Exchange{}
|
||||
var _ events.Subscriber = &Exchange{}
|
||||
|
||||
// Forward accepts an envelope to be directly distributed on the exchange.
|
||||
//
|
||||
// This is useful when an event is forwarded on behalf of another namespace or
|
||||
// when the event is propagated on behalf of another publisher.
|
||||
func (e *Exchange) Forward(ctx context.Context, envelope *events.Envelope) (err error) {
|
||||
if err := validateEnvelope(envelope); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
logger := log.G(ctx).WithFields(log.Fields{
|
||||
"topic": envelope.Topic,
|
||||
"ns": envelope.Namespace,
|
||||
"type": envelope.Event.GetTypeUrl(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("error forwarding event")
|
||||
} else {
|
||||
logger.Trace("event forwarded")
|
||||
}
|
||||
}()
|
||||
|
||||
return e.broadcaster.Write(envelope)
|
||||
}
|
||||
|
||||
// Publish packages and sends an event. The caller will be considered the
|
||||
// initial publisher of the event. This means the timestamp will be calculated
|
||||
// at this point and this method may read from the calling context.
|
||||
func (e *Exchange) Publish(ctx context.Context, topic string, event events.Event) (err error) {
|
||||
var (
|
||||
namespace string
|
||||
envelope events.Envelope
|
||||
)
|
||||
|
||||
namespace, err = namespaces.NamespaceRequired(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed publishing event: %w", err)
|
||||
}
|
||||
if err := validateTopic(topic); err != nil {
|
||||
return fmt.Errorf("envelope topic %q: %w", topic, err)
|
||||
}
|
||||
|
||||
encoded, err := typeurl.MarshalAny(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
envelope.Timestamp = time.Now().UTC()
|
||||
envelope.Namespace = namespace
|
||||
envelope.Topic = topic
|
||||
envelope.Event = encoded
|
||||
|
||||
defer func() {
|
||||
logger := log.G(ctx).WithFields(log.Fields{
|
||||
"topic": envelope.Topic,
|
||||
"ns": envelope.Namespace,
|
||||
"type": envelope.Event.GetTypeUrl(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("error publishing event")
|
||||
} else {
|
||||
logger.Trace("event published")
|
||||
}
|
||||
}()
|
||||
|
||||
return e.broadcaster.Write(&envelope)
|
||||
}
|
||||
|
||||
// Subscribe to events on the exchange. Events are sent through the returned
|
||||
// channel ch. If an error is encountered, it will be sent on channel errs and
|
||||
// errs will be closed. To end the subscription, cancel the provided context.
|
||||
//
|
||||
// Zero or more filters may be provided as strings. Only events that match
|
||||
// *any* of the provided filters will be sent on the channel. The filters use
|
||||
// the standard containerd filters package syntax.
|
||||
func (e *Exchange) Subscribe(ctx context.Context, fs ...string) (ch <-chan *events.Envelope, errs <-chan error) {
|
||||
var (
|
||||
evch = make(chan *events.Envelope)
|
||||
errq = make(chan error, 1)
|
||||
channel = goevents.NewChannel(0)
|
||||
queue = goevents.NewQueue(channel)
|
||||
dst goevents.Sink = queue
|
||||
)
|
||||
|
||||
closeAll := func() {
|
||||
channel.Close()
|
||||
queue.Close()
|
||||
e.broadcaster.Remove(dst)
|
||||
close(errq)
|
||||
}
|
||||
|
||||
ch = evch
|
||||
errs = errq
|
||||
|
||||
if len(fs) > 0 {
|
||||
filter, err := filters.ParseAll(fs...)
|
||||
if err != nil {
|
||||
errq <- fmt.Errorf("failed parsing subscription filters: %w", err)
|
||||
closeAll()
|
||||
return
|
||||
}
|
||||
|
||||
dst = goevents.NewFilter(queue, goevents.MatcherFunc(func(gev goevents.Event) bool {
|
||||
return filter.Match(adapt(gev))
|
||||
}))
|
||||
}
|
||||
|
||||
e.broadcaster.Add(dst)
|
||||
|
||||
go func() {
|
||||
defer closeAll()
|
||||
|
||||
var err error
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case ev := <-channel.C:
|
||||
env, ok := ev.(*events.Envelope)
|
||||
if !ok {
|
||||
// TODO(stevvooe): For the most part, we are well protected
|
||||
// from this condition. Both Forward and Publish protect
|
||||
// from this.
|
||||
err = fmt.Errorf("invalid envelope encountered %#v; please file a bug", ev)
|
||||
break
|
||||
}
|
||||
|
||||
select {
|
||||
case evch <- env:
|
||||
case <-ctx.Done():
|
||||
break loop
|
||||
}
|
||||
case <-ctx.Done():
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if cerr := ctx.Err(); cerr != context.Canceled {
|
||||
err = cerr
|
||||
}
|
||||
}
|
||||
|
||||
errq <- err
|
||||
}()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func validateTopic(topic string) error {
|
||||
if topic == "" {
|
||||
return fmt.Errorf("must not be empty: %w", errdefs.ErrInvalidArgument)
|
||||
}
|
||||
|
||||
if topic[0] != '/' {
|
||||
return fmt.Errorf("must start with '/': %w", errdefs.ErrInvalidArgument)
|
||||
}
|
||||
|
||||
if len(topic) == 1 {
|
||||
return fmt.Errorf("must have at least one component: %w", errdefs.ErrInvalidArgument)
|
||||
}
|
||||
|
||||
components := strings.SplitSeq(topic[1:], "/")
|
||||
for component := range components {
|
||||
if err := identifiers.Validate(component); err != nil {
|
||||
return fmt.Errorf("failed validation on component %q: %w", component, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateEnvelope(envelope *events.Envelope) error {
|
||||
if err := identifiers.Validate(envelope.Namespace); err != nil {
|
||||
return fmt.Errorf("event envelope has invalid namespace: %w", err)
|
||||
}
|
||||
|
||||
if err := validateTopic(envelope.Topic); err != nil {
|
||||
return fmt.Errorf("envelope topic %q: %w", envelope.Topic, err)
|
||||
}
|
||||
|
||||
if envelope.Timestamp.IsZero() {
|
||||
return fmt.Errorf("timestamp must be set on forwarded event: %w", errdefs.ErrInvalidArgument)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func adapt(ev any) filters.Adaptor {
|
||||
if adaptor, ok := ev.(filters.Adaptor); ok {
|
||||
return adaptor
|
||||
}
|
||||
|
||||
return filters.AdapterFunc(func(fieldpath []string) (string, bool) {
|
||||
return "", false
|
||||
})
|
||||
}
|
||||
98
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/cgroups.go
generated
vendored
Normal file
98
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/cgroups.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
//go:build linux
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/containerd/cgroups/v3"
|
||||
"github.com/containerd/containerd/v2/core/events"
|
||||
v1 "github.com/containerd/containerd/v2/core/metrics/cgroups/v1"
|
||||
v2 "github.com/containerd/containerd/v2/core/metrics/cgroups/v2"
|
||||
"github.com/containerd/containerd/v2/core/runtime"
|
||||
"github.com/containerd/containerd/v2/plugins"
|
||||
"github.com/containerd/containerd/v2/version"
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/containerd/plugin"
|
||||
"github.com/containerd/plugin/registry"
|
||||
metrics "github.com/docker/go-metrics"
|
||||
)
|
||||
|
||||
// Config for the cgroups monitor
|
||||
type Config struct {
|
||||
NoPrometheus bool `toml:"no_prometheus"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.Register(&plugin.Registration{
|
||||
Type: plugins.TaskMonitorPlugin,
|
||||
ID: "cgroups",
|
||||
InitFn: New,
|
||||
Requires: []plugin.Type{
|
||||
plugins.EventPlugin,
|
||||
},
|
||||
Config: &Config{},
|
||||
ConfigMigration: func(ctx context.Context, configVersion int, pluginConfigs map[string]any) error {
|
||||
if configVersion >= version.ConfigVersion {
|
||||
return nil
|
||||
}
|
||||
// Previous plugin name
|
||||
const pluginName = "io.containerd.monitor.v1.cgroups"
|
||||
c, ok := pluginConfigs[pluginName]
|
||||
if ok {
|
||||
pluginConfigs[string(plugins.TaskMonitorPlugin)+".cgroups"] = c
|
||||
delete(pluginConfigs, pluginName)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// New returns a new cgroups monitor
|
||||
func New(ic *plugin.InitContext) (any, error) {
|
||||
var ns *metrics.Namespace
|
||||
config := ic.Config.(*Config)
|
||||
if !config.NoPrometheus {
|
||||
ns = metrics.NewNamespace("container", "", nil)
|
||||
}
|
||||
var (
|
||||
tm runtime.TaskMonitor
|
||||
err error
|
||||
)
|
||||
|
||||
ep, err := ic.GetSingle(plugins.EventPlugin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cgroups.Mode() == cgroups.Unified {
|
||||
tm, err = v2.NewTaskMonitor(ic.Context, ep.(events.Publisher), ns)
|
||||
} else {
|
||||
tm, err = v1.NewTaskMonitor(ic.Context, ep.(events.Publisher), ns)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ns != nil {
|
||||
metrics.Register(ns)
|
||||
}
|
||||
ic.Meta.Platforms = append(ic.Meta.Platforms, platforms.DefaultSpec())
|
||||
return tm, nil
|
||||
}
|
||||
32
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/common/type.go
generated
vendored
Normal file
32
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/common/type.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
//go:build linux
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/containerd/containerd/v2/pkg/protobuf/types"
|
||||
)
|
||||
|
||||
// Statable type that returns cgroup metrics
|
||||
type Statable interface {
|
||||
ID() string
|
||||
Namespace() string
|
||||
Stats(context.Context) (*types.Any, error)
|
||||
}
|
||||
132
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/blkio.go
generated
vendored
Normal file
132
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/blkio.go
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
//go:build linux
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
v1 "github.com/containerd/containerd/v2/core/metrics/types/v1"
|
||||
metrics "github.com/docker/go-metrics"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var blkioMetrics = []*metric{
|
||||
{
|
||||
name: "blkio_io_merged_recursive",
|
||||
help: "The blkio io merged recursive",
|
||||
unit: metrics.Total,
|
||||
vt: prometheus.GaugeValue,
|
||||
labels: []string{"op", "device", "major", "minor"},
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Blkio == nil {
|
||||
return nil
|
||||
}
|
||||
return blkioValues(stats.Blkio.IoMergedRecursive)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "blkio_io_queued_recursive",
|
||||
help: "The blkio io queued recursive",
|
||||
unit: metrics.Total,
|
||||
vt: prometheus.GaugeValue,
|
||||
labels: []string{"op", "device", "major", "minor"},
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Blkio == nil {
|
||||
return nil
|
||||
}
|
||||
return blkioValues(stats.Blkio.IoQueuedRecursive)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "blkio_io_service_bytes_recursive",
|
||||
help: "The blkio io service bytes recursive",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
labels: []string{"op", "device", "major", "minor"},
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Blkio == nil {
|
||||
return nil
|
||||
}
|
||||
return blkioValues(stats.Blkio.IoServiceBytesRecursive)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "blkio_io_service_time_recursive",
|
||||
help: "The blkio io service time recursive",
|
||||
unit: metrics.Total,
|
||||
vt: prometheus.GaugeValue,
|
||||
labels: []string{"op", "device", "major", "minor"},
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Blkio == nil {
|
||||
return nil
|
||||
}
|
||||
return blkioValues(stats.Blkio.IoServiceTimeRecursive)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "blkio_io_serviced_recursive",
|
||||
help: "The blkio io serviced recursive",
|
||||
unit: metrics.Total,
|
||||
vt: prometheus.GaugeValue,
|
||||
labels: []string{"op", "device", "major", "minor"},
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Blkio == nil {
|
||||
return nil
|
||||
}
|
||||
return blkioValues(stats.Blkio.IoServicedRecursive)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "blkio_io_time_recursive",
|
||||
help: "The blkio io time recursive",
|
||||
unit: metrics.Total,
|
||||
vt: prometheus.GaugeValue,
|
||||
labels: []string{"op", "device", "major", "minor"},
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Blkio == nil {
|
||||
return nil
|
||||
}
|
||||
return blkioValues(stats.Blkio.IoTimeRecursive)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "blkio_sectors_recursive",
|
||||
help: "The blkio sectors recursive",
|
||||
unit: metrics.Total,
|
||||
vt: prometheus.GaugeValue,
|
||||
labels: []string{"op", "device", "major", "minor"},
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Blkio == nil {
|
||||
return nil
|
||||
}
|
||||
return blkioValues(stats.Blkio.SectorsRecursive)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func blkioValues(l []*v1.BlkIOEntry) []value {
|
||||
var out []value
|
||||
for _, e := range l {
|
||||
out = append(out, value{
|
||||
v: float64(e.Value),
|
||||
l: []string{e.Op, e.Device, strconv.FormatUint(e.Major, 10), strconv.FormatUint(e.Minor, 10)},
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
95
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/cgroups.go
generated
vendored
Normal file
95
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/cgroups.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
//go:build linux
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
cgroups "github.com/containerd/cgroups/v3/cgroup1"
|
||||
eventstypes "github.com/containerd/containerd/api/events"
|
||||
"github.com/containerd/containerd/v2/core/events"
|
||||
"github.com/containerd/containerd/v2/core/runtime"
|
||||
"github.com/containerd/containerd/v2/pkg/namespaces"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/containerd/log"
|
||||
"github.com/docker/go-metrics"
|
||||
)
|
||||
|
||||
// NewTaskMonitor returns a new cgroups monitor
|
||||
func NewTaskMonitor(ctx context.Context, publisher events.Publisher, ns *metrics.Namespace) (runtime.TaskMonitor, error) {
|
||||
collector := NewCollector(ns)
|
||||
oom, err := newOOMCollector(ns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cgroupsMonitor{
|
||||
collector: collector,
|
||||
oom: oom,
|
||||
context: ctx,
|
||||
publisher: publisher,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type cgroupsMonitor struct {
|
||||
collector *Collector
|
||||
oom *oomCollector
|
||||
context context.Context
|
||||
publisher events.Publisher
|
||||
}
|
||||
|
||||
type cgroupTask interface {
|
||||
Cgroup() (cgroups.Cgroup, error)
|
||||
}
|
||||
|
||||
func (m *cgroupsMonitor) Monitor(c runtime.Task, labels map[string]string) error {
|
||||
if err := m.collector.Add(c, labels); err != nil {
|
||||
return err
|
||||
}
|
||||
t, ok := c.(cgroupTask)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
cg, err := t.Cgroup()
|
||||
if err != nil {
|
||||
if errdefs.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = m.oom.Add(c.ID(), c.Namespace(), cg, m.trigger)
|
||||
if err == cgroups.ErrMemoryNotSupported {
|
||||
log.L.WithError(err).Warn("OOM monitoring failed")
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *cgroupsMonitor) Stop(c runtime.Task) error {
|
||||
m.collector.Remove(c)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *cgroupsMonitor) trigger(id, namespace string, cg cgroups.Cgroup) {
|
||||
ctx := namespaces.WithNamespace(m.context, namespace)
|
||||
if err := m.publisher.Publish(ctx, runtime.TaskOOMEventTopic, &eventstypes.TaskOOM{
|
||||
ContainerID: id,
|
||||
}); err != nil {
|
||||
log.G(m.context).WithError(err).Error("post OOM event")
|
||||
}
|
||||
}
|
||||
146
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/cpu.go
generated
vendored
Normal file
146
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/cpu.go
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
//go:build linux
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
v1 "github.com/containerd/containerd/v2/core/metrics/types/v1"
|
||||
metrics "github.com/docker/go-metrics"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var cpuMetrics = []*metric{
|
||||
{
|
||||
name: "cpu_total",
|
||||
help: "The total cpu time",
|
||||
unit: metrics.Nanoseconds,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.CPU == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.CPU.Usage.Total),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cpu_kernel",
|
||||
help: "The total kernel cpu time",
|
||||
unit: metrics.Nanoseconds,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.CPU == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.CPU.Usage.Kernel),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cpu_user",
|
||||
help: "The total user cpu time",
|
||||
unit: metrics.Nanoseconds,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.CPU == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.CPU.Usage.User),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "per_cpu",
|
||||
help: "The total cpu time per cpu",
|
||||
unit: metrics.Nanoseconds,
|
||||
vt: prometheus.GaugeValue,
|
||||
labels: []string{"cpu"},
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.CPU == nil {
|
||||
return nil
|
||||
}
|
||||
var out []value
|
||||
for i, v := range stats.CPU.Usage.PerCPU {
|
||||
out = append(out, value{
|
||||
v: float64(v),
|
||||
l: []string{strconv.Itoa(i)},
|
||||
})
|
||||
}
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cpu_throttle_periods",
|
||||
help: "The total cpu throttle periods",
|
||||
unit: metrics.Total,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.CPU == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.CPU.Throttling.Periods),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cpu_throttled_periods",
|
||||
help: "The total cpu throttled periods",
|
||||
unit: metrics.Total,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.CPU == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.CPU.Throttling.ThrottledPeriods),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cpu_throttled_time",
|
||||
help: "The total cpu throttled time",
|
||||
unit: metrics.Nanoseconds,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.CPU == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.CPU.Throttling.ThrottledTime),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
88
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/hugetlb.go
generated
vendored
Normal file
88
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/hugetlb.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
//go:build linux
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
v1 "github.com/containerd/containerd/v2/core/metrics/types/v1"
|
||||
metrics "github.com/docker/go-metrics"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var hugetlbMetrics = []*metric{
|
||||
{
|
||||
name: "hugetlb_usage",
|
||||
help: "The hugetlb usage",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
labels: []string{"page"},
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Hugetlb == nil {
|
||||
return nil
|
||||
}
|
||||
var out []value
|
||||
for _, v := range stats.Hugetlb {
|
||||
out = append(out, value{
|
||||
v: float64(v.Usage),
|
||||
l: []string{v.Pagesize},
|
||||
})
|
||||
}
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hugetlb_failcnt",
|
||||
help: "The hugetlb failcnt",
|
||||
unit: metrics.Total,
|
||||
vt: prometheus.GaugeValue,
|
||||
labels: []string{"page"},
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Hugetlb == nil {
|
||||
return nil
|
||||
}
|
||||
var out []value
|
||||
for _, v := range stats.Hugetlb {
|
||||
out = append(out, value{
|
||||
v: float64(v.Failcnt),
|
||||
l: []string{v.Pagesize},
|
||||
})
|
||||
}
|
||||
return out
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hugetlb_max",
|
||||
help: "The hugetlb maximum usage",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
labels: []string{"page"},
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Hugetlb == nil {
|
||||
return nil
|
||||
}
|
||||
var out []value
|
||||
for _, v := range stats.Hugetlb {
|
||||
out = append(out, value{
|
||||
v: float64(v.Max),
|
||||
l: []string{v.Pagesize},
|
||||
})
|
||||
}
|
||||
return out
|
||||
},
|
||||
},
|
||||
}
|
||||
796
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/memory.go
generated
vendored
Normal file
796
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/memory.go
generated
vendored
Normal file
@@ -0,0 +1,796 @@
|
||||
//go:build linux
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
v1 "github.com/containerd/containerd/v2/core/metrics/types/v1"
|
||||
metrics "github.com/docker/go-metrics"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var memoryMetrics = []*metric{
|
||||
{
|
||||
name: "memory_cache",
|
||||
help: "The cache amount used",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.Cache),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_rss",
|
||||
help: "The rss amount used",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.RSS),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_rss_huge",
|
||||
help: "The rss_huge amount used",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.RSSHuge),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_mapped_file",
|
||||
help: "The mapped_file amount used",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.MappedFile),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_dirty",
|
||||
help: "The dirty amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.Dirty),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_writeback",
|
||||
help: "The writeback amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.Writeback),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_pgpgin",
|
||||
help: "The pgpgin amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.PgPgIn),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_pgpgout",
|
||||
help: "The pgpgout amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.PgPgOut),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_pgfault",
|
||||
help: "The pgfault amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.PgFault),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_pgmajfault",
|
||||
help: "The pgmajfault amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.PgMajFault),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_inactive_anon",
|
||||
help: "The inactive_anon amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.InactiveAnon),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_active_anon",
|
||||
help: "The active_anon amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.ActiveAnon),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_inactive_file",
|
||||
help: "The inactive_file amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.InactiveFile),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_active_file",
|
||||
help: "The active_file amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.ActiveFile),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_unevictable",
|
||||
help: "The unevictable amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.Unevictable),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_hierarchical_memory_limit",
|
||||
help: "The hierarchical_memory_limit amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.HierarchicalMemoryLimit),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_hierarchical_memsw_limit",
|
||||
help: "The hierarchical_memsw_limit amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.HierarchicalSwapLimit),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_total_cache",
|
||||
help: "The total_cache amount used",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.TotalCache),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_total_rss",
|
||||
help: "The total_rss amount used",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.TotalRSS),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_total_rss_huge",
|
||||
help: "The total_rss_huge amount used",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.TotalRSSHuge),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_total_mapped_file",
|
||||
help: "The total_mapped_file amount used",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.TotalMappedFile),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_total_dirty",
|
||||
help: "The total_dirty amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.TotalDirty),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_total_writeback",
|
||||
help: "The total_writeback amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.TotalWriteback),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_total_pgpgin",
|
||||
help: "The total_pgpgin amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.TotalPgPgIn),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_total_pgpgout",
|
||||
help: "The total_pgpgout amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.TotalPgPgOut),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_total_pgfault",
|
||||
help: "The total_pgfault amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.TotalPgFault),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_total_pgmajfault",
|
||||
help: "The total_pgmajfault amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.TotalPgMajFault),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_total_inactive_anon",
|
||||
help: "The total_inactive_anon amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.TotalInactiveAnon),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_total_active_anon",
|
||||
help: "The total_active_anon amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.TotalActiveAnon),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_total_inactive_file",
|
||||
help: "The total_inactive_file amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.TotalInactiveFile),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_total_active_file",
|
||||
help: "The total_active_file amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.TotalActiveFile),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_total_unevictable",
|
||||
help: "The total_unevictable amount",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.TotalUnevictable),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_usage_failcnt",
|
||||
help: "The usage failcnt",
|
||||
unit: metrics.Total,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.GetMemory().GetUsage() == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.Usage.Failcnt),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_usage_limit",
|
||||
help: "The memory limit",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.GetMemory().GetUsage() == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.Usage.Limit),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_usage_max",
|
||||
help: "The memory maximum usage",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.GetMemory().GetUsage() == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.Usage.Max),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_usage_usage",
|
||||
help: "The memory usage",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.GetMemory().GetUsage() == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.Usage.Usage),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_swap_failcnt",
|
||||
help: "The swap failcnt",
|
||||
unit: metrics.Total,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.GetMemory().GetSwap() == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.Swap.Failcnt),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_swap_limit",
|
||||
help: "The swap limit",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.GetMemory().GetSwap() == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.Swap.Limit),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_swap_max",
|
||||
help: "The swap maximum usage",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.GetMemory().GetSwap() == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.Swap.Max),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_swap_usage",
|
||||
help: "The swap usage",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.GetMemory().GetSwap() == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.Swap.Usage),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_kernel_failcnt",
|
||||
help: "The kernel failcnt",
|
||||
unit: metrics.Total,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.GetMemory().GetKernel() == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.Kernel.Failcnt),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_kernel_limit",
|
||||
help: "The kernel limit",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.GetMemory().GetKernel() == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.Kernel.Limit),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_kernel_max",
|
||||
help: "The kernel maximum usage",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.GetMemory().GetKernel() == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.Kernel.Max),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_kernel_usage",
|
||||
help: "The kernel usage",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.GetMemory().GetKernel() == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.Kernel.Usage),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_kerneltcp_failcnt",
|
||||
help: "The kerneltcp failcnt",
|
||||
unit: metrics.Total,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.GetMemory().GetKernelTCP() == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.KernelTCP.Failcnt),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_kerneltcp_limit",
|
||||
help: "The kerneltcp limit",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.GetMemory().GetKernelTCP() == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.KernelTCP.Limit),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_kerneltcp_max",
|
||||
help: "The kerneltcp maximum usage",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.GetMemory().GetKernelTCP() == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.KernelTCP.Max),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory_kerneltcp_usage",
|
||||
help: "The kerneltcp usage",
|
||||
unit: metrics.Bytes,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.GetMemory().GetKernelTCP() == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Memory.KernelTCP.Usage),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
64
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/metric.go
generated
vendored
Normal file
64
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/metric.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
//go:build linux
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
v1 "github.com/containerd/containerd/v2/core/metrics/types/v1"
|
||||
metrics "github.com/docker/go-metrics"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// IDName is the name that is used to identify the id being collected in the metric
|
||||
var IDName = "container_id"
|
||||
|
||||
type value struct {
|
||||
v float64
|
||||
l []string
|
||||
}
|
||||
|
||||
type metric struct {
|
||||
name string
|
||||
help string
|
||||
unit metrics.Unit
|
||||
vt prometheus.ValueType
|
||||
labels []string
|
||||
// getValues returns the value and labels for the data
|
||||
getValues func(stats *v1.Metrics) []value
|
||||
}
|
||||
|
||||
func (m *metric) desc(ns *metrics.Namespace) *prometheus.Desc {
|
||||
// the namespace label is for containerd namespaces
|
||||
return ns.NewDesc(m.name, m.help, m.unit, append([]string{IDName, "namespace"}, m.labels...)...)
|
||||
}
|
||||
|
||||
func (m *metric) collect(id, namespace string, stats *v1.Metrics, ns *metrics.Namespace, ch chan<- prometheus.Metric, block bool) {
|
||||
values := m.getValues(stats)
|
||||
for _, v := range values {
|
||||
// block signals to block on the sending the metrics so none are missed
|
||||
if block {
|
||||
ch <- prometheus.MustNewConstMetric(m.desc(ns), m.vt, v.v, append([]string{id, namespace}, v.l...)...)
|
||||
continue
|
||||
}
|
||||
// non-blocking metrics can be dropped if the chan is full
|
||||
select {
|
||||
case ch <- prometheus.MustNewConstMetric(m.desc(ns), m.vt, v.v, append([]string{id, namespace}, v.l...)...):
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
208
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/metrics.go
generated
vendored
Normal file
208
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/metrics.go
generated
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
//go:build linux
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
cgroups "github.com/containerd/cgroups/v3/cgroup1"
|
||||
cmetrics "github.com/containerd/containerd/v2/core/metrics"
|
||||
"github.com/containerd/containerd/v2/core/metrics/cgroups/common"
|
||||
v1 "github.com/containerd/containerd/v2/core/metrics/types/v1"
|
||||
"github.com/containerd/containerd/v2/pkg/namespaces"
|
||||
"github.com/containerd/containerd/v2/pkg/timeout"
|
||||
"github.com/containerd/log"
|
||||
"github.com/containerd/typeurl/v2"
|
||||
"github.com/docker/go-metrics"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// Trigger will be called when an event happens and provides the cgroup
|
||||
// where the event originated from
|
||||
type Trigger func(string, string, cgroups.Cgroup)
|
||||
|
||||
// NewCollector registers the collector with the provided namespace and returns it so
|
||||
// that cgroups can be added for collection
|
||||
func NewCollector(ns *metrics.Namespace) *Collector {
|
||||
if ns == nil {
|
||||
return &Collector{}
|
||||
}
|
||||
// add machine cpus and memory info
|
||||
c := &Collector{
|
||||
ns: ns,
|
||||
tasks: make(map[string]entry),
|
||||
}
|
||||
c.metrics = append(c.metrics, pidMetrics...)
|
||||
c.metrics = append(c.metrics, cpuMetrics...)
|
||||
c.metrics = append(c.metrics, memoryMetrics...)
|
||||
c.metrics = append(c.metrics, hugetlbMetrics...)
|
||||
c.metrics = append(c.metrics, blkioMetrics...)
|
||||
c.storedMetrics = make(chan prometheus.Metric, 100*len(c.metrics))
|
||||
ns.Add(c)
|
||||
return c
|
||||
}
|
||||
|
||||
func taskID(id, namespace string) string {
|
||||
return fmt.Sprintf("%s-%s", id, namespace)
|
||||
}
|
||||
|
||||
type entry struct {
|
||||
task common.Statable
|
||||
// ns is an optional child namespace that contains additional to parent labels.
|
||||
// This can be used to append task specific labels to be able to differentiate the different containerd metrics.
|
||||
ns *metrics.Namespace
|
||||
}
|
||||
|
||||
// Collector provides the ability to collect container stats and export
|
||||
// them in the prometheus format
|
||||
type Collector struct {
|
||||
ns *metrics.Namespace
|
||||
storedMetrics chan prometheus.Metric
|
||||
|
||||
// TODO(fuweid):
|
||||
//
|
||||
// The Collector.Collect will be the field ns'Collect's callback,
|
||||
// which be invoked periodically with internal lock. And Collector.Add
|
||||
// might also invoke ns.Lock if the labels is not nil, which is easy to
|
||||
// cause dead-lock.
|
||||
//
|
||||
// Goroutine X:
|
||||
//
|
||||
// ns.Collect
|
||||
// ns.Lock
|
||||
// Collector.Collect
|
||||
// Collector.RLock
|
||||
//
|
||||
//
|
||||
// Goroutine Y:
|
||||
//
|
||||
// Collector.Add
|
||||
// ...(RLock/Lock)
|
||||
// ns.Lock
|
||||
//
|
||||
// I think we should seek the way to decouple ns from Collector.
|
||||
mu sync.RWMutex
|
||||
tasks map[string]entry
|
||||
metrics []*metric
|
||||
}
|
||||
|
||||
// Describe prometheus metrics
|
||||
func (c *Collector) Describe(ch chan<- *prometheus.Desc) {
|
||||
for _, m := range c.metrics {
|
||||
ch <- m.desc(c.ns)
|
||||
}
|
||||
}
|
||||
|
||||
// Collect prometheus metrics
|
||||
func (c *Collector) Collect(ch chan<- prometheus.Metric) {
|
||||
c.mu.RLock()
|
||||
wg := &sync.WaitGroup{}
|
||||
for _, t := range c.tasks {
|
||||
wg.Add(1)
|
||||
go c.collect(t, ch, true, wg)
|
||||
}
|
||||
storedLoop:
|
||||
for {
|
||||
// read stored metrics until the channel is flushed
|
||||
select {
|
||||
case m := <-c.storedMetrics:
|
||||
ch <- m
|
||||
default:
|
||||
break storedLoop
|
||||
}
|
||||
}
|
||||
c.mu.RUnlock()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (c *Collector) collect(entry entry, ch chan<- prometheus.Metric, block bool, wg *sync.WaitGroup) {
|
||||
if wg != nil {
|
||||
defer wg.Done()
|
||||
}
|
||||
|
||||
t := entry.task
|
||||
ctx, cancel := timeout.WithContext(context.Background(), cmetrics.ShimStatsRequestTimeout)
|
||||
stats, err := t.Stats(namespaces.WithNamespace(ctx, t.Namespace()))
|
||||
cancel()
|
||||
|
||||
if err != nil {
|
||||
log.L.WithError(err).Errorf("stat task %s", t.ID())
|
||||
return
|
||||
}
|
||||
|
||||
s := &v1.Metrics{}
|
||||
if err := typeurl.UnmarshalTo(stats, s); err != nil {
|
||||
log.L.WithError(err).Errorf("unmarshal stats for %s", t.ID())
|
||||
return
|
||||
}
|
||||
|
||||
ns := entry.ns
|
||||
if ns == nil {
|
||||
ns = c.ns
|
||||
}
|
||||
for _, m := range c.metrics {
|
||||
m.collect(t.ID(), t.Namespace(), s, ns, ch, block)
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds the provided cgroup and id so that metrics are collected and exported
|
||||
func (c *Collector) Add(t common.Statable, labels map[string]string) error {
|
||||
if c.ns == nil {
|
||||
return nil
|
||||
}
|
||||
c.mu.RLock()
|
||||
id := taskID(t.ID(), t.Namespace())
|
||||
_, ok := c.tasks[id]
|
||||
c.mu.RUnlock()
|
||||
if ok {
|
||||
return nil // requests to collect metrics should be idempotent
|
||||
}
|
||||
|
||||
entry := entry{task: t}
|
||||
if labels != nil {
|
||||
entry.ns = c.ns.WithConstLabels(labels)
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
c.tasks[id] = entry
|
||||
c.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes the provided cgroup by id from the collector
|
||||
func (c *Collector) Remove(t common.Statable) {
|
||||
if c.ns == nil {
|
||||
return
|
||||
}
|
||||
c.mu.Lock()
|
||||
delete(c.tasks, taskID(t.ID(), t.Namespace()))
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// RemoveAll statable items from the collector
|
||||
func (c *Collector) RemoveAll() {
|
||||
if c.ns == nil {
|
||||
return
|
||||
}
|
||||
c.mu.Lock()
|
||||
c.tasks = make(map[string]entry)
|
||||
c.mu.Unlock()
|
||||
}
|
||||
165
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/oom.go
generated
vendored
Normal file
165
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/oom.go
generated
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
//go:build linux
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
cgroups "github.com/containerd/cgroups/v3/cgroup1"
|
||||
"github.com/containerd/containerd/v2/pkg/sys"
|
||||
"github.com/containerd/log"
|
||||
metrics "github.com/docker/go-metrics"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func newOOMCollector(ns *metrics.Namespace) (*oomCollector, error) {
|
||||
fd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var desc *prometheus.Desc
|
||||
if ns != nil {
|
||||
desc = ns.NewDesc("memory_oom", "The number of times a container has received an oom event", metrics.Total, "container_id", "namespace")
|
||||
}
|
||||
c := &oomCollector{
|
||||
fd: fd,
|
||||
desc: desc,
|
||||
set: make(map[uintptr]*oom),
|
||||
}
|
||||
if ns != nil {
|
||||
ns.Add(c)
|
||||
}
|
||||
go c.start()
|
||||
return c, nil
|
||||
}
|
||||
|
||||
type oomCollector struct {
|
||||
mu sync.Mutex
|
||||
|
||||
desc *prometheus.Desc
|
||||
fd int
|
||||
set map[uintptr]*oom
|
||||
}
|
||||
|
||||
type oom struct {
|
||||
// count needs to stay the first member of this struct to ensure 64bits
|
||||
// alignment on a 32bits machine (e.g. arm32). This is necessary as we use
|
||||
// the sync/atomic operations on this field.
|
||||
count atomic.Int64
|
||||
id string
|
||||
namespace string
|
||||
c cgroups.Cgroup
|
||||
triggers []Trigger
|
||||
}
|
||||
|
||||
func (o *oomCollector) Add(id, namespace string, cg cgroups.Cgroup, triggers ...Trigger) error {
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
fd, err := cg.OOMEventFD()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.set[fd] = &oom{
|
||||
id: id,
|
||||
c: cg,
|
||||
triggers: triggers,
|
||||
namespace: namespace,
|
||||
}
|
||||
event := unix.EpollEvent{
|
||||
Fd: int32(fd),
|
||||
Events: unix.EPOLLHUP | unix.EPOLLIN | unix.EPOLLERR,
|
||||
}
|
||||
return unix.EpollCtl(o.fd, unix.EPOLL_CTL_ADD, int(fd), &event)
|
||||
}
|
||||
|
||||
func (o *oomCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
ch <- o.desc
|
||||
}
|
||||
|
||||
func (o *oomCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
for _, t := range o.set {
|
||||
c := t.count.Load()
|
||||
ch <- prometheus.MustNewConstMetric(o.desc, prometheus.CounterValue, float64(c), t.id, t.namespace)
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the epoll fd
|
||||
func (o *oomCollector) Close() error {
|
||||
return unix.Close(o.fd)
|
||||
}
|
||||
|
||||
func (o *oomCollector) start() {
|
||||
var (
|
||||
n int
|
||||
err error
|
||||
events [128]unix.EpollEvent
|
||||
)
|
||||
for {
|
||||
if err := sys.IgnoringEINTR(func() error {
|
||||
n, err = unix.EpollWait(o.fd, events[:], -1)
|
||||
return err
|
||||
}); err != nil {
|
||||
log.L.WithError(err).Error("cgroups: epoll wait failed, OOM notifications disabled")
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
o.process(uintptr(events[i].Fd))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *oomCollector) process(fd uintptr) {
|
||||
// make sure to always flush the eventfd
|
||||
flushEventfd(fd)
|
||||
|
||||
o.mu.Lock()
|
||||
info, ok := o.set[fd]
|
||||
if !ok {
|
||||
o.mu.Unlock()
|
||||
return
|
||||
}
|
||||
o.mu.Unlock()
|
||||
// if we received an event but it was caused by the cgroup being deleted and the fd
|
||||
// being closed make sure we close our copy and remove the container from the set
|
||||
if info.c.State() == cgroups.Deleted {
|
||||
o.mu.Lock()
|
||||
delete(o.set, fd)
|
||||
o.mu.Unlock()
|
||||
unix.Close(int(fd))
|
||||
return
|
||||
}
|
||||
info.count.Add(1)
|
||||
for _, t := range info.triggers {
|
||||
t(info.id, info.namespace, info.c)
|
||||
}
|
||||
}
|
||||
|
||||
func flushEventfd(efd uintptr) error {
|
||||
// Buffer must be >= 8 bytes for eventfd reads
|
||||
// https://man7.org/linux/man-pages/man2/eventfd.2.html
|
||||
var buf [8]byte
|
||||
_, err := unix.Read(int(efd), buf[:])
|
||||
return err
|
||||
}
|
||||
60
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/pids.go
generated
vendored
Normal file
60
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v1/pids.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
//go:build linux
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
v1 "github.com/containerd/containerd/v2/core/metrics/types/v1"
|
||||
metrics "github.com/docker/go-metrics"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var pidMetrics = []*metric{
|
||||
{
|
||||
name: "pids",
|
||||
help: "The limit to the number of pids allowed",
|
||||
unit: metrics.Unit("limit"),
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Pids == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Pids.Limit),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "pids",
|
||||
help: "The current number of pids",
|
||||
unit: metrics.Unit("current"),
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v1.Metrics) []value {
|
||||
if stats.Pids == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.Pids.Current),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
55
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v2/cgroups.go
generated
vendored
Normal file
55
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v2/cgroups.go
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
//go:build linux
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/events"
|
||||
"github.com/containerd/containerd/v2/core/runtime"
|
||||
"github.com/docker/go-metrics"
|
||||
)
|
||||
|
||||
// NewTaskMonitor returns a new cgroups monitor
|
||||
func NewTaskMonitor(ctx context.Context, publisher events.Publisher, ns *metrics.Namespace) (runtime.TaskMonitor, error) {
|
||||
collector := NewCollector(ns)
|
||||
return &cgroupsMonitor{
|
||||
collector: collector,
|
||||
context: ctx,
|
||||
publisher: publisher,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type cgroupsMonitor struct {
|
||||
collector *Collector
|
||||
context context.Context
|
||||
publisher events.Publisher
|
||||
}
|
||||
|
||||
func (m *cgroupsMonitor) Monitor(c runtime.Task, labels map[string]string) error {
|
||||
if err := m.collector.Add(c, labels); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *cgroupsMonitor) Stop(c runtime.Task) error {
|
||||
m.collector.Remove(c)
|
||||
return nil
|
||||
}
|
||||
124
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v2/cpu.go
generated
vendored
Normal file
124
vendor/github.com/containerd/containerd/v2/core/metrics/cgroups/v2/cpu.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
//go:build linux
|
||||
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
v2 "github.com/containerd/containerd/v2/core/metrics/types/v2"
|
||||
metrics "github.com/docker/go-metrics"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var cpuMetrics = []*metric{
|
||||
{
|
||||
name: "cpu_usage_usec",
|
||||
help: "Total cpu usage (cgroup v2)",
|
||||
unit: metrics.Unit("microseconds"),
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v2.Metrics) []value {
|
||||
if stats.CPU == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.CPU.UsageUsec),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cpu_user_usec",
|
||||
help: "Current cpu usage in user space (cgroup v2)",
|
||||
unit: metrics.Unit("microseconds"),
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v2.Metrics) []value {
|
||||
if stats.CPU == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.CPU.UserUsec),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cpu_system_usec",
|
||||
help: "Current cpu usage in kernel space (cgroup v2)",
|
||||
unit: metrics.Unit("microseconds"),
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v2.Metrics) []value {
|
||||
if stats.CPU == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.CPU.SystemUsec),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cpu_nr_periods",
|
||||
help: "Current cpu number of periods (only if controller is enabled)",
|
||||
unit: metrics.Total,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v2.Metrics) []value {
|
||||
if stats.CPU == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.CPU.NrPeriods),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cpu_nr_throttled",
|
||||
help: "Total number of times tasks have been throttled (only if controller is enabled)",
|
||||
unit: metrics.Total,
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v2.Metrics) []value {
|
||||
if stats.CPU == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.CPU.NrThrottled),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cpu_throttled_usec",
|
||||
help: "Total time duration for which tasks have been throttled. (only if controller is enabled)",
|
||||
unit: metrics.Unit("microseconds"),
|
||||
vt: prometheus.GaugeValue,
|
||||
getValues: func(stats *v2.Metrics) []value {
|
||||
if stats.CPU == nil {
|
||||
return nil
|
||||
}
|
||||
return []value{
|
||||
{
|
||||
v: float64(stats.CPU.ThrottledUsec),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user