From f9beac3db8e9345b9acfc1cbe5126ff0c7e8c9eb Mon Sep 17 00:00:00 2001 From: TinaMor Date: Tue, 2 Jul 2024 16:11:16 +0300 Subject: [PATCH 01/27] Set stderr to empty string when using terminal on Windows. Windows HCSShim requires that stderr is an empty string when using terminal. Reference: https://github.com/microsoft/hcsshim/blob/200feabd854da69f615a598ed6a1263ce9531676/cmd/containerd-shim-runhcs-v1/service_internal.go#L127 Signed-off-by: Christine Murimi (cherry picked from commit eaa1afe63477b6404c1603377586758b1fe564c2) --- cio/io.go | 20 -------------------- cio/io_unix.go | 35 +++++++++++++++++++++++++++++++++++ cio/io_windows.go | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 20 deletions(-) diff --git a/cio/io.go b/cio/io.go index 11724f8d88..8bd14c89c7 100644 --- a/cio/io.go +++ b/cio/io.go @@ -268,26 +268,6 @@ func BinaryIO(binary string, args map[string]string) Creator { } } -// TerminalBinaryIO forwards container STDOUT|STDERR directly to a logging binary -// It also sets the terminal option to true -func TerminalBinaryIO(binary string, args map[string]string) Creator { - return func(_ string) (IO, error) { - uri, err := LogURIGenerator("binary", binary, args) - if err != nil { - return nil, err - } - - res := uri.String() - return &logURI{ - config: Config{ - Stdout: res, - Stderr: res, - Terminal: true, - }, - }, nil - } -} - // LogFile creates a file on disk that logs the task's STDOUT,STDERR. // If the log file already exists, the logs will be appended to the file. func LogFile(path string) Creator { diff --git a/cio/io_unix.go b/cio/io_unix.go index 9dc21dcc88..7038883cec 100644 --- a/cio/io_unix.go +++ b/cio/io_unix.go @@ -22,6 +22,7 @@ import ( "context" "fmt" "io" + "net/url" "os" "path/filepath" "sync" @@ -158,3 +159,37 @@ func NewDirectIO(ctx context.Context, fifos *FIFOSet) (*DirectIO, error) { }, }, err } + +// TerminalLogURI provides the raw logging URI +// as well as sets the terminal option to true. +func TerminalLogURI(uri *url.URL) Creator { + return func(_ string) (IO, error) { + return &logURI{ + config: Config{ + Stdout: uri.String(), + Stderr: uri.String(), + Terminal: true, + }, + }, nil + } +} + +// TerminalBinaryIO forwards container STDOUT|STDERR directly to a logging binary +// It also sets the terminal option to true +func TerminalBinaryIO(binary string, args map[string]string) Creator { + return func(_ string) (IO, error) { + uri, err := LogURIGenerator("binary", binary, args) + if err != nil { + return nil, err + } + + res := uri.String() + return &logURI{ + config: Config{ + Stdout: res, + Stderr: res, + Terminal: true, + }, + }, nil + } +} diff --git a/cio/io_windows.go b/cio/io_windows.go index 59b14c42ff..d67c4b30b3 100644 --- a/cio/io_windows.go +++ b/cio/io_windows.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "io" + "net/url" winio "github.com/Microsoft/go-winio" "github.com/containerd/log" @@ -155,3 +156,42 @@ func NewDirectIOFromFIFOSet(ctx context.Context, stdin io.WriteCloser, stdout, s }, } } + +// TerminalLogURI provides the raw logging URI +// as well as sets the terminal option to true. +func TerminalLogURI(uri *url.URL) Creator { + return func(_ string) (IO, error) { + return &logURI{ + config: Config{ + Terminal: true, + Stdout: uri.String(), + + // Windows HCSShim requires that stderr is an empty string when using terminal. + // https://github.com/microsoft/hcsshim/blob/200feabd854da69f615a598ed6a1263ce9531676/cmd/containerd-shim-runhcs-v1/service_internal.go#L127 + Stderr: "", + }, + }, nil + } +} + +// TerminalBinaryIO forwards container STDOUT|STDERR directly to a logging binary +// It also sets the terminal option to true +func TerminalBinaryIO(binary string, args map[string]string) Creator { + return func(_ string) (IO, error) { + uri, err := LogURIGenerator("binary", binary, args) + if err != nil { + return nil, err + } + + return &logURI{ + config: Config{ + Terminal: true, + Stdout: uri.String(), + + // Windows HCSShim requires that stderr is an empty string when using terminal. + // https://github.com/microsoft/hcsshim/blob/200feabd854da69f615a598ed6a1263ce9531676/cmd/containerd-shim-runhcs-v1/service_internal.go#L127 + Stderr: "", + }, + }, nil + } +} From b140792e46e962d8a5c6aafaf0012cf572bdacb3 Mon Sep 17 00:00:00 2001 From: Samuel Karp Date: Wed, 24 Jul 2024 16:30:44 -0700 Subject: [PATCH 02/27] introspection: regenerate UUID if state is empty The /var/lib/containerd/io.containerd.grpc.v1.introspection/uuid file stores a UUID to identify the particular containerd daemon responding to requests. The file should either exist with a UUID, or not exist. However, it has been observed that the file can be truncated with 0 bytes, which will then fail to be parsed as a valid UUID. As a defensive practice, detect a 0-length file and overwrite with a new UUID rather than failing. Fixes: https://github.com/containerd/containerd/issues/10491 Signed-off-by: Samuel Karp --- services/introspection/local.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/introspection/local.go b/services/introspection/local.go index 757408bbde..8c992047dc 100644 --- a/services/introspection/local.go +++ b/services/introspection/local.go @@ -163,6 +163,9 @@ func (l *Local) getUUID() (string, error) { } return "", err } + if len(data) == 0 { + return l.generateUUID() + } u := string(data) if _, err := uuid.Parse(u); err != nil { return "", err From 9afb8dcdfecfdc37297ab110548214e4954bb2ab Mon Sep 17 00:00:00 2001 From: Samuel Karp Date: Thu, 25 Jul 2024 14:25:39 -0700 Subject: [PATCH 03/27] deprecation: update warnings for CRI config fields Some CRI config properties had removal postponed until v2.1 in https://github.com/containerd/containerd/pull/9966. Update the associated deprecation warnings to match the new removal version. Signed-off-by: Samuel Karp (cherry picked from commit f9146c0516c7cd92814cfded33958f048b950c0e) Signed-off-by: Samuel Karp --- pkg/deprecation/deprecation.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/deprecation/deprecation.go b/pkg/deprecation/deprecation.go index 06fda0638c..813a0da3ae 100644 --- a/pkg/deprecation/deprecation.go +++ b/pkg/deprecation/deprecation.go @@ -73,11 +73,11 @@ var messages = map[Warning]string{ "Use a v2 runtime and `options` instead.", CRIRuntimeRoot: "The `runtime_root` property of [plugins.\"io.containerd.grpc.v1.cri\".containerd.runtimes.*] is deprecated since containerd v1.3 and will be removed in containerd v2.0. " + "Use a v2 runtime and `options.Root` instead.", - CRIRegistryMirrors: "The `mirrors` property of `[plugins.\"io.containerd.grpc.v1.cri\".registry]` is deprecated since containerd v1.5 and will be removed in containerd v2.0. " + + CRIRegistryMirrors: "The `mirrors` property of `[plugins.\"io.containerd.grpc.v1.cri\".registry]` is deprecated since containerd v1.5 and will be removed in containerd v2.1. " + "Use `config_path` instead.", - CRIRegistryAuths: "The `auths` property of `[plugins.\"io.containerd.grpc.v1.cri\".registry]` is deprecated since containerd v1.3 and will be removed in containerd v2.0. " + + CRIRegistryAuths: "The `auths` property of `[plugins.\"io.containerd.grpc.v1.cri\".registry]` is deprecated since containerd v1.3 and will be removed in containerd v2.1. " + "Use `ImagePullSecrets` instead.", - CRIRegistryConfigs: "The `configs` property of `[plugins.\"io.containerd.grpc.v1.cri\".registry]` is deprecated since containerd v1.5 and will be removed in containerd v2.0. " + + CRIRegistryConfigs: "The `configs` property of `[plugins.\"io.containerd.grpc.v1.cri\".registry]` is deprecated since containerd v1.5 and will be removed in containerd v2.1. " + "Use `config_path` instead.", CRIAPIV1Alpha2: "CRI API v1alpha2 is deprecated since containerd v1.7 and removed in containerd v2.0. Use CRI API v1 instead.", AUFSSnapshotter: "The aufs snapshotter is deprecated since containerd v1.5 and removed in containerd v2.0. Use the overlay snapshotter instead.", From 0db46f664ab1394add6c813b764121a5f12d6ef3 Mon Sep 17 00:00:00 2001 From: Iceber Gu Date: Mon, 8 Jul 2024 17:21:50 +0800 Subject: [PATCH 04/27] client: fix tasks with PID 0 cannot be forced to delete Signed-off-by: Iceber Gu --- task_opts.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/task_opts.go b/task_opts.go index 7bcd8a4e6a..3774d6592d 100644 --- a/task_opts.go +++ b/task_opts.go @@ -158,6 +158,12 @@ type ProcessDeleteOpts func(context.Context, Process) error // WithProcessKill will forcefully kill and delete a process func WithProcessKill(ctx context.Context, p Process) error { + // Skip killing tasks with PID 0 + // https://github.com/containerd/containerd/issues/10441 + if p.Pid() == 0 { + return nil + } + ctx, cancel := context.WithCancel(ctx) defer cancel() // ignore errors to wait and kill as we are forcefully killing From b3b6f15075e1ecd07ca49d667e6dedd94bf4145a Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Tue, 30 Jul 2024 10:40:14 +0200 Subject: [PATCH 05/27] Make `StopPodSandbox` RPC idempotent Similar to sandbox removal, the stop of a sandbox should be a noop if the sandbox has not been found. Found during: https://github.com/kubernetes-sigs/cri-tools/pull/1535 Signed-off-by: Sascha Grunert (cherry picked from commit c6cea95d95a66afe14c465adc7c43f03565bf7ea) Signed-off-by: Sascha Grunert --- pkg/cri/sbserver/sandbox_stop.go | 12 ++++++++++-- pkg/cri/server/sandbox_stop.go | 11 +++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/pkg/cri/sbserver/sandbox_stop.go b/pkg/cri/sbserver/sandbox_stop.go index 3346364cf0..930eb31652 100644 --- a/pkg/cri/sbserver/sandbox_stop.go +++ b/pkg/cri/sbserver/sandbox_stop.go @@ -22,6 +22,7 @@ import ( "fmt" "time" + "github.com/containerd/errdefs" "github.com/containerd/log" runtime "k8s.io/cri-api/pkg/apis/runtime/v1" @@ -33,8 +34,15 @@ import ( func (c *criService) StopPodSandbox(ctx context.Context, r *runtime.StopPodSandboxRequest) (*runtime.StopPodSandboxResponse, error) { sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId()) if err != nil { - return nil, fmt.Errorf("an error occurred when try to find sandbox %q: %w", - r.GetPodSandboxId(), err) + if !errdefs.IsNotFound(err) { + return nil, fmt.Errorf("an error occurred when try to find sandbox %q: %w", + r.GetPodSandboxId(), err) + } + + // The StopPodSandbox RPC is idempotent, and must not return an error + // if all relevant resources have already been reclaimed. Ref: + // https://github.com/kubernetes/cri-api/blob/c20fa40/pkg/apis/runtime/v1/api.proto#L45-L46 + return &runtime.StopPodSandboxResponse{}, nil } if err := c.stopPodSandbox(ctx, sandbox); err != nil { diff --git a/pkg/cri/server/sandbox_stop.go b/pkg/cri/server/sandbox_stop.go index 9a753edc54..893ce90c5f 100644 --- a/pkg/cri/server/sandbox_stop.go +++ b/pkg/cri/server/sandbox_stop.go @@ -38,8 +38,15 @@ import ( func (c *criService) StopPodSandbox(ctx context.Context, r *runtime.StopPodSandboxRequest) (*runtime.StopPodSandboxResponse, error) { sandbox, err := c.sandboxStore.Get(r.GetPodSandboxId()) if err != nil { - return nil, fmt.Errorf("an error occurred when try to find sandbox %q: %w", - r.GetPodSandboxId(), err) + if !errdefs.IsNotFound(err) { + return nil, fmt.Errorf("an error occurred when try to find sandbox %q: %w", + r.GetPodSandboxId(), err) + } + + // The StopPodSandbox RPC is idempotent, and must not return an error + // if all relevant resources have already been reclaimed. Ref: + // https://github.com/kubernetes/cri-api/blob/c20fa40/pkg/apis/runtime/v1/api.proto#L45-L46 + return &runtime.StopPodSandboxResponse{}, nil } if err := c.stopPodSandbox(ctx, sandbox); err != nil { From 6da4e40b22ce2beb3cbb88dcdb8c7ede279e5b14 Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Tue, 30 Jul 2024 11:47:22 +0200 Subject: [PATCH 06/27] Make `StopContainer` RPC idempotent Similar to container removal, the stop of a container should be a noop if the container has not been found. Found during: https://github.com/kubernetes-sigs/cri-tools/pull/1536 Signed-off-by: Sascha Grunert (cherry picked from commit a97b11898a195251e0852c94bce6a0189df51ba5) Signed-off-by: Sascha Grunert --- pkg/cri/sbserver/container_stop.go | 9 ++++++++- pkg/cri/server/container_stop.go | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/pkg/cri/sbserver/container_stop.go b/pkg/cri/sbserver/container_stop.go index f317f05181..c59a9f53c4 100644 --- a/pkg/cri/sbserver/container_stop.go +++ b/pkg/cri/sbserver/container_stop.go @@ -40,7 +40,14 @@ func (c *criService) StopContainer(ctx context.Context, r *runtime.StopContainer // Get container config from container store. container, err := c.containerStore.Get(r.GetContainerId()) if err != nil { - return nil, fmt.Errorf("an error occurred when try to find container %q: %w", r.GetContainerId(), err) + if !errdefs.IsNotFound(err) { + return nil, fmt.Errorf("an error occurred when try to find container %q: %w", r.GetContainerId(), err) + } + + // The StopContainer RPC is idempotent, and must not return an error if + // the container has already been stopped. Ref: + // https://github.com/kubernetes/cri-api/blob/c20fa40/pkg/apis/runtime/v1/api.proto#L67-L68 + return &runtime.StopContainerResponse{}, nil } if err := c.stopContainer(ctx, container, time.Duration(r.GetTimeout())*time.Second); err != nil { diff --git a/pkg/cri/server/container_stop.go b/pkg/cri/server/container_stop.go index 96b3909894..72a1b63779 100644 --- a/pkg/cri/server/container_stop.go +++ b/pkg/cri/server/container_stop.go @@ -40,7 +40,14 @@ func (c *criService) StopContainer(ctx context.Context, r *runtime.StopContainer // Get container config from container store. container, err := c.containerStore.Get(r.GetContainerId()) if err != nil { - return nil, fmt.Errorf("an error occurred when try to find container %q: %w", r.GetContainerId(), err) + if !errdefs.IsNotFound(err) { + return nil, fmt.Errorf("an error occurred when try to find container %q: %w", r.GetContainerId(), err) + } + + // The StopContainer RPC is idempotent, and must not return an error if + // the container has already been stopped. Ref: + // https://github.com/kubernetes/cri-api/blob/c20fa40/pkg/apis/runtime/v1/api.proto#L67-L68 + return &runtime.StopContainerResponse{}, nil } if err := c.stopContainer(ctx, container, time.Duration(r.GetTimeout())*time.Second); err != nil { From 16c5fc7689d8c9d715b2387c2844b22bb7a8e76e Mon Sep 17 00:00:00 2001 From: Erikson Tung Date: Tue, 30 Jul 2024 14:18:33 -0700 Subject: [PATCH 07/27] Ensure /run/containerd is created with correct perms There are a couple directories that get created under the default state directory ("/run/containerd") even when containerd is configured to use a different location for its state directory. Create the default state directory even if containerd is configured to use a different state directory location. This ensure pkg/shim and pkg/fifo won't create the default state directory with incorrect permissions when calling os.MkdirAll for their respective subdirectories. Signed-off-by: Erikson Tung --- services/server/server.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/services/server/server.go b/services/server/server.go index 1ede2d67a4..b31f39afa4 100644 --- a/services/server/server.go +++ b/services/server/server.go @@ -84,6 +84,15 @@ func CreateTopLevelDirectories(config *srvconfig.Config) error { if err := sys.MkdirAllWithACL(config.State, 0711); err != nil { return err } + if config.State != defaults.DefaultStateDir { + // XXX: socketRoot in pkg/shim is hard-coded to the default state directory. + // See https://github.com/containerd/containerd/issues/10502#issuecomment-2249268582 for why it's set up that way. + // The default fifo directory in pkg/cio is also configured separately and defaults to the default state directory instead of the configured state directory. + // Make sure the default state directory is created with the correct permissions. + if err := sys.MkdirAllWithACL(defaults.DefaultStateDir, 0o711); err != nil { + return err + } + } if config.TempDir != "" { if err := sys.MkdirAllWithACL(config.TempDir, 0711); err != nil { From 7bb1455d88aef5c558125c8c3b08230dc78fbdcb Mon Sep 17 00:00:00 2001 From: "rongfu.leng" Date: Tue, 30 May 2023 23:05:48 +0800 Subject: [PATCH 08/27] update auths code comment Signed-off-by: rongfu.leng --- pkg/cri/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cri/config/config.go b/pkg/cri/config/config.go index 4aed6efbcb..2200e2778f 100644 --- a/pkg/cri/config/config.go +++ b/pkg/cri/config/config.go @@ -239,7 +239,7 @@ type Registry struct { Configs map[string]RegistryConfig `toml:"configs" json:"configs"` // Auths are registry endpoint to auth config mapping. The registry endpoint must // be a valid url with host specified. - // DEPRECATED: Use ConfigPath instead. Remove in containerd 1.6. + // DEPRECATED: Use ConfigPath instead. Remove in containerd 2.0, supported in 1.x releases. Auths map[string]AuthConfig `toml:"auths" json:"auths"` // Headers adds additional HTTP headers that get sent to all registries Headers map[string][]string `toml:"headers" json:"headers"` From 3ff82ba0f007e0fb856f7b2b174f5bc1ab1237cd Mon Sep 17 00:00:00 2001 From: Mauri de Souza Meneguzzo Date: Sat, 15 Jun 2024 20:15:20 -0300 Subject: [PATCH 09/27] Fix TestNewBinaryIOCleanup failing with gotip This PR ignores a new pidfd file descriptor that is introduced in gotip (future 1.23) and should not be considered when detecting fd leaks. Fixes #10345 Signed-off-by: Mauri de Souza Meneguzzo (cherry picked from commit f0aecaa2e2a28e3950a2afac1c6b45b2d9543462) Signed-off-by: Shengjing Zhu --- pkg/process/io_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pkg/process/io_test.go b/pkg/process/io_test.go index 90072f7ccd..471bef9c26 100644 --- a/pkg/process/io_test.go +++ b/pkg/process/io_test.go @@ -20,8 +20,10 @@ package process import ( "context" + "fmt" "net/url" "os" + "strings" "testing" "github.com/containerd/containerd/namespaces" @@ -68,5 +70,28 @@ func TestNewBinaryIOCleanup(t *testing.T) { func descriptorCount(t *testing.T) int { t.Helper() files, _ := os.ReadDir("/proc/self/fd") + + // Go 1.23 introduced a new internal file descriptor type "pidfd" + // that we don't want to count towards the total file descriptors in + // use by the process. This retains the behavior of previous Go + // versions. + // See https://go.dev/issues/62654. + // + // Once the proposal to check for internal file descriptors is + // accepted, we can use that instead to detect internal fds in use + // by the Go runtime. + // See https://go.dev/issues/67639. + for i, file := range files { + sym, err := os.Readlink(fmt.Sprintf("/proc/self/fd/%s", file.Name())) + if err != nil { + // ignore fds that cannot be followed. + continue + } + + if strings.HasPrefix(sym, "pidfd:") { + files = append(files[:i], files[i+1:]...) + } + } + return len(files) } From d51143f6fad370fce2c2f5b0507365fb0a229372 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 6 Aug 2024 14:15:27 +0200 Subject: [PATCH 10/27] script/setup/install-runc: fix runc using incorrect version runc v1.1.13 introduced an option to customize the version (as printed by the `--version` flag) through a `VERSION` Make variable / environment variable (see [1]). This variable collided with the `VERSION` environment variable used by containerd for the same purpose, which lead to `runc` binaries built using the version of containerd; runc --version runc version 1.7.20 commit: v1.1.13-0-g58aa9203 ... This patch unsets the `VERSION` variable to bring prevent it from being inherited and to bring back the previous behavior. Before this patch: docker build -t containerd-test -f contrib/Dockerfile.test . docker run -it --rm --env VERSION=1.7.20 containerd-test sh -c 'script/setup/install-runc && /usr/local/sbin/runc --version' # .... HEAD is now at 58aa9203 VERSION: release 1.1.13 go build -trimpath "-buildmode=pie" -tags "seccomp" -ldflags "-X main.gitCommit=v1.1.13-0-g58aa9203 -X main.version=1.7.20 " -o runc . install -D -m0755 runc /usr/local/sbin/runc /go/src/github.com/containerd/containerd runc version 1.7.20 commit: v1.1.13-0-g58aa9203 spec: 1.0.2-dev go: go1.22.5 libseccomp: 2.5.4 With this patch: docker build -t containerd-test -f contrib/Dockerfile.test . docker run -it --rm --env VERSION=1.7.20 containerd-test sh -c 'script/setup/install-runc && /usr/local/sbin/runc --version' # .... HEAD is now at 58aa9203 VERSION: release 1.1.13 go build -trimpath "-buildmode=pie" -tags "seccomp" -ldflags "-X main.gitCommit=v1.1.13-0-g58aa9203 -X main.version=v1.1.13 " -o runc . install -D -m0755 runc /usr/local/sbin/runc /go/src/github.com/containerd/containerd runc version v1.1.13 commit: v1.1.13-0-g58aa9203 spec: 1.0.2-dev go: go1.22.5 libseccomp: 2.5.4 [1]: https://github.com/opencontainers/runc/commit/6f4d975c402d7848f5097f53c18000aa42581def Signed-off-by: Sebastiaan van Stijn (cherry picked from commit 349d2b5c151fc5fda830b76a42767fb869fffe02) Signed-off-by: Sebastiaan van Stijn --- script/setup/install-runc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/setup/install-runc b/script/setup/install-runc index 07a3be7c8f..67743d750b 100755 --- a/script/setup/install-runc +++ b/script/setup/install-runc @@ -37,7 +37,7 @@ function install_runc() { git clone "${RUNC_REPO}" "${TMPROOT}"/runc pushd "${TMPROOT}"/runc git checkout "${RUNC_VERSION}" - make BUILDTAGS='seccomp' runc + env -u VERSION make BUILDTAGS='seccomp' runc $SUDO make install popd rm -fR "${TMPROOT}" From f21675c2731774c54412b3855a9eb43a757f69c4 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 25 Jul 2024 11:12:27 +0200 Subject: [PATCH 11/27] vendor: github.com/moby/sys/user v0.2.0 full diff: https://github.com/moby/sys/compare/user/v0.1.0...user/v0.2.0 Signed-off-by: Sebastiaan van Stijn (cherry picked from commit 40a315b95c78b2e910a95450adfa25d074ae0c22) Signed-off-by: Sebastiaan van Stijn --- go.mod | 2 +- go.sum | 4 ++-- integration/client/go.mod | 2 +- integration/client/go.sum | 4 ++-- vendor/github.com/moby/sys/user/user.go | 1 - vendor/modules.txt | 4 ++-- 6 files changed, 8 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 2072f710f0..084a39fbf8 100644 --- a/go.mod +++ b/go.mod @@ -48,7 +48,7 @@ require ( github.com/moby/sys/sequential v0.5.0 github.com/moby/sys/signal v0.7.0 github.com/moby/sys/symlink v0.2.0 - github.com/moby/sys/user v0.1.0 + github.com/moby/sys/user v0.2.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0 github.com/opencontainers/runtime-spec v1.1.0 diff --git a/go.sum b/go.sum index 92f221988a..632a8ed7f5 100644 --- a/go.sum +++ b/go.sum @@ -545,8 +545,8 @@ github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= github.com/moby/sys/symlink v0.2.0 h1:tk1rOM+Ljp0nFmfOIBtlV3rTDlWOwFRhjEeAhZB0nZc= github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= -github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= -github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/sys/user v0.2.0 h1:OnpapJsRp25vkhw8TFG6OLJODNh/3rEwRWtJ3kakwRM= +github.com/moby/sys/user v0.2.0/go.mod h1:RYstrcWOJpVh+6qzUqp2bU3eaRpdiQeKGlKitaH0PM8= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= diff --git a/integration/client/go.mod b/integration/client/go.mod index 303a50c66f..5d4c14442a 100644 --- a/integration/client/go.mod +++ b/integration/client/go.mod @@ -52,7 +52,7 @@ require ( github.com/moby/sys/mountinfo v0.6.2 // indirect github.com/moby/sys/sequential v0.5.0 // indirect github.com/moby/sys/signal v0.7.0 // indirect - github.com/moby/sys/user v0.1.0 // indirect + github.com/moby/sys/user v0.2.0 // indirect github.com/opencontainers/selinux v1.11.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pkg/errors v0.9.1 // indirect diff --git a/integration/client/go.sum b/integration/client/go.sum index e3e76a1b90..79af20d5b8 100644 --- a/integration/client/go.sum +++ b/integration/client/go.sum @@ -1820,8 +1820,8 @@ github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI= github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= -github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= -github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/sys/user v0.2.0 h1:OnpapJsRp25vkhw8TFG6OLJODNh/3rEwRWtJ3kakwRM= +github.com/moby/sys/user v0.2.0/go.mod h1:RYstrcWOJpVh+6qzUqp2bU3eaRpdiQeKGlKitaH0PM8= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/vendor/github.com/moby/sys/user/user.go b/vendor/github.com/moby/sys/user/user.go index 984466d1ab..198c493679 100644 --- a/vendor/github.com/moby/sys/user/user.go +++ b/vendor/github.com/moby/sys/user/user.go @@ -197,7 +197,6 @@ func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) { for { var line []byte line, isPrefix, err = rd.ReadLine() - if err != nil { // We should return no error if EOF is reached // without a match. diff --git a/vendor/modules.txt b/vendor/modules.txt index df74d5c1cd..2a0cae9d33 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -366,8 +366,8 @@ github.com/moby/sys/signal # github.com/moby/sys/symlink v0.2.0 ## explicit; go 1.16 github.com/moby/sys/symlink -# github.com/moby/sys/user v0.1.0 -## explicit; go 1.17 +# github.com/moby/sys/user v0.2.0 +## explicit; go 1.21 github.com/moby/sys/user # github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd ## explicit From 65f7d07409562252aaab84da481ca4e9334a5810 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 16 Jul 2024 21:50:24 +0200 Subject: [PATCH 12/27] pkg/userns: deprecate and migrate to github.com/moby/sys/user/userns The userns package in libcontainer was integrated into the moby/sys/user module at commit [3778ae603c706494fd1e2c2faf83b406e38d687d][1]. This patch deprecates the containerd fork of that package, and adds it as an alias for the moby/sys/user/userns package. [1]: https://github.com/opencontainers/runc/commit/3778ae603c706494fd1e2c2faf83b406e38d687d Signed-off-by: Sebastiaan van Stijn (cherry picked from commit 8437c567d89bae1154c4ff926bf96bac39b8f30f) Signed-off-by: Sebastiaan van Stijn --- archive/tar.go | 3 +- archive/tar_unix.go | 5 +- diff/apply/apply_linux.go | 6 +- mount/mount_linux.go | 2 +- oci/utils_unix.go | 2 +- oci/utils_unix_test.go | 3 +- .../sbserver/podsandbox/sandbox_run_linux.go | 2 +- pkg/cri/sbserver/service_linux.go | 2 +- pkg/cri/server/sandbox_run_linux.go | 2 +- pkg/cri/server/service_linux.go | 2 +- ...ns_unsupported.go => userns_deprecated.go} | 14 +++-- pkg/userns/userns_linux.go | 62 ------------------- runtime/v2/runc/task/service.go | 3 +- snapshots/overlay/overlayutils/check.go | 3 +- sys/oom_linux.go | 2 +- sys/oom_linux_test.go | 2 +- .../github.com/moby/sys/user/userns/userns.go | 16 +++++ .../moby/sys/user/userns/userns_linux.go | 53 ++++++++++++++++ .../sys/user/userns/userns_linux_fuzzer.go | 8 +++ .../sys/user/userns/userns_unsupported.go | 6 ++ vendor/modules.txt | 1 + 21 files changed, 114 insertions(+), 85 deletions(-) rename pkg/userns/{userns_unsupported.go => userns_deprecated.go} (66%) delete mode 100644 pkg/userns/userns_linux.go create mode 100644 vendor/github.com/moby/sys/user/userns/userns.go create mode 100644 vendor/github.com/moby/sys/user/userns/userns_linux.go create mode 100644 vendor/github.com/moby/sys/user/userns/userns_linux_fuzzer.go create mode 100644 vendor/github.com/moby/sys/user/userns/userns_unsupported.go diff --git a/archive/tar.go b/archive/tar.go index cdc7e14bfd..aadb694f8d 100644 --- a/archive/tar.go +++ b/archive/tar.go @@ -29,9 +29,10 @@ import ( "syscall" "time" + "github.com/moby/sys/user/userns" + "github.com/containerd/containerd/archive/tarheader" "github.com/containerd/containerd/pkg/epoch" - "github.com/containerd/containerd/pkg/userns" "github.com/containerd/continuity/fs" "github.com/containerd/log" ) diff --git a/archive/tar_unix.go b/archive/tar_unix.go index 8e883eee62..866a368242 100644 --- a/archive/tar_unix.go +++ b/archive/tar_unix.go @@ -27,10 +27,11 @@ import ( "strings" "syscall" - "github.com/containerd/containerd/pkg/userns" + "github.com/moby/sys/user/userns" + "golang.org/x/sys/unix" + "github.com/containerd/continuity/fs" "github.com/containerd/continuity/sysx" - "golang.org/x/sys/unix" ) func chmodTarEntry(perm os.FileMode) os.FileMode { diff --git a/diff/apply/apply_linux.go b/diff/apply/apply_linux.go index ee30a1d1c7..3d93f38451 100644 --- a/diff/apply/apply_linux.go +++ b/diff/apply/apply_linux.go @@ -23,12 +23,12 @@ import ( "os" "strings" + "github.com/moby/sys/user/userns" + "golang.org/x/sys/unix" + "github.com/containerd/containerd/archive" "github.com/containerd/containerd/mount" - "github.com/containerd/containerd/pkg/userns" "github.com/containerd/errdefs" - - "golang.org/x/sys/unix" ) func apply(ctx context.Context, mounts []mount.Mount, r io.Reader, sync bool) (retErr error) { diff --git a/mount/mount_linux.go b/mount/mount_linux.go index 34aa639bb6..6c353e9a2a 100644 --- a/mount/mount_linux.go +++ b/mount/mount_linux.go @@ -26,7 +26,7 @@ import ( "strings" "time" - "github.com/containerd/containerd/pkg/userns" + "github.com/moby/sys/user/userns" "golang.org/x/sys/unix" ) diff --git a/oci/utils_unix.go b/oci/utils_unix.go index b3cb8a600d..f954df7976 100644 --- a/oci/utils_unix.go +++ b/oci/utils_unix.go @@ -24,7 +24,7 @@ import ( "os" "path/filepath" - "github.com/containerd/containerd/pkg/userns" + "github.com/moby/sys/user/userns" specs "github.com/opencontainers/runtime-spec/specs-go" "golang.org/x/sys/unix" ) diff --git a/oci/utils_unix_test.go b/oci/utils_unix_test.go index 18d4e944ec..da4838b391 100644 --- a/oci/utils_unix_test.go +++ b/oci/utils_unix_test.go @@ -25,9 +25,8 @@ import ( "runtime" "testing" + "github.com/moby/sys/user/userns" "github.com/stretchr/testify/assert" - - "github.com/containerd/containerd/pkg/userns" ) func cleanupTest() { diff --git a/pkg/cri/sbserver/podsandbox/sandbox_run_linux.go b/pkg/cri/sbserver/podsandbox/sandbox_run_linux.go index 9b3077208c..a1a26d63e3 100644 --- a/pkg/cri/sbserver/podsandbox/sandbox_run_linux.go +++ b/pkg/cri/sbserver/podsandbox/sandbox_run_linux.go @@ -25,6 +25,7 @@ import ( "github.com/containerd/containerd" "github.com/containerd/containerd/oci" "github.com/containerd/containerd/plugin" + "github.com/moby/sys/user/userns" imagespec "github.com/opencontainers/image-spec/specs-go/v1" runtimespec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux" @@ -33,7 +34,6 @@ import ( "github.com/containerd/containerd/pkg/cri/annotations" customopts "github.com/containerd/containerd/pkg/cri/opts" - "github.com/containerd/containerd/pkg/userns" ) func (c *Controller) sandboxContainerSpec(id string, config *runtime.PodSandboxConfig, diff --git a/pkg/cri/sbserver/service_linux.go b/pkg/cri/sbserver/service_linux.go index 205d46814c..a094b28228 100644 --- a/pkg/cri/sbserver/service_linux.go +++ b/pkg/cri/sbserver/service_linux.go @@ -20,8 +20,8 @@ import ( "fmt" "github.com/containerd/containerd/pkg/cap" - "github.com/containerd/containerd/pkg/userns" "github.com/containerd/go-cni" + "github.com/moby/sys/user/userns" "github.com/opencontainers/selinux/go-selinux" "github.com/sirupsen/logrus" "tags.cncf.io/container-device-interface/pkg/cdi" diff --git a/pkg/cri/server/sandbox_run_linux.go b/pkg/cri/server/sandbox_run_linux.go index 6c24017e6c..24fcb0e2b1 100644 --- a/pkg/cri/server/sandbox_run_linux.go +++ b/pkg/cri/server/sandbox_run_linux.go @@ -26,6 +26,7 @@ import ( "github.com/containerd/containerd/oci" "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/snapshots" + "github.com/moby/sys/user/userns" imagespec "github.com/opencontainers/image-spec/specs-go/v1" runtimespec "github.com/opencontainers/runtime-spec/specs-go" selinux "github.com/opencontainers/selinux/go-selinux" @@ -34,7 +35,6 @@ import ( "github.com/containerd/containerd/pkg/cri/annotations" customopts "github.com/containerd/containerd/pkg/cri/opts" - "github.com/containerd/containerd/pkg/userns" ) func (c *criService) sandboxContainerSpec(id string, config *runtime.PodSandboxConfig, diff --git a/pkg/cri/server/service_linux.go b/pkg/cri/server/service_linux.go index 7d3dd6733b..3a51d81d10 100644 --- a/pkg/cri/server/service_linux.go +++ b/pkg/cri/server/service_linux.go @@ -20,8 +20,8 @@ import ( "fmt" "github.com/containerd/containerd/pkg/cap" - "github.com/containerd/containerd/pkg/userns" cni "github.com/containerd/go-cni" + "github.com/moby/sys/user/userns" "github.com/opencontainers/selinux/go-selinux" "github.com/sirupsen/logrus" "tags.cncf.io/container-device-interface/pkg/cdi" diff --git a/pkg/userns/userns_unsupported.go b/pkg/userns/userns_deprecated.go similarity index 66% rename from pkg/userns/userns_unsupported.go rename to pkg/userns/userns_deprecated.go index c67f773d0a..e1edbc44bf 100644 --- a/pkg/userns/userns_unsupported.go +++ b/pkg/userns/userns_deprecated.go @@ -1,5 +1,3 @@ -//go:build !linux - /* Copyright The containerd Authors. @@ -16,10 +14,16 @@ limitations under the License. */ +// Deprecated: use github.com/moby/sys/user/userns package userns -// RunningInUserNS is a stub for non-Linux systems -// Always returns false +import "github.com/moby/sys/user/userns" + +// RunningInUserNS detects whether we are currently running in a Linux +// user namespace and memoizes the result. It returns false on non-Linux +// platforms. +// +// Deprecated: use [userns.RunningInUserNS]. func RunningInUserNS() bool { - return false + return userns.RunningInUserNS() } diff --git a/pkg/userns/userns_linux.go b/pkg/userns/userns_linux.go deleted file mode 100644 index 6656465efb..0000000000 --- a/pkg/userns/userns_linux.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - 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 userns - -import ( - "bufio" - "fmt" - "os" - "sync" -) - -var ( - inUserNS bool - nsOnce sync.Once -) - -// RunningInUserNS detects whether we are currently running in a user namespace. -// Originally copied from github.com/lxc/lxd/shared/util.go -func RunningInUserNS() bool { - nsOnce.Do(func() { - file, err := os.Open("/proc/self/uid_map") - if err != nil { - // This kernel-provided file only exists if user namespaces are supported - return - } - defer file.Close() - - buf := bufio.NewReader(file) - l, _, err := buf.ReadLine() - if err != nil { - return - } - - line := string(l) - var a, b, c int64 - fmt.Sscanf(line, "%d %d %d", &a, &b, &c) - - /* - * We assume we are in the initial user namespace if we have a full - * range - 4294967295 uids starting at uid 0. - */ - if a == 0 && b == 0 && c == 4294967295 { - return - } - inUserNS = true - }) - return inUserNS -} diff --git a/runtime/v2/runc/task/service.go b/runtime/v2/runc/task/service.go index 1395c37697..05557e7482 100644 --- a/runtime/v2/runc/task/service.go +++ b/runtime/v2/runc/task/service.go @@ -24,6 +24,8 @@ import ( "os" "sync" + "github.com/moby/sys/user/userns" + "github.com/containerd/cgroups/v3" "github.com/containerd/cgroups/v3/cgroup1" cgroupsv2 "github.com/containerd/cgroups/v3/cgroup2" @@ -37,7 +39,6 @@ import ( "github.com/containerd/containerd/pkg/process" "github.com/containerd/containerd/pkg/shutdown" "github.com/containerd/containerd/pkg/stdio" - "github.com/containerd/containerd/pkg/userns" "github.com/containerd/containerd/protobuf" ptypes "github.com/containerd/containerd/protobuf/types" "github.com/containerd/containerd/runtime/v2/runc" diff --git a/snapshots/overlay/overlayutils/check.go b/snapshots/overlay/overlayutils/check.go index 313947b077..a6e876fa6e 100644 --- a/snapshots/overlay/overlayutils/check.go +++ b/snapshots/overlay/overlayutils/check.go @@ -24,9 +24,10 @@ import ( "path/filepath" "syscall" + "github.com/moby/sys/user/userns" + kernel "github.com/containerd/containerd/contrib/seccomp/kernelversion" "github.com/containerd/containerd/mount" - "github.com/containerd/containerd/pkg/userns" "github.com/containerd/continuity/fs" "github.com/containerd/log" ) diff --git a/sys/oom_linux.go b/sys/oom_linux.go index bb2a3eafb4..5894544d4d 100644 --- a/sys/oom_linux.go +++ b/sys/oom_linux.go @@ -22,7 +22,7 @@ import ( "strconv" "strings" - "github.com/containerd/containerd/pkg/userns" + "github.com/moby/sys/user/userns" "golang.org/x/sys/unix" ) diff --git a/sys/oom_linux_test.go b/sys/oom_linux_test.go index cb22870955..da9cf912a8 100644 --- a/sys/oom_linux_test.go +++ b/sys/oom_linux_test.go @@ -24,7 +24,7 @@ import ( "testing" "time" - "github.com/containerd/containerd/pkg/userns" + "github.com/moby/sys/user/userns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/vendor/github.com/moby/sys/user/userns/userns.go b/vendor/github.com/moby/sys/user/userns/userns.go new file mode 100644 index 0000000000..56b24c44ad --- /dev/null +++ b/vendor/github.com/moby/sys/user/userns/userns.go @@ -0,0 +1,16 @@ +// Package userns provides utilities to detect whether we are currently running +// in a Linux user namespace. +// +// This code was migrated from [libcontainer/runc], which based its implementation +// on code from [lcx/incus]. +// +// [libcontainer/runc]: https://github.com/opencontainers/runc/blob/3778ae603c706494fd1e2c2faf83b406e38d687d/libcontainer/userns/userns_linux.go#L12-L49 +// [lcx/incus]: https://github.com/lxc/incus/blob/e45085dd42f826b3c8c3228e9733c0b6f998eafe/shared/util.go#L678-L700 +package userns + +// RunningInUserNS detects whether we are currently running in a Linux +// user namespace and memoizes the result. It returns false on non-Linux +// platforms. +func RunningInUserNS() bool { + return inUserNS() +} diff --git a/vendor/github.com/moby/sys/user/userns/userns_linux.go b/vendor/github.com/moby/sys/user/userns/userns_linux.go new file mode 100644 index 0000000000..87c1c38eec --- /dev/null +++ b/vendor/github.com/moby/sys/user/userns/userns_linux.go @@ -0,0 +1,53 @@ +package userns + +import ( + "bufio" + "fmt" + "os" + "sync" +) + +var inUserNS = sync.OnceValue(runningInUserNS) + +// runningInUserNS detects whether we are currently running in a user namespace. +// +// This code was migrated from [libcontainer/runc] and based on an implementation +// from [lcx/incus]. +// +// [libcontainer/runc]: https://github.com/opencontainers/runc/blob/3778ae603c706494fd1e2c2faf83b406e38d687d/libcontainer/userns/userns_linux.go#L12-L49 +// [lcx/incus]: https://github.com/lxc/incus/blob/e45085dd42f826b3c8c3228e9733c0b6f998eafe/shared/util.go#L678-L700 +func runningInUserNS() bool { + file, err := os.Open("/proc/self/uid_map") + if err != nil { + // This kernel-provided file only exists if user namespaces are supported. + return false + } + defer file.Close() + + buf := bufio.NewReader(file) + l, _, err := buf.ReadLine() + if err != nil { + return false + } + + return uidMapInUserNS(string(l)) +} + +func uidMapInUserNS(uidMap string) bool { + if uidMap == "" { + // File exist but empty (the initial state when userns is created, + // see user_namespaces(7)). + return true + } + + var a, b, c int64 + if _, err := fmt.Sscanf(uidMap, "%d %d %d", &a, &b, &c); err != nil { + // Assume we are in a regular, non user namespace. + return false + } + + // As per user_namespaces(7), /proc/self/uid_map of + // the initial user namespace shows 0 0 4294967295. + initNS := a == 0 && b == 0 && c == 4294967295 + return !initNS +} diff --git a/vendor/github.com/moby/sys/user/userns/userns_linux_fuzzer.go b/vendor/github.com/moby/sys/user/userns/userns_linux_fuzzer.go new file mode 100644 index 0000000000..26ba2e16ec --- /dev/null +++ b/vendor/github.com/moby/sys/user/userns/userns_linux_fuzzer.go @@ -0,0 +1,8 @@ +//go:build linux && gofuzz + +package userns + +func FuzzUIDMap(uidmap []byte) int { + _ = uidMapInUserNS(string(uidmap)) + return 1 +} diff --git a/vendor/github.com/moby/sys/user/userns/userns_unsupported.go b/vendor/github.com/moby/sys/user/userns/userns_unsupported.go new file mode 100644 index 0000000000..8ed83072c2 --- /dev/null +++ b/vendor/github.com/moby/sys/user/userns/userns_unsupported.go @@ -0,0 +1,6 @@ +//go:build !linux + +package userns + +// inUserNS is a stub for non-Linux systems. Always returns false. +func inUserNS() bool { return false } diff --git a/vendor/modules.txt b/vendor/modules.txt index 2a0cae9d33..1ccd59f369 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -369,6 +369,7 @@ github.com/moby/sys/symlink # github.com/moby/sys/user v0.2.0 ## explicit; go 1.21 github.com/moby/sys/user +github.com/moby/sys/user/userns # github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd ## explicit github.com/modern-go/concurrent From dce0b5a6d338900f33ff44808483ebf63dced65a Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 8 Aug 2024 12:48:54 +0200 Subject: [PATCH 13/27] migrate to github.com/moby/sys/userns Commit 8437c567d89bae1154c4ff926bf96bac39b8f30f migrated the use of the userns package to the github.com/moby/sys/user module. After further discussion with maintainers, it was decided to move the userns package to a separate module, as it has no direct relation with "user" operations (other than having "user" in its name). This patch migrates our code to use the new module. Signed-off-by: Sebastiaan van Stijn (cherry picked from commit 977604724340e6ed410c077fa2df5301ae1f30fe) Signed-off-by: Sebastiaan van Stijn --- archive/tar.go | 2 +- archive/tar_unix.go | 2 +- diff/apply/apply_linux.go | 2 +- go.mod | 3 +- go.sum | 6 +- integration/client/go.mod | 3 +- integration/client/go.sum | 6 +- mount/mount_linux.go | 2 +- oci/utils_unix.go | 2 +- oci/utils_unix_test.go | 2 +- .../sbserver/podsandbox/sandbox_run_linux.go | 2 +- pkg/cri/sbserver/service_linux.go | 2 +- pkg/cri/server/sandbox_run_linux.go | 2 +- pkg/cri/server/service_linux.go | 2 +- pkg/userns/userns_deprecated.go | 4 +- runtime/v2/runc/task/service.go | 2 +- snapshots/overlay/overlayutils/check.go | 2 +- sys/oom_linux.go | 2 +- sys/oom_linux_test.go | 2 +- vendor/github.com/moby/sys/userns/LICENSE | 202 ++++++++++++++++++ .../moby/sys/{user => }/userns/userns.go | 0 .../sys/{user => }/userns/userns_linux.go | 0 .../{user => }/userns/userns_linux_fuzzer.go | 0 .../{user => }/userns/userns_unsupported.go | 0 vendor/modules.txt | 8 +- 25 files changed, 235 insertions(+), 25 deletions(-) create mode 100644 vendor/github.com/moby/sys/userns/LICENSE rename vendor/github.com/moby/sys/{user => }/userns/userns.go (100%) rename vendor/github.com/moby/sys/{user => }/userns/userns_linux.go (100%) rename vendor/github.com/moby/sys/{user => }/userns/userns_linux_fuzzer.go (100%) rename vendor/github.com/moby/sys/{user => }/userns/userns_unsupported.go (100%) diff --git a/archive/tar.go b/archive/tar.go index aadb694f8d..c61f89ec8d 100644 --- a/archive/tar.go +++ b/archive/tar.go @@ -29,7 +29,7 @@ import ( "syscall" "time" - "github.com/moby/sys/user/userns" + "github.com/moby/sys/userns" "github.com/containerd/containerd/archive/tarheader" "github.com/containerd/containerd/pkg/epoch" diff --git a/archive/tar_unix.go b/archive/tar_unix.go index 866a368242..fa61100609 100644 --- a/archive/tar_unix.go +++ b/archive/tar_unix.go @@ -27,7 +27,7 @@ import ( "strings" "syscall" - "github.com/moby/sys/user/userns" + "github.com/moby/sys/userns" "golang.org/x/sys/unix" "github.com/containerd/continuity/fs" diff --git a/diff/apply/apply_linux.go b/diff/apply/apply_linux.go index 3d93f38451..99f3278de9 100644 --- a/diff/apply/apply_linux.go +++ b/diff/apply/apply_linux.go @@ -23,7 +23,7 @@ import ( "os" "strings" - "github.com/moby/sys/user/userns" + "github.com/moby/sys/userns" "golang.org/x/sys/unix" "github.com/containerd/containerd/archive" diff --git a/go.mod b/go.mod index 084a39fbf8..0b92b6d2a3 100644 --- a/go.mod +++ b/go.mod @@ -48,7 +48,8 @@ require ( github.com/moby/sys/sequential v0.5.0 github.com/moby/sys/signal v0.7.0 github.com/moby/sys/symlink v0.2.0 - github.com/moby/sys/user v0.2.0 + github.com/moby/sys/user v0.3.0 + github.com/moby/sys/userns v0.1.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0 github.com/opencontainers/runtime-spec v1.1.0 diff --git a/go.sum b/go.sum index 632a8ed7f5..044d37b418 100644 --- a/go.sum +++ b/go.sum @@ -545,8 +545,10 @@ github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= github.com/moby/sys/symlink v0.2.0 h1:tk1rOM+Ljp0nFmfOIBtlV3rTDlWOwFRhjEeAhZB0nZc= github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= -github.com/moby/sys/user v0.2.0 h1:OnpapJsRp25vkhw8TFG6OLJODNh/3rEwRWtJ3kakwRM= -github.com/moby/sys/user v0.2.0/go.mod h1:RYstrcWOJpVh+6qzUqp2bU3eaRpdiQeKGlKitaH0PM8= +github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= +github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= diff --git a/integration/client/go.mod b/integration/client/go.mod index 5d4c14442a..691214ef69 100644 --- a/integration/client/go.mod +++ b/integration/client/go.mod @@ -52,7 +52,8 @@ require ( github.com/moby/sys/mountinfo v0.6.2 // indirect github.com/moby/sys/sequential v0.5.0 // indirect github.com/moby/sys/signal v0.7.0 // indirect - github.com/moby/sys/user v0.2.0 // indirect + github.com/moby/sys/user v0.3.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect github.com/opencontainers/selinux v1.11.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pkg/errors v0.9.1 // indirect diff --git a/integration/client/go.sum b/integration/client/go.sum index 79af20d5b8..3da1496212 100644 --- a/integration/client/go.sum +++ b/integration/client/go.sum @@ -1820,8 +1820,10 @@ github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI= github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= -github.com/moby/sys/user v0.2.0 h1:OnpapJsRp25vkhw8TFG6OLJODNh/3rEwRWtJ3kakwRM= -github.com/moby/sys/user v0.2.0/go.mod h1:RYstrcWOJpVh+6qzUqp2bU3eaRpdiQeKGlKitaH0PM8= +github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= +github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/mount/mount_linux.go b/mount/mount_linux.go index 6c353e9a2a..402def9c79 100644 --- a/mount/mount_linux.go +++ b/mount/mount_linux.go @@ -26,7 +26,7 @@ import ( "strings" "time" - "github.com/moby/sys/user/userns" + "github.com/moby/sys/userns" "golang.org/x/sys/unix" ) diff --git a/oci/utils_unix.go b/oci/utils_unix.go index f954df7976..602bbed148 100644 --- a/oci/utils_unix.go +++ b/oci/utils_unix.go @@ -24,7 +24,7 @@ import ( "os" "path/filepath" - "github.com/moby/sys/user/userns" + "github.com/moby/sys/userns" specs "github.com/opencontainers/runtime-spec/specs-go" "golang.org/x/sys/unix" ) diff --git a/oci/utils_unix_test.go b/oci/utils_unix_test.go index da4838b391..9c19205d69 100644 --- a/oci/utils_unix_test.go +++ b/oci/utils_unix_test.go @@ -25,7 +25,7 @@ import ( "runtime" "testing" - "github.com/moby/sys/user/userns" + "github.com/moby/sys/userns" "github.com/stretchr/testify/assert" ) diff --git a/pkg/cri/sbserver/podsandbox/sandbox_run_linux.go b/pkg/cri/sbserver/podsandbox/sandbox_run_linux.go index a1a26d63e3..3b899a8de3 100644 --- a/pkg/cri/sbserver/podsandbox/sandbox_run_linux.go +++ b/pkg/cri/sbserver/podsandbox/sandbox_run_linux.go @@ -25,7 +25,7 @@ import ( "github.com/containerd/containerd" "github.com/containerd/containerd/oci" "github.com/containerd/containerd/plugin" - "github.com/moby/sys/user/userns" + "github.com/moby/sys/userns" imagespec "github.com/opencontainers/image-spec/specs-go/v1" runtimespec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux" diff --git a/pkg/cri/sbserver/service_linux.go b/pkg/cri/sbserver/service_linux.go index a094b28228..8b3cf866fa 100644 --- a/pkg/cri/sbserver/service_linux.go +++ b/pkg/cri/sbserver/service_linux.go @@ -21,7 +21,7 @@ import ( "github.com/containerd/containerd/pkg/cap" "github.com/containerd/go-cni" - "github.com/moby/sys/user/userns" + "github.com/moby/sys/userns" "github.com/opencontainers/selinux/go-selinux" "github.com/sirupsen/logrus" "tags.cncf.io/container-device-interface/pkg/cdi" diff --git a/pkg/cri/server/sandbox_run_linux.go b/pkg/cri/server/sandbox_run_linux.go index 24fcb0e2b1..cba1dadcbd 100644 --- a/pkg/cri/server/sandbox_run_linux.go +++ b/pkg/cri/server/sandbox_run_linux.go @@ -26,7 +26,7 @@ import ( "github.com/containerd/containerd/oci" "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/snapshots" - "github.com/moby/sys/user/userns" + "github.com/moby/sys/userns" imagespec "github.com/opencontainers/image-spec/specs-go/v1" runtimespec "github.com/opencontainers/runtime-spec/specs-go" selinux "github.com/opencontainers/selinux/go-selinux" diff --git a/pkg/cri/server/service_linux.go b/pkg/cri/server/service_linux.go index 3a51d81d10..851f51a140 100644 --- a/pkg/cri/server/service_linux.go +++ b/pkg/cri/server/service_linux.go @@ -21,7 +21,7 @@ import ( "github.com/containerd/containerd/pkg/cap" cni "github.com/containerd/go-cni" - "github.com/moby/sys/user/userns" + "github.com/moby/sys/userns" "github.com/opencontainers/selinux/go-selinux" "github.com/sirupsen/logrus" "tags.cncf.io/container-device-interface/pkg/cdi" diff --git a/pkg/userns/userns_deprecated.go b/pkg/userns/userns_deprecated.go index e1edbc44bf..79d22a5d1d 100644 --- a/pkg/userns/userns_deprecated.go +++ b/pkg/userns/userns_deprecated.go @@ -14,10 +14,10 @@ limitations under the License. */ -// Deprecated: use github.com/moby/sys/user/userns +// Deprecated: use github.com/moby/sys/userns package userns -import "github.com/moby/sys/user/userns" +import "github.com/moby/sys/userns" // RunningInUserNS detects whether we are currently running in a Linux // user namespace and memoizes the result. It returns false on non-Linux diff --git a/runtime/v2/runc/task/service.go b/runtime/v2/runc/task/service.go index 05557e7482..132a5cbb3e 100644 --- a/runtime/v2/runc/task/service.go +++ b/runtime/v2/runc/task/service.go @@ -24,7 +24,7 @@ import ( "os" "sync" - "github.com/moby/sys/user/userns" + "github.com/moby/sys/userns" "github.com/containerd/cgroups/v3" "github.com/containerd/cgroups/v3/cgroup1" diff --git a/snapshots/overlay/overlayutils/check.go b/snapshots/overlay/overlayutils/check.go index a6e876fa6e..23a39e016d 100644 --- a/snapshots/overlay/overlayutils/check.go +++ b/snapshots/overlay/overlayutils/check.go @@ -24,7 +24,7 @@ import ( "path/filepath" "syscall" - "github.com/moby/sys/user/userns" + "github.com/moby/sys/userns" kernel "github.com/containerd/containerd/contrib/seccomp/kernelversion" "github.com/containerd/containerd/mount" diff --git a/sys/oom_linux.go b/sys/oom_linux.go index 5894544d4d..01ebbebb59 100644 --- a/sys/oom_linux.go +++ b/sys/oom_linux.go @@ -22,7 +22,7 @@ import ( "strconv" "strings" - "github.com/moby/sys/user/userns" + "github.com/moby/sys/userns" "golang.org/x/sys/unix" ) diff --git a/sys/oom_linux_test.go b/sys/oom_linux_test.go index da9cf912a8..9266f65a91 100644 --- a/sys/oom_linux_test.go +++ b/sys/oom_linux_test.go @@ -24,7 +24,7 @@ import ( "testing" "time" - "github.com/moby/sys/user/userns" + "github.com/moby/sys/userns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/vendor/github.com/moby/sys/userns/LICENSE b/vendor/github.com/moby/sys/userns/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/github.com/moby/sys/userns/LICENSE @@ -0,0 +1,202 @@ + + 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. diff --git a/vendor/github.com/moby/sys/user/userns/userns.go b/vendor/github.com/moby/sys/userns/userns.go similarity index 100% rename from vendor/github.com/moby/sys/user/userns/userns.go rename to vendor/github.com/moby/sys/userns/userns.go diff --git a/vendor/github.com/moby/sys/user/userns/userns_linux.go b/vendor/github.com/moby/sys/userns/userns_linux.go similarity index 100% rename from vendor/github.com/moby/sys/user/userns/userns_linux.go rename to vendor/github.com/moby/sys/userns/userns_linux.go diff --git a/vendor/github.com/moby/sys/user/userns/userns_linux_fuzzer.go b/vendor/github.com/moby/sys/userns/userns_linux_fuzzer.go similarity index 100% rename from vendor/github.com/moby/sys/user/userns/userns_linux_fuzzer.go rename to vendor/github.com/moby/sys/userns/userns_linux_fuzzer.go diff --git a/vendor/github.com/moby/sys/user/userns/userns_unsupported.go b/vendor/github.com/moby/sys/userns/userns_unsupported.go similarity index 100% rename from vendor/github.com/moby/sys/user/userns/userns_unsupported.go rename to vendor/github.com/moby/sys/userns/userns_unsupported.go diff --git a/vendor/modules.txt b/vendor/modules.txt index 1ccd59f369..61d5ec0205 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -366,10 +366,12 @@ github.com/moby/sys/signal # github.com/moby/sys/symlink v0.2.0 ## explicit; go 1.16 github.com/moby/sys/symlink -# github.com/moby/sys/user v0.2.0 -## explicit; go 1.21 +# github.com/moby/sys/user v0.3.0 +## explicit; go 1.17 github.com/moby/sys/user -github.com/moby/sys/user/userns +# github.com/moby/sys/userns v0.1.0 +## explicit; go 1.21 +github.com/moby/sys/userns # github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd ## explicit github.com/modern-go/concurrent From 228914a5e533db62f2637267c26b0093b6e8625e Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 9 Aug 2024 17:05:41 +0200 Subject: [PATCH 14/27] update to go1.21.13 / go1.22.6 - https://github.com/golang/go/issues?q=milestone%3AGo1.22.6+label%3ACherryPickApproved - full diff: https://github.com/golang/go/compare/go1.22.5...go1.22.6 go1.22.6 (released 2024-08-06) includes fixes to the go command, the compiler, the linker, the trace command, the covdata command, and the bytes, go/types, and os/exec packages. See the Go 1.22.6 milestone on our issue tracker for details. go1.21.13 (released 2024-08-06) includes fixes to the go command, the covdata command, and the bytes package. See the Go 1.21.13 milestone on our issue tracker for details. Signed-off-by: Sebastiaan van Stijn --- .github/actions/install-go/action.yml | 2 +- .github/workflows/api-release.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- Vagrantfile | 2 +- contrib/Dockerfile.test | 2 +- contrib/fuzz/oss_fuzz_build.sh | 4 ++-- script/setup/prepare_env_windows.ps1 | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/actions/install-go/action.yml b/.github/actions/install-go/action.yml index 01eb62ff67..bdaed8a5be 100644 --- a/.github/actions/install-go/action.yml +++ b/.github/actions/install-go/action.yml @@ -3,7 +3,7 @@ description: "Reusable action to install Go, so there is one place to bump Go ve inputs: go-version: required: true - default: "1.21.12" + default: "1.21.13" description: "Go version to install" runs: diff --git a/.github/workflows/api-release.yml b/.github/workflows/api-release.yml index 69d158f70a..71e56ec670 100644 --- a/.github/workflows/api-release.yml +++ b/.github/workflows/api-release.yml @@ -6,7 +6,7 @@ on: name: API Release env: - GO_VERSION: "1.21.12" + GO_VERSION: "1.21.13" permissions: # added using https://github.com/step-security/secure-workflows contents: read diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ff800f007..b9a3376c97 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -191,7 +191,7 @@ jobs: strategy: matrix: os: [ubuntu-20.04, actuated-arm64-4cpu-16gb, macos-12, windows-2019, windows-2022] - go-version: ["1.21.12", "1.22.5"] + go-version: ["1.21.13", "1.22.6"] steps: - uses: actions/checkout@v4 - uses: ./.github/actions/install-go diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bcf026ce40..e424e6c4f0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ on: name: Release env: - GO_VERSION: "1.21.12" + GO_VERSION: "1.21.13" permissions: # added using https://github.com/step-security/secure-workflows contents: read diff --git a/Vagrantfile b/Vagrantfile index 63cfa47d71..75eb4f0d16 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -104,7 +104,7 @@ EOF config.vm.provision "install-golang", type: "shell", run: "once" do |sh| sh.upload_path = "/tmp/vagrant-install-golang" sh.env = { - 'GO_VERSION': ENV['GO_VERSION'] || "1.21.12", + 'GO_VERSION': ENV['GO_VERSION'] || "1.21.13", } sh.inline = <<~SHELL #!/usr/bin/env bash diff --git a/contrib/Dockerfile.test b/contrib/Dockerfile.test index 78a6e31f21..4efe428b15 100644 --- a/contrib/Dockerfile.test +++ b/contrib/Dockerfile.test @@ -29,7 +29,7 @@ # docker run --privileged containerd-test # ------------------------------------------------------------------------------ -ARG GOLANG_VERSION=1.21.12 +ARG GOLANG_VERSION=1.21.13 ARG GOLANG_IMAGE=golang FROM ${GOLANG_IMAGE}:${GOLANG_VERSION} AS golang diff --git a/contrib/fuzz/oss_fuzz_build.sh b/contrib/fuzz/oss_fuzz_build.sh index 0553ef6c2c..1c520b3bcf 100755 --- a/contrib/fuzz/oss_fuzz_build.sh +++ b/contrib/fuzz/oss_fuzz_build.sh @@ -43,11 +43,11 @@ go run main.go --target_dir $SRC/containerd/images apt-get update && apt-get install -y wget cd $SRC -wget --quiet https://go.dev/dl/go1.21.12.linux-amd64.tar.gz +wget --quiet https://go.dev/dl/go1.21.13.linux-amd64.tar.gz mkdir temp-go rm -rf /root/.go/* -tar -C temp-go/ -xzf go1.21.12.linux-amd64.tar.gz +tar -C temp-go/ -xzf go1.21.13.linux-amd64.tar.gz mv temp-go/go/* /root/.go/ cd $SRC/containerd diff --git a/script/setup/prepare_env_windows.ps1 b/script/setup/prepare_env_windows.ps1 index 5f66040edd..76fbcd76ab 100644 --- a/script/setup/prepare_env_windows.ps1 +++ b/script/setup/prepare_env_windows.ps1 @@ -5,7 +5,7 @@ # lived test environment. Set-MpPreference -DisableRealtimeMonitoring:$true -$PACKAGES= @{ mingw = "10.2.0"; git = ""; golang = "1.21.12"; make = ""; nssm = "" } +$PACKAGES= @{ mingw = "10.2.0"; git = ""; golang = "1.21.13"; make = ""; nssm = "" } Write-Host "Downloading chocolatey package" curl.exe -L "https://packages.chocolatey.org/chocolatey.0.10.15.nupkg" -o 'c:\choco.zip' From 52b79f3377af7c0003abe85e3d93d7d679d8418f Mon Sep 17 00:00:00 2001 From: Chris Henzie Date: Fri, 9 Aug 2024 15:37:40 -0700 Subject: [PATCH 15/27] Update CRIAPIV1Alpha2 warning lastOccurrence every call This helps gather more accurate API usage data that informs efforts on how to safely migrate containerd clients to 2.0. Signed-off-by: Chris Henzie --- pkg/cri/instrument/instrumented_service.go | 8 ++++---- pkg/cri/server/service_test.go | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pkg/cri/instrument/instrumented_service.go b/pkg/cri/instrument/instrumented_service.go index b73843cb4b..9a44757fa9 100644 --- a/pkg/cri/instrument/instrumented_service.go +++ b/pkg/cri/instrument/instrumented_service.go @@ -105,13 +105,13 @@ func (in *instrumentedAlphaService) checkInitialized(ctx context.Context) error // emitUsageWarning emits a warning when v1alpha2 cri-api is called. func (in *instrumentedAlphaService) emitUsageWarning(ctx context.Context) { - // Only emit the warning the first time an v1alpha2 api is called + // Only log the warning the first time an v1alpha2 api is called in.emitWarning.Do(func() { log.G(ctx).Warning("CRI API v1alpha2 is deprecated since containerd v1.7 and removed in containerd v2.0. Use CRI API v1 instead.") - if in.warn != nil { - in.warn.Emit(ctx, deprecation.CRIAPIV1Alpha2) - } }) + if in.warn != nil { + in.warn.Emit(ctx, deprecation.CRIAPIV1Alpha2) + } } func (in *instrumentedService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandboxRequest) (res *runtime.RunPodSandboxResponse, err error) { diff --git a/pkg/cri/server/service_test.go b/pkg/cri/server/service_test.go index 8646581d13..35e0b4dd24 100644 --- a/pkg/cri/server/service_test.go +++ b/pkg/cri/server/service_test.go @@ -101,9 +101,10 @@ func TestAlphaCRIWarning(t *testing.T) { c.Version(ctx, &v1alpha2.VersionRequest{}) c.Status(ctx, &v1alpha2.StatusRequest{}) - // Only emit the warning the first time an v1alpha2 api is called. + // Emit warnings both times an v1alpha2 api is called. expectedWarnings := []deprecation.Warning{ deprecation.CRIAPIV1Alpha2, + deprecation.CRIAPIV1Alpha2, } assert.Equal(t, expectedWarnings, ws.GetWarnings()) } From 82bfa44d0fedef27c2b74af49866d74d5317359b Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Sat, 10 Aug 2024 20:52:48 -0400 Subject: [PATCH 16/27] Copy over wsstream from k8s v1.31.0-rc.1 release SHA: 60c4c2b2521fb454ce69dee737e3eb91a25e0535 Signed-off-by: Davanum Srinivas --- pkg/cri/streaming/wsstream/conn.go | 452 ++++++++++++++++++++++ pkg/cri/streaming/wsstream/conn_test.go | 418 ++++++++++++++++++++ pkg/cri/streaming/wsstream/stream.go | 177 +++++++++ pkg/cri/streaming/wsstream/stream_test.go | 293 ++++++++++++++ 4 files changed, 1340 insertions(+) create mode 100644 pkg/cri/streaming/wsstream/conn.go create mode 100644 pkg/cri/streaming/wsstream/conn_test.go create mode 100644 pkg/cri/streaming/wsstream/stream.go create mode 100644 pkg/cri/streaming/wsstream/stream_test.go diff --git a/pkg/cri/streaming/wsstream/conn.go b/pkg/cri/streaming/wsstream/conn.go new file mode 100644 index 0000000000..2e477fee2a --- /dev/null +++ b/pkg/cri/streaming/wsstream/conn.go @@ -0,0 +1,452 @@ +/* +Copyright 2015 The Kubernetes 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 wsstream + +import ( + "encoding/base64" + "fmt" + "io" + "net/http" + "strings" + "time" + + "golang.org/x/net/websocket" + + "k8s.io/apimachinery/pkg/util/httpstream" + "k8s.io/apimachinery/pkg/util/portforward" + "k8s.io/apimachinery/pkg/util/remotecommand" + "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/klog/v2" +) + +const WebSocketProtocolHeader = "Sec-Websocket-Protocol" + +// The Websocket subprotocol "channel.k8s.io" prepends each binary message with a byte indicating +// the channel number (zero indexed) the message was sent on. Messages in both directions should +// prefix their messages with this channel byte. When used for remote execution, the channel numbers +// are by convention defined to match the POSIX file-descriptors assigned to STDIN, STDOUT, and STDERR +// (0, 1, and 2). No other conversion is performed on the raw subprotocol - writes are sent as they +// are received by the server. +// +// Example client session: +// +// CONNECT http://server.com with subprotocol "channel.k8s.io" +// WRITE []byte{0, 102, 111, 111, 10} # send "foo\n" on channel 0 (STDIN) +// READ []byte{1, 10} # receive "\n" on channel 1 (STDOUT) +// CLOSE +const ChannelWebSocketProtocol = "channel.k8s.io" + +// The Websocket subprotocol "base64.channel.k8s.io" base64 encodes each message with a character +// indicating the channel number (zero indexed) the message was sent on. Messages in both directions +// should prefix their messages with this channel char. When used for remote execution, the channel +// numbers are by convention defined to match the POSIX file-descriptors assigned to STDIN, STDOUT, +// and STDERR ('0', '1', and '2'). The data received on the server is base64 decoded (and must be +// be valid) and data written by the server to the client is base64 encoded. +// +// Example client session: +// +// CONNECT http://server.com with subprotocol "base64.channel.k8s.io" +// WRITE []byte{48, 90, 109, 57, 118, 67, 103, 111, 61} # send "foo\n" (base64: "Zm9vCgo=") on channel '0' (STDIN) +// READ []byte{49, 67, 103, 61, 61} # receive "\n" (base64: "Cg==") on channel '1' (STDOUT) +// CLOSE +const Base64ChannelWebSocketProtocol = "base64.channel.k8s.io" + +type codecType int + +const ( + rawCodec codecType = iota + base64Codec +) + +type ChannelType int + +const ( + IgnoreChannel ChannelType = iota + ReadChannel + WriteChannel + ReadWriteChannel +) + +// IsWebSocketRequest returns true if the incoming request contains connection upgrade headers +// for WebSockets. +func IsWebSocketRequest(req *http.Request) bool { + if !strings.EqualFold(req.Header.Get("Upgrade"), "websocket") { + return false + } + return httpstream.IsUpgradeRequest(req) +} + +// IsWebSocketRequestWithStreamCloseProtocol returns true if the request contains headers +// identifying that it is requesting a websocket upgrade with a remotecommand protocol +// version that supports the "CLOSE" signal; false otherwise. +func IsWebSocketRequestWithStreamCloseProtocol(req *http.Request) bool { + if !IsWebSocketRequest(req) { + return false + } + requestedProtocols := strings.TrimSpace(req.Header.Get(WebSocketProtocolHeader)) + for _, requestedProtocol := range strings.Split(requestedProtocols, ",") { + if protocolSupportsStreamClose(strings.TrimSpace(requestedProtocol)) { + return true + } + } + + return false +} + +// IsWebSocketRequestWithTunnelingProtocol returns true if the request contains headers +// identifying that it is requesting a websocket upgrade with a tunneling protocol; +// false otherwise. +func IsWebSocketRequestWithTunnelingProtocol(req *http.Request) bool { + if !IsWebSocketRequest(req) { + return false + } + requestedProtocols := strings.TrimSpace(req.Header.Get(WebSocketProtocolHeader)) + for _, requestedProtocol := range strings.Split(requestedProtocols, ",") { + if protocolSupportsWebsocketTunneling(strings.TrimSpace(requestedProtocol)) { + return true + } + } + + return false +} + +// IgnoreReceives reads from a WebSocket until it is closed, then returns. If timeout is set, the +// read and write deadlines are pushed every time a new message is received. +func IgnoreReceives(ws *websocket.Conn, timeout time.Duration) { + defer runtime.HandleCrash() + var data []byte + for { + resetTimeout(ws, timeout) + if err := websocket.Message.Receive(ws, &data); err != nil { + return + } + } +} + +// handshake ensures the provided user protocol matches one of the allowed protocols. It returns +// no error if no protocol is specified. +func handshake(config *websocket.Config, req *http.Request, allowed []string) error { + protocols := config.Protocol + if len(protocols) == 0 { + protocols = []string{""} + } + + for _, protocol := range protocols { + for _, allow := range allowed { + if allow == protocol { + config.Protocol = []string{protocol} + return nil + } + } + } + + return fmt.Errorf("requested protocol(s) are not supported: %v; supports %v", config.Protocol, allowed) +} + +// ChannelProtocolConfig describes a websocket subprotocol with channels. +type ChannelProtocolConfig struct { + Binary bool + Channels []ChannelType +} + +// NewDefaultChannelProtocols returns a channel protocol map with the +// subprotocols "", "channel.k8s.io", "base64.channel.k8s.io" and the given +// channels. +func NewDefaultChannelProtocols(channels []ChannelType) map[string]ChannelProtocolConfig { + return map[string]ChannelProtocolConfig{ + "": {Binary: true, Channels: channels}, + ChannelWebSocketProtocol: {Binary: true, Channels: channels}, + Base64ChannelWebSocketProtocol: {Binary: false, Channels: channels}, + } +} + +// Conn supports sending multiple binary channels over a websocket connection. +type Conn struct { + protocols map[string]ChannelProtocolConfig + selectedProtocol string + channels []*websocketChannel + codec codecType + ready chan struct{} + ws *websocket.Conn + timeout time.Duration +} + +// NewConn creates a WebSocket connection that supports a set of channels. Channels begin each +// web socket message with a single byte indicating the channel number (0-N). 255 is reserved for +// future use. The channel types for each channel are passed as an array, supporting the different +// duplex modes. Read and Write refer to whether the channel can be used as a Reader or Writer. +// +// The protocols parameter maps subprotocol names to ChannelProtocols. The empty string subprotocol +// name is used if websocket.Config.Protocol is empty. +func NewConn(protocols map[string]ChannelProtocolConfig) *Conn { + return &Conn{ + ready: make(chan struct{}), + protocols: protocols, + } +} + +// SetIdleTimeout sets the interval for both reads and writes before timeout. If not specified, +// there is no timeout on the connection. +func (conn *Conn) SetIdleTimeout(duration time.Duration) { + conn.timeout = duration +} + +// SetWriteDeadline sets a timeout on writing to the websocket connection. The +// passed "duration" identifies how far into the future the write must complete +// by before the timeout fires. +func (conn *Conn) SetWriteDeadline(duration time.Duration) { + conn.ws.SetWriteDeadline(time.Now().Add(duration)) //nolint:errcheck +} + +// Open the connection and create channels for reading and writing. It returns +// the selected subprotocol, a slice of channels and an error. +func (conn *Conn) Open(w http.ResponseWriter, req *http.Request) (string, []io.ReadWriteCloser, error) { + // serveHTTPComplete is channel that is closed/selected when "websocket#ServeHTTP" finishes. + serveHTTPComplete := make(chan struct{}) + // Ensure panic in spawned goroutine is propagated into the parent goroutine. + panicChan := make(chan any, 1) + go func() { + // If websocket server returns, propagate panic if necessary. Otherwise, + // signal HTTPServe finished by closing "serveHTTPComplete". + defer func() { + if p := recover(); p != nil { + panicChan <- p + } else { + close(serveHTTPComplete) + } + }() + websocket.Server{Handshake: conn.handshake, Handler: conn.handle}.ServeHTTP(w, req) + }() + + // In normal circumstances, "websocket.Server#ServeHTTP" calls "initialize" which closes + // "conn.ready" and then blocks until serving is complete. + select { + case <-conn.ready: + klog.V(8).Infof("websocket server initialized--serving") + case <-serveHTTPComplete: + // websocket server returned before completing initialization; cleanup and return error. + conn.closeNonThreadSafe() //nolint:errcheck + return "", nil, fmt.Errorf("websocket server finished before becoming ready") + case p := <-panicChan: + panic(p) + } + + rwc := make([]io.ReadWriteCloser, len(conn.channels)) + for i := range conn.channels { + rwc[i] = conn.channels[i] + } + return conn.selectedProtocol, rwc, nil +} + +func (conn *Conn) initialize(ws *websocket.Conn) { + negotiated := ws.Config().Protocol + conn.selectedProtocol = negotiated[0] + p := conn.protocols[conn.selectedProtocol] + if p.Binary { + conn.codec = rawCodec + } else { + conn.codec = base64Codec + } + conn.ws = ws + conn.channels = make([]*websocketChannel, len(p.Channels)) + for i, t := range p.Channels { + switch t { + case ReadChannel: + conn.channels[i] = newWebsocketChannel(conn, byte(i), true, false) + case WriteChannel: + conn.channels[i] = newWebsocketChannel(conn, byte(i), false, true) + case ReadWriteChannel: + conn.channels[i] = newWebsocketChannel(conn, byte(i), true, true) + case IgnoreChannel: + conn.channels[i] = newWebsocketChannel(conn, byte(i), false, false) + } + } + + close(conn.ready) +} + +func (conn *Conn) handshake(config *websocket.Config, req *http.Request) error { + supportedProtocols := make([]string, 0, len(conn.protocols)) + for p := range conn.protocols { + supportedProtocols = append(supportedProtocols, p) + } + return handshake(config, req, supportedProtocols) +} + +func (conn *Conn) resetTimeout() { + if conn.timeout > 0 { + conn.ws.SetDeadline(time.Now().Add(conn.timeout)) + } +} + +// closeNonThreadSafe cleans up by closing streams and the websocket +// connection *without* waiting for the "ready" channel. +func (conn *Conn) closeNonThreadSafe() error { + for _, s := range conn.channels { + s.Close() + } + var err error + if conn.ws != nil { + err = conn.ws.Close() + } + return err +} + +// Close is only valid after Open has been called +func (conn *Conn) Close() error { + <-conn.ready + return conn.closeNonThreadSafe() +} + +// protocolSupportsStreamClose returns true if the passed protocol +// supports the stream close signal (currently only V5 remotecommand); +// false otherwise. +func protocolSupportsStreamClose(protocol string) bool { + return protocol == remotecommand.StreamProtocolV5Name +} + +// protocolSupportsWebsocketTunneling returns true if the passed protocol +// is a tunneled Kubernetes spdy protocol; false otherwise. +func protocolSupportsWebsocketTunneling(protocol string) bool { + return strings.HasPrefix(protocol, portforward.WebsocketsSPDYTunnelingPrefix) && strings.HasSuffix(protocol, portforward.KubernetesSuffix) +} + +// handle implements a websocket handler. +func (conn *Conn) handle(ws *websocket.Conn) { + conn.initialize(ws) + defer conn.Close() + supportsStreamClose := protocolSupportsStreamClose(conn.selectedProtocol) + + for { + conn.resetTimeout() + var data []byte + if err := websocket.Message.Receive(ws, &data); err != nil { + if err != io.EOF { + klog.Errorf("Error on socket receive: %v", err) + } + break + } + if len(data) == 0 { + continue + } + if supportsStreamClose && data[0] == remotecommand.StreamClose { + if len(data) != 2 { + klog.Errorf("Single channel byte should follow stream close signal. Got %d bytes", len(data)-1) + break + } else { + channel := data[1] + if int(channel) >= len(conn.channels) { + klog.Errorf("Close is targeted for a channel %d that is not valid, possible protocol error", channel) + break + } + klog.V(4).Infof("Received half-close signal from client; close %d stream", channel) + conn.channels[channel].Close() // After first Close, other closes are noop. + } + continue + } + channel := data[0] + if conn.codec == base64Codec { + channel = channel - '0' + } + data = data[1:] + if int(channel) >= len(conn.channels) { + klog.V(6).Infof("Frame is targeted for a reader %d that is not valid, possible protocol error", channel) + continue + } + if _, err := conn.channels[channel].DataFromSocket(data); err != nil { + klog.Errorf("Unable to write frame (%d bytes) to %d: %v", len(data), channel, err) + continue + } + } +} + +// write multiplexes the specified channel onto the websocket +func (conn *Conn) write(num byte, data []byte) (int, error) { + conn.resetTimeout() + switch conn.codec { + case rawCodec: + frame := make([]byte, len(data)+1) + frame[0] = num + copy(frame[1:], data) + if err := websocket.Message.Send(conn.ws, frame); err != nil { + return 0, err + } + case base64Codec: + frame := string('0'+num) + base64.StdEncoding.EncodeToString(data) + if err := websocket.Message.Send(conn.ws, frame); err != nil { + return 0, err + } + } + return len(data), nil +} + +// websocketChannel represents a channel in a connection +type websocketChannel struct { + conn *Conn + num byte + r io.Reader + w io.WriteCloser + + read, write bool +} + +// newWebsocketChannel creates a pipe for writing to a websocket. Do not write to this pipe +// prior to the connection being opened. It may be no, half, or full duplex depending on +// read and write. +func newWebsocketChannel(conn *Conn, num byte, read, write bool) *websocketChannel { + r, w := io.Pipe() + return &websocketChannel{conn, num, r, w, read, write} +} + +func (p *websocketChannel) Write(data []byte) (int, error) { + if !p.write { + return len(data), nil + } + return p.conn.write(p.num, data) +} + +// DataFromSocket is invoked by the connection receiver to move data from the connection +// into a specific channel. +func (p *websocketChannel) DataFromSocket(data []byte) (int, error) { + if !p.read { + return len(data), nil + } + + switch p.conn.codec { + case rawCodec: + return p.w.Write(data) + case base64Codec: + dst := make([]byte, len(data)) + n, err := base64.StdEncoding.Decode(dst, data) + if err != nil { + return 0, err + } + return p.w.Write(dst[:n]) + } + return 0, nil +} + +func (p *websocketChannel) Read(data []byte) (int, error) { + if !p.read { + return 0, io.EOF + } + return p.r.Read(data) +} + +func (p *websocketChannel) Close() error { + return p.w.Close() +} diff --git a/pkg/cri/streaming/wsstream/conn_test.go b/pkg/cri/streaming/wsstream/conn_test.go new file mode 100644 index 0000000000..e4a88a1a8c --- /dev/null +++ b/pkg/cri/streaming/wsstream/conn_test.go @@ -0,0 +1,418 @@ +/* +Copyright 2015 The Kubernetes 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 wsstream + +import ( + "encoding/base64" + "io" + "net/http" + "net/http/httptest" + "reflect" + "sync" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/websocket" +) + +func newServer(handler http.Handler) (*httptest.Server, string) { + server := httptest.NewServer(handler) + serverAddr := server.Listener.Addr().String() + return server, serverAddr +} + +func TestRawConn(t *testing.T) { + channels := []ChannelType{ReadWriteChannel, ReadWriteChannel, IgnoreChannel, ReadChannel, WriteChannel} + conn := NewConn(NewDefaultChannelProtocols(channels)) + + s, addr := newServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + conn.Open(w, req) + })) + defer s.Close() + + client, err := websocket.Dial("ws://"+addr, "", "http://localhost/") + if err != nil { + t.Fatal(err) + } + defer client.Close() + + <-conn.ready + wg := sync.WaitGroup{} + + // verify we can read a client write + wg.Add(1) + go func() { + defer wg.Done() + data, err := io.ReadAll(conn.channels[0]) + if err != nil { + t.Error(err) + return + } + if !reflect.DeepEqual(data, []byte("client")) { + t.Errorf("unexpected server read: %v", data) + } + }() + + if n, err := client.Write(append([]byte{0}, []byte("client")...)); err != nil || n != 7 { + t.Fatalf("%d: %v", n, err) + } + + // verify we can read a server write + wg.Add(1) + go func() { + defer wg.Done() + if n, err := conn.channels[1].Write([]byte("server")); err != nil && n != 6 { + t.Errorf("%d: %v", n, err) + } + }() + + data := make([]byte, 1024) + if n, err := io.ReadAtLeast(client, data, 6); n != 7 || err != nil { + t.Fatalf("%d: %v", n, err) + } + if !reflect.DeepEqual(data[:7], append([]byte{1}, []byte("server")...)) { + t.Errorf("unexpected client read: %v", data[:7]) + } + + // verify that an ignore channel is empty in both directions. + if n, err := conn.channels[2].Write([]byte("test")); n != 4 || err != nil { + t.Errorf("writes should be ignored") + } + data = make([]byte, 1024) + if n, err := conn.channels[2].Read(data); n != 0 || err != io.EOF { + t.Errorf("reads should be ignored") + } + + // verify that a write to a Read channel doesn't block + if n, err := conn.channels[3].Write([]byte("test")); n != 4 || err != nil { + t.Errorf("writes should be ignored") + } + + // verify that a read from a Write channel doesn't block + data = make([]byte, 1024) + if n, err := conn.channels[4].Read(data); n != 0 || err != io.EOF { + t.Errorf("reads should be ignored") + } + + // verify that a client write to a Write channel doesn't block (is dropped) + if n, err := client.Write(append([]byte{4}, []byte("ignored")...)); err != nil || n != 8 { + t.Fatalf("%d: %v", n, err) + } + + client.Close() + wg.Wait() +} + +func TestBase64Conn(t *testing.T) { + conn := NewConn(NewDefaultChannelProtocols([]ChannelType{ReadWriteChannel, ReadWriteChannel})) + s, addr := newServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + conn.Open(w, req) + })) + defer s.Close() + + config, err := websocket.NewConfig("ws://"+addr, "http://localhost/") + if err != nil { + t.Fatal(err) + } + config.Protocol = []string{"base64.channel.k8s.io"} + client, err := websocket.DialConfig(config) + if err != nil { + t.Fatal(err) + } + defer client.Close() + + <-conn.ready + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + data, err := io.ReadAll(conn.channels[0]) + if err != nil { + t.Error(err) + return + } + if !reflect.DeepEqual(data, []byte("client")) { + t.Errorf("unexpected server read: %s", string(data)) + } + }() + + clientData := base64.StdEncoding.EncodeToString([]byte("client")) + if n, err := client.Write(append([]byte{'0'}, clientData...)); err != nil || n != len(clientData)+1 { + t.Fatalf("%d: %v", n, err) + } + + wg.Add(1) + go func() { + defer wg.Done() + if n, err := conn.channels[1].Write([]byte("server")); err != nil && n != 6 { + t.Errorf("%d: %v", n, err) + } + }() + + data := make([]byte, 1024) + if n, err := io.ReadAtLeast(client, data, 9); n != 9 || err != nil { + t.Fatalf("%d: %v", n, err) + } + expect := []byte(base64.StdEncoding.EncodeToString([]byte("server"))) + + if !reflect.DeepEqual(data[:9], append([]byte{'1'}, expect...)) { + t.Errorf("unexpected client read: %v", data[:9]) + } + + client.Close() + wg.Wait() +} + +type versionTest struct { + supported map[string]bool // protocol -> binary + requested []string + error bool + expected string +} + +func versionTests() []versionTest { + const ( + binary = true + base64 = false + ) + return []versionTest{ + { + supported: nil, + requested: []string{"raw"}, + error: true, + }, + { + supported: map[string]bool{"": binary, "raw": binary, "base64": base64}, + requested: nil, + expected: "", + }, + { + supported: map[string]bool{"": binary, "raw": binary, "base64": base64}, + requested: []string{"v1.raw"}, + error: true, + }, + { + supported: map[string]bool{"": binary, "raw": binary, "base64": base64}, + requested: []string{"v1.raw", "v1.base64"}, + error: true, + }, { + supported: map[string]bool{"": binary, "raw": binary, "base64": base64}, + requested: []string{"v1.raw", "raw"}, + expected: "raw", + }, + { + supported: map[string]bool{"": binary, "v1.raw": binary, "v1.base64": base64, "v2.raw": binary, "v2.base64": base64}, + requested: []string{"v1.raw"}, + expected: "v1.raw", + }, + { + supported: map[string]bool{"": binary, "v1.raw": binary, "v1.base64": base64, "v2.raw": binary, "v2.base64": base64}, + requested: []string{"v2.base64"}, + expected: "v2.base64", + }, + } +} + +func TestVersionedConn(t *testing.T) { + for i, test := range versionTests() { + func() { + supportedProtocols := map[string]ChannelProtocolConfig{} + for p, binary := range test.supported { + supportedProtocols[p] = ChannelProtocolConfig{ + Binary: binary, + Channels: []ChannelType{ReadWriteChannel}, + } + } + conn := NewConn(supportedProtocols) + // note that it's not enough to wait for conn.ready to avoid a race here. Hence, + // we use a channel. + selectedProtocol := make(chan string) + s, addr := newServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + p, _, _ := conn.Open(w, req) + selectedProtocol <- p + })) + defer s.Close() + + config, err := websocket.NewConfig("ws://"+addr, "http://localhost/") + if err != nil { + t.Fatal(err) + } + config.Protocol = test.requested + client, err := websocket.DialConfig(config) + if err != nil { + if !test.error { + t.Fatalf("test %d: didn't expect error: %v", i, err) + } else { + return + } + } + defer client.Close() + if test.error && err == nil { + t.Fatalf("test %d: expected an error", i) + } + + <-conn.ready + if got, expected := <-selectedProtocol, test.expected; got != expected { + t.Fatalf("test %d: unexpected protocol version: got=%s expected=%s", i, got, expected) + } + }() + } +} + +func TestIsWebSocketRequestWithStreamCloseProtocol(t *testing.T) { + tests := map[string]struct { + headers map[string]string + expected bool + }{ + "No headers returns false": { + headers: map[string]string{}, + expected: false, + }, + "Only connection upgrade header is false": { + headers: map[string]string{ + "Connection": "upgrade", + }, + expected: false, + }, + "Only websocket upgrade header is false": { + headers: map[string]string{ + "Upgrade": "websocket", + }, + expected: false, + }, + "Only websocket and connection upgrade headers is false": { + headers: map[string]string{ + "Connection": "upgrade", + "Upgrade": "websocket", + }, + expected: false, + }, + "Missing connection/upgrade header is false": { + headers: map[string]string{ + "Upgrade": "websocket", + WebSocketProtocolHeader: "v5.channel.k8s.io", + }, + expected: false, + }, + "Websocket connection upgrade headers with v5 protocol is true": { + headers: map[string]string{ + "Connection": "upgrade", + "Upgrade": "websocket", + WebSocketProtocolHeader: "v5.channel.k8s.io", + }, + expected: true, + }, + "Websocket connection upgrade headers with wrong case v5 protocol is false": { + headers: map[string]string{ + "Connection": "upgrade", + "Upgrade": "websocket", + WebSocketProtocolHeader: "v5.CHANNEL.k8s.io", // header value is case-sensitive + }, + expected: false, + }, + "Websocket connection upgrade headers with v4 protocol is false": { + headers: map[string]string{ + "Connection": "upgrade", + "Upgrade": "websocket", + WebSocketProtocolHeader: "v4.channel.k8s.io", + }, + expected: false, + }, + "Websocket connection upgrade headers with multiple protocols but missing v5 is false": { + headers: map[string]string{ + "Connection": "upgrade", + "Upgrade": "websocket", + WebSocketProtocolHeader: "v4.channel.k8s.io,v3.channel.k8s.io,v2.channel.k8s.io", + }, + expected: false, + }, + "Websocket connection upgrade headers with multiple protocols including v5 and spaces is true": { + headers: map[string]string{ + "Connection": "upgrade", + "Upgrade": "websocket", + WebSocketProtocolHeader: "v5.channel.k8s.io, v4.channel.k8s.io", + }, + expected: true, + }, + "Websocket connection upgrade headers with multiple protocols out of order including v5 and spaces is true": { + headers: map[string]string{ + "Connection": "upgrade", + "Upgrade": "websocket", + WebSocketProtocolHeader: "v4.channel.k8s.io, v5.channel.k8s.io, v3.channel.k8s.io", + }, + expected: true, + }, + + "Websocket connection upgrade headers key is case-insensitive": { + headers: map[string]string{ + "Connection": "upgrade", + "Upgrade": "websocket", + "sec-websocket-protocol": "v4.channel.k8s.io, v5.channel.k8s.io, v3.channel.k8s.io", + }, + expected: true, + }, + } + + for name, test := range tests { + req, err := http.NewRequest("GET", "http://www.example.com/", nil) + require.NoError(t, err) + for key, value := range test.headers { + req.Header.Add(key, value) + } + actual := IsWebSocketRequestWithStreamCloseProtocol(req) + assert.Equal(t, test.expected, actual, "%s: expected (%t), got (%t)", name, test.expected, actual) + } +} + +func TestProtocolSupportsStreamClose(t *testing.T) { + tests := map[string]struct { + protocol string + expected bool + }{ + "empty protocol returns false": { + protocol: "", + expected: false, + }, + "not binary protocol returns false": { + protocol: "base64.channel.k8s.io", + expected: false, + }, + "V1 protocol returns false": { + protocol: "channel.k8s.io", + expected: false, + }, + "V4 protocol returns false": { + protocol: "v4.channel.k8s.io", + expected: false, + }, + "V5 protocol returns true": { + protocol: "v5.channel.k8s.io", + expected: true, + }, + "V5 protocol wrong case returns false": { + protocol: "V5.channel.K8S.io", + expected: false, + }, + } + + for name, test := range tests { + actual := protocolSupportsStreamClose(test.protocol) + assert.Equal(t, test.expected, actual, + "%s: expected (%t), got (%t)", name, test.expected, actual) + } +} diff --git a/pkg/cri/streaming/wsstream/stream.go b/pkg/cri/streaming/wsstream/stream.go new file mode 100644 index 0000000000..ba7e6a519a --- /dev/null +++ b/pkg/cri/streaming/wsstream/stream.go @@ -0,0 +1,177 @@ +/* +Copyright 2015 The Kubernetes 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 wsstream + +import ( + "encoding/base64" + "io" + "net/http" + "sync" + "time" + + "golang.org/x/net/websocket" + + "k8s.io/apimachinery/pkg/util/runtime" +) + +// The WebSocket subprotocol "binary.k8s.io" will only send messages to the +// client and ignore messages sent to the server. The received messages are +// the exact bytes written to the stream. Zero byte messages are possible. +const binaryWebSocketProtocol = "binary.k8s.io" + +// The WebSocket subprotocol "base64.binary.k8s.io" will only send messages to the +// client and ignore messages sent to the server. The received messages are +// a base64 version of the bytes written to the stream. Zero byte messages are +// possible. +const base64BinaryWebSocketProtocol = "base64.binary.k8s.io" + +// ReaderProtocolConfig describes a websocket subprotocol with one stream. +type ReaderProtocolConfig struct { + Binary bool +} + +// NewDefaultReaderProtocols returns a stream protocol map with the +// subprotocols "", "channel.k8s.io", "base64.channel.k8s.io". +func NewDefaultReaderProtocols() map[string]ReaderProtocolConfig { + return map[string]ReaderProtocolConfig{ + "": {Binary: true}, + binaryWebSocketProtocol: {Binary: true}, + base64BinaryWebSocketProtocol: {Binary: false}, + } +} + +// Reader supports returning an arbitrary byte stream over a websocket channel. +type Reader struct { + err chan error + r io.Reader + ping bool + timeout time.Duration + protocols map[string]ReaderProtocolConfig + selectedProtocol string + + handleCrash func(additionalHandlers ...func(interface{})) // overridable for testing +} + +// NewReader creates a WebSocket pipe that will copy the contents of r to a provided +// WebSocket connection. If ping is true, a zero length message will be sent to the client +// before the stream begins reading. +// +// The protocols parameter maps subprotocol names to StreamProtocols. The empty string +// subprotocol name is used if websocket.Config.Protocol is empty. +func NewReader(r io.Reader, ping bool, protocols map[string]ReaderProtocolConfig) *Reader { + return &Reader{ + r: r, + err: make(chan error), + ping: ping, + protocols: protocols, + handleCrash: runtime.HandleCrash, + } +} + +// SetIdleTimeout sets the interval for both reads and writes before timeout. If not specified, +// there is no timeout on the reader. +func (r *Reader) SetIdleTimeout(duration time.Duration) { + r.timeout = duration +} + +func (r *Reader) handshake(config *websocket.Config, req *http.Request) error { + supportedProtocols := make([]string, 0, len(r.protocols)) + for p := range r.protocols { + supportedProtocols = append(supportedProtocols, p) + } + return handshake(config, req, supportedProtocols) +} + +// Copy the reader to the response. The created WebSocket is closed after this +// method completes. +func (r *Reader) Copy(w http.ResponseWriter, req *http.Request) error { + go func() { + defer r.handleCrash() + websocket.Server{Handshake: r.handshake, Handler: r.handle}.ServeHTTP(w, req) + }() + return <-r.err +} + +// handle implements a WebSocket handler. +func (r *Reader) handle(ws *websocket.Conn) { + // Close the connection when the client requests it, or when we finish streaming, whichever happens first + closeConnOnce := &sync.Once{} + closeConn := func() { + closeConnOnce.Do(func() { + ws.Close() + }) + } + + negotiated := ws.Config().Protocol + r.selectedProtocol = negotiated[0] + defer close(r.err) + defer closeConn() + + go func() { + defer runtime.HandleCrash() + // This blocks until the connection is closed. + // Client should not send anything. + IgnoreReceives(ws, r.timeout) + // Once the client closes, we should also close + closeConn() + }() + + r.err <- messageCopy(ws, r.r, !r.protocols[r.selectedProtocol].Binary, r.ping, r.timeout) +} + +func resetTimeout(ws *websocket.Conn, timeout time.Duration) { + if timeout > 0 { + ws.SetDeadline(time.Now().Add(timeout)) + } +} + +func messageCopy(ws *websocket.Conn, r io.Reader, base64Encode, ping bool, timeout time.Duration) error { + buf := make([]byte, 2048) + if ping { + resetTimeout(ws, timeout) + if base64Encode { + if err := websocket.Message.Send(ws, ""); err != nil { + return err + } + } else { + if err := websocket.Message.Send(ws, []byte{}); err != nil { + return err + } + } + } + for { + resetTimeout(ws, timeout) + n, err := r.Read(buf) + if err != nil { + if err == io.EOF { + return nil + } + return err + } + if n > 0 { + if base64Encode { + if err := websocket.Message.Send(ws, base64.StdEncoding.EncodeToString(buf[:n])); err != nil { + return err + } + } else { + if err := websocket.Message.Send(ws, buf[:n]); err != nil { + return err + } + } + } + } +} diff --git a/pkg/cri/streaming/wsstream/stream_test.go b/pkg/cri/streaming/wsstream/stream_test.go new file mode 100644 index 0000000000..226bc3f210 --- /dev/null +++ b/pkg/cri/streaming/wsstream/stream_test.go @@ -0,0 +1,293 @@ +/* +Copyright 2015 The Kubernetes 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 wsstream + +import ( + "bytes" + "encoding/base64" + "fmt" + "io" + "net/http" + "reflect" + "strings" + "testing" + "time" + + "golang.org/x/net/websocket" +) + +func TestStream(t *testing.T) { + input := "some random text" + r := NewReader(bytes.NewBuffer([]byte(input)), true, NewDefaultReaderProtocols()) + r.SetIdleTimeout(time.Second) + data, err := readWebSocket(r, t, nil) + if !reflect.DeepEqual(data, []byte(input)) { + t.Errorf("unexpected server read: %v", data) + } + if err != nil { + t.Fatal(err) + } +} + +func TestStreamPing(t *testing.T) { + input := "some random text" + r := NewReader(bytes.NewBuffer([]byte(input)), true, NewDefaultReaderProtocols()) + r.SetIdleTimeout(time.Second) + err := expectWebSocketFrames(r, t, nil, [][]byte{ + {}, + []byte(input), + }) + if err != nil { + t.Fatal(err) + } +} + +func TestStreamBase64(t *testing.T) { + input := "some random text" + encoded := base64.StdEncoding.EncodeToString([]byte(input)) + r := NewReader(bytes.NewBuffer([]byte(input)), true, NewDefaultReaderProtocols()) + data, err := readWebSocket(r, t, nil, "base64.binary.k8s.io") + if !reflect.DeepEqual(data, []byte(encoded)) { + t.Errorf("unexpected server read: %v\n%v", data, []byte(encoded)) + } + if err != nil { + t.Fatal(err) + } +} + +func TestStreamVersionedBase64(t *testing.T) { + input := "some random text" + encoded := base64.StdEncoding.EncodeToString([]byte(input)) + r := NewReader(bytes.NewBuffer([]byte(input)), true, map[string]ReaderProtocolConfig{ + "": {Binary: true}, + "binary.k8s.io": {Binary: true}, + "base64.binary.k8s.io": {Binary: false}, + "v1.binary.k8s.io": {Binary: true}, + "v1.base64.binary.k8s.io": {Binary: false}, + "v2.binary.k8s.io": {Binary: true}, + "v2.base64.binary.k8s.io": {Binary: false}, + }) + data, err := readWebSocket(r, t, nil, "v2.base64.binary.k8s.io") + if !reflect.DeepEqual(data, []byte(encoded)) { + t.Errorf("unexpected server read: %v\n%v", data, []byte(encoded)) + } + if err != nil { + t.Fatal(err) + } +} + +func TestStreamVersionedCopy(t *testing.T) { + for i, test := range versionTests() { + func() { + supportedProtocols := map[string]ReaderProtocolConfig{} + for p, binary := range test.supported { + supportedProtocols[p] = ReaderProtocolConfig{ + Binary: binary, + } + } + input := "some random text" + r := NewReader(bytes.NewBuffer([]byte(input)), true, supportedProtocols) + s, addr := newServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + err := r.Copy(w, req) + if err != nil { + w.WriteHeader(503) + } + })) + defer s.Close() + + config, err := websocket.NewConfig("ws://"+addr, "http://localhost/") + if err != nil { + t.Error(err) + return + } + config.Protocol = test.requested + client, err := websocket.DialConfig(config) + if err != nil { + if !test.error { + t.Errorf("test %d: didn't expect error: %v", i, err) + } + return + } + defer client.Close() + if test.error && err == nil { + t.Errorf("test %d: expected an error", i) + return + } + + <-r.err + if got, expected := r.selectedProtocol, test.expected; got != expected { + t.Errorf("test %d: unexpected protocol version: got=%s expected=%s", i, got, expected) + } + }() + } +} + +func TestStreamError(t *testing.T) { + input := "some random text" + errs := &errorReader{ + reads: [][]byte{ + []byte("some random"), + []byte(" text"), + }, + err: fmt.Errorf("bad read"), + } + r := NewReader(errs, false, NewDefaultReaderProtocols()) + + data, err := readWebSocket(r, t, nil) + if !reflect.DeepEqual(data, []byte(input)) { + t.Errorf("unexpected server read: %v", data) + } + if err == nil || err.Error() != "bad read" { + t.Fatal(err) + } +} + +func TestStreamSurvivesPanic(t *testing.T) { + input := "some random text" + errs := &errorReader{ + reads: [][]byte{ + []byte("some random"), + []byte(" text"), + }, + panicMessage: "bad read", + } + r := NewReader(errs, false, NewDefaultReaderProtocols()) + + // do not call runtime.HandleCrash() in handler. Otherwise, the tests are interrupted. + r.handleCrash = func(additionalHandlers ...func(interface{})) { recover() } + + data, err := readWebSocket(r, t, nil) + if !reflect.DeepEqual(data, []byte(input)) { + t.Errorf("unexpected server read: %v", data) + } + if err != nil { + t.Fatal(err) + } +} + +func TestStreamClosedDuringRead(t *testing.T) { + for i := 0; i < 25; i++ { + ch := make(chan struct{}) + input := "some random text" + errs := &errorReader{ + reads: [][]byte{ + []byte("some random"), + []byte(" text"), + }, + err: fmt.Errorf("stuff"), + pause: ch, + } + r := NewReader(errs, false, NewDefaultReaderProtocols()) + + data, err := readWebSocket(r, t, func(c *websocket.Conn) { + c.Close() + close(ch) + }) + // verify that the data returned by the server on an early close always has a specific error + if err == nil || !strings.Contains(err.Error(), "use of closed network connection") { + t.Fatal(err) + } + // verify that the data returned is a strict subset of the input + if !bytes.HasPrefix([]byte(input), data) && len(data) != 0 { + t.Fatalf("unexpected server read: %q", string(data)) + } + } +} + +type errorReader struct { + reads [][]byte + err error + panicMessage string + pause chan struct{} +} + +func (r *errorReader) Read(p []byte) (int, error) { + if len(r.reads) == 0 { + if r.pause != nil { + <-r.pause + } + if len(r.panicMessage) != 0 { + panic(r.panicMessage) + } + return 0, r.err + } + next := r.reads[0] + r.reads = r.reads[1:] + copy(p, next) + return len(next), nil +} + +func readWebSocket(r *Reader, t *testing.T, fn func(*websocket.Conn), protocols ...string) ([]byte, error) { + errCh := make(chan error, 1) + s, addr := newServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + errCh <- r.Copy(w, req) + })) + defer s.Close() + + config, _ := websocket.NewConfig("ws://"+addr, "http://"+addr) + config.Protocol = protocols + client, err := websocket.DialConfig(config) + if err != nil { + return nil, err + } + defer client.Close() + + if fn != nil { + fn(client) + } + + data, err := io.ReadAll(client) + if err != nil { + return data, err + } + return data, <-errCh +} + +func expectWebSocketFrames(r *Reader, t *testing.T, fn func(*websocket.Conn), frames [][]byte, protocols ...string) error { + errCh := make(chan error, 1) + s, addr := newServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + errCh <- r.Copy(w, req) + })) + defer s.Close() + + config, _ := websocket.NewConfig("ws://"+addr, "http://"+addr) + config.Protocol = protocols + ws, err := websocket.DialConfig(config) + if err != nil { + return err + } + defer ws.Close() + + if fn != nil { + fn(ws) + } + + for i := range frames { + var data []byte + if err := websocket.Message.Receive(ws, &data); err != nil { + return err + } + if !reflect.DeepEqual(frames[i], data) { + return fmt.Errorf("frame %d did not match expected: %v", data, err) + } + } + var data []byte + if err := websocket.Message.Receive(ws, &data); err != io.EOF { + return fmt.Errorf("expected no more frames: %v (%v)", err, data) + } + return <-errCh +} From 0a85d476a1c2dd9b0ee562a8a3e295744a6f9685 Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Sat, 10 Aug 2024 20:54:12 -0400 Subject: [PATCH 17/27] Fix up some constant references Signed-off-by: Davanum Srinivas --- pkg/cri/streaming/wsstream/conn.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pkg/cri/streaming/wsstream/conn.go b/pkg/cri/streaming/wsstream/conn.go index 2e477fee2a..9f8ff4f695 100644 --- a/pkg/cri/streaming/wsstream/conn.go +++ b/pkg/cri/streaming/wsstream/conn.go @@ -27,8 +27,6 @@ import ( "golang.org/x/net/websocket" "k8s.io/apimachinery/pkg/util/httpstream" - "k8s.io/apimachinery/pkg/util/portforward" - "k8s.io/apimachinery/pkg/util/remotecommand" "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/klog/v2" ) @@ -312,17 +310,24 @@ func (conn *Conn) Close() error { return conn.closeNonThreadSafe() } +const ( + StreamProtocolV5Name = "v5.channel.k8s.io" + WebsocketsSPDYTunnelingPrefix = "SPDY/3.1+" + KubernetesSuffix = ".k8s.io" + StreamClose = 255 +) + // protocolSupportsStreamClose returns true if the passed protocol // supports the stream close signal (currently only V5 remotecommand); // false otherwise. func protocolSupportsStreamClose(protocol string) bool { - return protocol == remotecommand.StreamProtocolV5Name + return protocol == StreamProtocolV5Name } // protocolSupportsWebsocketTunneling returns true if the passed protocol // is a tunneled Kubernetes spdy protocol; false otherwise. func protocolSupportsWebsocketTunneling(protocol string) bool { - return strings.HasPrefix(protocol, portforward.WebsocketsSPDYTunnelingPrefix) && strings.HasSuffix(protocol, portforward.KubernetesSuffix) + return strings.HasPrefix(protocol, WebsocketsSPDYTunnelingPrefix) && strings.HasSuffix(protocol, KubernetesSuffix) } // handle implements a websocket handler. @@ -343,7 +348,7 @@ func (conn *Conn) handle(ws *websocket.Conn) { if len(data) == 0 { continue } - if supportsStreamClose && data[0] == remotecommand.StreamClose { + if supportsStreamClose && data[0] == StreamClose { if len(data) != 2 { klog.Errorf("Single channel byte should follow stream close signal. Got %d bytes", len(data)-1) break From 6f3252733c4f5dd13048096fdf08799ad5733893 Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Sat, 10 Aug 2024 21:03:06 -0400 Subject: [PATCH 18/27] switch over references to the new package Signed-off-by: Davanum Srinivas --- pkg/cri/streaming/portforward/portforward.go | 3 ++- pkg/cri/streaming/portforward/websocket.go | 3 ++- pkg/cri/streaming/remotecommand/httpstream.go | 3 ++- pkg/cri/streaming/remotecommand/websocket.go | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/cri/streaming/portforward/portforward.go b/pkg/cri/streaming/portforward/portforward.go index 89a39ea66d..4ab767fc0b 100644 --- a/pkg/cri/streaming/portforward/portforward.go +++ b/pkg/cri/streaming/portforward/portforward.go @@ -39,7 +39,8 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apiserver/pkg/util/wsstream" + + "github.com/containerd/containerd/pkg/cri/streaming/wsstream" ) // PortForwarder knows how to forward content from a data stream to/from a port diff --git a/pkg/cri/streaming/portforward/websocket.go b/pkg/cri/streaming/portforward/websocket.go index 1b1c0151ae..2def03b45b 100644 --- a/pkg/cri/streaming/portforward/websocket.go +++ b/pkg/cri/streaming/portforward/websocket.go @@ -48,7 +48,8 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apiserver/pkg/server/httplog" - "k8s.io/apiserver/pkg/util/wsstream" + + "github.com/containerd/containerd/pkg/cri/streaming/wsstream" ) const ( diff --git a/pkg/cri/streaming/remotecommand/httpstream.go b/pkg/cri/streaming/remotecommand/httpstream.go index 9177fa794d..e586d94f61 100644 --- a/pkg/cri/streaming/remotecommand/httpstream.go +++ b/pkg/cri/streaming/remotecommand/httpstream.go @@ -48,10 +48,11 @@ import ( "k8s.io/apimachinery/pkg/util/httpstream/spdy" remotecommandconsts "k8s.io/apimachinery/pkg/util/remotecommand" "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apiserver/pkg/util/wsstream" "k8s.io/client-go/tools/remotecommand" "k8s.io/klog/v2" + + "github.com/containerd/containerd/pkg/cri/streaming/wsstream" ) // Options contains details about which streams are required for diff --git a/pkg/cri/streaming/remotecommand/websocket.go b/pkg/cri/streaming/remotecommand/websocket.go index 99c0af7c3b..ee324ae52a 100644 --- a/pkg/cri/streaming/remotecommand/websocket.go +++ b/pkg/cri/streaming/remotecommand/websocket.go @@ -39,7 +39,8 @@ import ( "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apiserver/pkg/server/httplog" - "k8s.io/apiserver/pkg/util/wsstream" + + "github.com/containerd/containerd/pkg/cri/streaming/wsstream" ) const ( From 226f93d928723e1cc14d07d84f774e77d4d1bb61 Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Sat, 10 Aug 2024 21:08:33 -0400 Subject: [PATCH 19/27] Add copyright headers Signed-off-by: Davanum Srinivas --- pkg/cri/streaming/wsstream/conn.go | 16 ++++++++++++++++ pkg/cri/streaming/wsstream/conn_test.go | 16 ++++++++++++++++ pkg/cri/streaming/wsstream/stream.go | 16 ++++++++++++++++ pkg/cri/streaming/wsstream/stream_test.go | 16 ++++++++++++++++ 4 files changed, 64 insertions(+) diff --git a/pkg/cri/streaming/wsstream/conn.go b/pkg/cri/streaming/wsstream/conn.go index 9f8ff4f695..2e459e98d0 100644 --- a/pkg/cri/streaming/wsstream/conn.go +++ b/pkg/cri/streaming/wsstream/conn.go @@ -1,3 +1,19 @@ +/* + 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. +*/ + /* Copyright 2015 The Kubernetes Authors. diff --git a/pkg/cri/streaming/wsstream/conn_test.go b/pkg/cri/streaming/wsstream/conn_test.go index e4a88a1a8c..358ae8200d 100644 --- a/pkg/cri/streaming/wsstream/conn_test.go +++ b/pkg/cri/streaming/wsstream/conn_test.go @@ -1,3 +1,19 @@ +/* + 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. +*/ + /* Copyright 2015 The Kubernetes Authors. diff --git a/pkg/cri/streaming/wsstream/stream.go b/pkg/cri/streaming/wsstream/stream.go index ba7e6a519a..49a661e48f 100644 --- a/pkg/cri/streaming/wsstream/stream.go +++ b/pkg/cri/streaming/wsstream/stream.go @@ -1,3 +1,19 @@ +/* + 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. +*/ + /* Copyright 2015 The Kubernetes Authors. diff --git a/pkg/cri/streaming/wsstream/stream_test.go b/pkg/cri/streaming/wsstream/stream_test.go index 226bc3f210..2594d61062 100644 --- a/pkg/cri/streaming/wsstream/stream_test.go +++ b/pkg/cri/streaming/wsstream/stream_test.go @@ -1,3 +1,19 @@ +/* + 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. +*/ + /* Copyright 2015 The Kubernetes Authors. From 1c431dc6ff7bb928ad3656c318a24548026f560f Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Sat, 10 Aug 2024 21:16:59 -0400 Subject: [PATCH 20/27] Run go mod tidy Signed-off-by: Davanum Srinivas --- go.mod | 2 +- .../apiserver/pkg/util/wsstream/conn.go | 350 ------------------ .../k8s.io/apiserver/pkg/util/wsstream/doc.go | 21 -- .../apiserver/pkg/util/wsstream/stream.go | 177 --------- vendor/modules.txt | 1 - 5 files changed, 1 insertion(+), 550 deletions(-) delete mode 100644 vendor/k8s.io/apiserver/pkg/util/wsstream/conn.go delete mode 100644 vendor/k8s.io/apiserver/pkg/util/wsstream/doc.go delete mode 100644 vendor/k8s.io/apiserver/pkg/util/wsstream/stream.go diff --git a/go.mod b/go.mod index 0b92b6d2a3..a12092d072 100644 --- a/go.mod +++ b/go.mod @@ -71,6 +71,7 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 go.opentelemetry.io/otel/sdk v1.21.0 go.opentelemetry.io/otel/trace v1.21.0 + golang.org/x/net v0.23.0 golang.org/x/sync v0.5.0 golang.org/x/sys v0.18.0 google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 @@ -133,7 +134,6 @@ require ( go.opentelemetry.io/proto/otlp v1.0.0 // indirect golang.org/x/crypto v0.21.0 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.23.0 // indirect golang.org/x/oauth2 v0.11.0 // indirect golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect diff --git a/vendor/k8s.io/apiserver/pkg/util/wsstream/conn.go b/vendor/k8s.io/apiserver/pkg/util/wsstream/conn.go deleted file mode 100644 index 09f54a49c7..0000000000 --- a/vendor/k8s.io/apiserver/pkg/util/wsstream/conn.go +++ /dev/null @@ -1,350 +0,0 @@ -/* -Copyright 2015 The Kubernetes 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 wsstream - -import ( - "encoding/base64" - "fmt" - "io" - "net/http" - "regexp" - "strings" - "time" - - "golang.org/x/net/websocket" - "k8s.io/klog/v2" - - "k8s.io/apimachinery/pkg/util/runtime" -) - -// The Websocket subprotocol "channel.k8s.io" prepends each binary message with a byte indicating -// the channel number (zero indexed) the message was sent on. Messages in both directions should -// prefix their messages with this channel byte. When used for remote execution, the channel numbers -// are by convention defined to match the POSIX file-descriptors assigned to STDIN, STDOUT, and STDERR -// (0, 1, and 2). No other conversion is performed on the raw subprotocol - writes are sent as they -// are received by the server. -// -// Example client session: -// -// CONNECT http://server.com with subprotocol "channel.k8s.io" -// WRITE []byte{0, 102, 111, 111, 10} # send "foo\n" on channel 0 (STDIN) -// READ []byte{1, 10} # receive "\n" on channel 1 (STDOUT) -// CLOSE -const ChannelWebSocketProtocol = "channel.k8s.io" - -// The Websocket subprotocol "base64.channel.k8s.io" base64 encodes each message with a character -// indicating the channel number (zero indexed) the message was sent on. Messages in both directions -// should prefix their messages with this channel char. When used for remote execution, the channel -// numbers are by convention defined to match the POSIX file-descriptors assigned to STDIN, STDOUT, -// and STDERR ('0', '1', and '2'). The data received on the server is base64 decoded (and must be -// be valid) and data written by the server to the client is base64 encoded. -// -// Example client session: -// -// CONNECT http://server.com with subprotocol "base64.channel.k8s.io" -// WRITE []byte{48, 90, 109, 57, 118, 67, 103, 111, 61} # send "foo\n" (base64: "Zm9vCgo=") on channel '0' (STDIN) -// READ []byte{49, 67, 103, 61, 61} # receive "\n" (base64: "Cg==") on channel '1' (STDOUT) -// CLOSE -const Base64ChannelWebSocketProtocol = "base64.channel.k8s.io" - -type codecType int - -const ( - rawCodec codecType = iota - base64Codec -) - -type ChannelType int - -const ( - IgnoreChannel ChannelType = iota - ReadChannel - WriteChannel - ReadWriteChannel -) - -var ( - // connectionUpgradeRegex matches any Connection header value that includes upgrade - connectionUpgradeRegex = regexp.MustCompile("(^|.*,\\s*)upgrade($|\\s*,)") -) - -// IsWebSocketRequest returns true if the incoming request contains connection upgrade headers -// for WebSockets. -func IsWebSocketRequest(req *http.Request) bool { - if !strings.EqualFold(req.Header.Get("Upgrade"), "websocket") { - return false - } - return connectionUpgradeRegex.MatchString(strings.ToLower(req.Header.Get("Connection"))) -} - -// IgnoreReceives reads from a WebSocket until it is closed, then returns. If timeout is set, the -// read and write deadlines are pushed every time a new message is received. -func IgnoreReceives(ws *websocket.Conn, timeout time.Duration) { - defer runtime.HandleCrash() - var data []byte - for { - resetTimeout(ws, timeout) - if err := websocket.Message.Receive(ws, &data); err != nil { - return - } - } -} - -// handshake ensures the provided user protocol matches one of the allowed protocols. It returns -// no error if no protocol is specified. -func handshake(config *websocket.Config, req *http.Request, allowed []string) error { - protocols := config.Protocol - if len(protocols) == 0 { - protocols = []string{""} - } - - for _, protocol := range protocols { - for _, allow := range allowed { - if allow == protocol { - config.Protocol = []string{protocol} - return nil - } - } - } - - return fmt.Errorf("requested protocol(s) are not supported: %v; supports %v", config.Protocol, allowed) -} - -// ChannelProtocolConfig describes a websocket subprotocol with channels. -type ChannelProtocolConfig struct { - Binary bool - Channels []ChannelType -} - -// NewDefaultChannelProtocols returns a channel protocol map with the -// subprotocols "", "channel.k8s.io", "base64.channel.k8s.io" and the given -// channels. -func NewDefaultChannelProtocols(channels []ChannelType) map[string]ChannelProtocolConfig { - return map[string]ChannelProtocolConfig{ - "": {Binary: true, Channels: channels}, - ChannelWebSocketProtocol: {Binary: true, Channels: channels}, - Base64ChannelWebSocketProtocol: {Binary: false, Channels: channels}, - } -} - -// Conn supports sending multiple binary channels over a websocket connection. -type Conn struct { - protocols map[string]ChannelProtocolConfig - selectedProtocol string - channels []*websocketChannel - codec codecType - ready chan struct{} - ws *websocket.Conn - timeout time.Duration -} - -// NewConn creates a WebSocket connection that supports a set of channels. Channels begin each -// web socket message with a single byte indicating the channel number (0-N). 255 is reserved for -// future use. The channel types for each channel are passed as an array, supporting the different -// duplex modes. Read and Write refer to whether the channel can be used as a Reader or Writer. -// -// The protocols parameter maps subprotocol names to ChannelProtocols. The empty string subprotocol -// name is used if websocket.Config.Protocol is empty. -func NewConn(protocols map[string]ChannelProtocolConfig) *Conn { - return &Conn{ - ready: make(chan struct{}), - protocols: protocols, - } -} - -// SetIdleTimeout sets the interval for both reads and writes before timeout. If not specified, -// there is no timeout on the connection. -func (conn *Conn) SetIdleTimeout(duration time.Duration) { - conn.timeout = duration -} - -// Open the connection and create channels for reading and writing. It returns -// the selected subprotocol, a slice of channels and an error. -func (conn *Conn) Open(w http.ResponseWriter, req *http.Request) (string, []io.ReadWriteCloser, error) { - go func() { - defer runtime.HandleCrash() - defer conn.Close() - websocket.Server{Handshake: conn.handshake, Handler: conn.handle}.ServeHTTP(w, req) - }() - <-conn.ready - rwc := make([]io.ReadWriteCloser, len(conn.channels)) - for i := range conn.channels { - rwc[i] = conn.channels[i] - } - return conn.selectedProtocol, rwc, nil -} - -func (conn *Conn) initialize(ws *websocket.Conn) { - negotiated := ws.Config().Protocol - conn.selectedProtocol = negotiated[0] - p := conn.protocols[conn.selectedProtocol] - if p.Binary { - conn.codec = rawCodec - } else { - conn.codec = base64Codec - } - conn.ws = ws - conn.channels = make([]*websocketChannel, len(p.Channels)) - for i, t := range p.Channels { - switch t { - case ReadChannel: - conn.channels[i] = newWebsocketChannel(conn, byte(i), true, false) - case WriteChannel: - conn.channels[i] = newWebsocketChannel(conn, byte(i), false, true) - case ReadWriteChannel: - conn.channels[i] = newWebsocketChannel(conn, byte(i), true, true) - case IgnoreChannel: - conn.channels[i] = newWebsocketChannel(conn, byte(i), false, false) - } - } - - close(conn.ready) -} - -func (conn *Conn) handshake(config *websocket.Config, req *http.Request) error { - supportedProtocols := make([]string, 0, len(conn.protocols)) - for p := range conn.protocols { - supportedProtocols = append(supportedProtocols, p) - } - return handshake(config, req, supportedProtocols) -} - -func (conn *Conn) resetTimeout() { - if conn.timeout > 0 { - conn.ws.SetDeadline(time.Now().Add(conn.timeout)) - } -} - -// Close is only valid after Open has been called -func (conn *Conn) Close() error { - <-conn.ready - for _, s := range conn.channels { - s.Close() - } - conn.ws.Close() - return nil -} - -// handle implements a websocket handler. -func (conn *Conn) handle(ws *websocket.Conn) { - defer conn.Close() - conn.initialize(ws) - - for { - conn.resetTimeout() - var data []byte - if err := websocket.Message.Receive(ws, &data); err != nil { - if err != io.EOF { - klog.Errorf("Error on socket receive: %v", err) - } - break - } - if len(data) == 0 { - continue - } - channel := data[0] - if conn.codec == base64Codec { - channel = channel - '0' - } - data = data[1:] - if int(channel) >= len(conn.channels) { - klog.V(6).Infof("Frame is targeted for a reader %d that is not valid, possible protocol error", channel) - continue - } - if _, err := conn.channels[channel].DataFromSocket(data); err != nil { - klog.Errorf("Unable to write frame to %d: %v\n%s", channel, err, string(data)) - continue - } - } -} - -// write multiplexes the specified channel onto the websocket -func (conn *Conn) write(num byte, data []byte) (int, error) { - conn.resetTimeout() - switch conn.codec { - case rawCodec: - frame := make([]byte, len(data)+1) - frame[0] = num - copy(frame[1:], data) - if err := websocket.Message.Send(conn.ws, frame); err != nil { - return 0, err - } - case base64Codec: - frame := string('0'+num) + base64.StdEncoding.EncodeToString(data) - if err := websocket.Message.Send(conn.ws, frame); err != nil { - return 0, err - } - } - return len(data), nil -} - -// websocketChannel represents a channel in a connection -type websocketChannel struct { - conn *Conn - num byte - r io.Reader - w io.WriteCloser - - read, write bool -} - -// newWebsocketChannel creates a pipe for writing to a websocket. Do not write to this pipe -// prior to the connection being opened. It may be no, half, or full duplex depending on -// read and write. -func newWebsocketChannel(conn *Conn, num byte, read, write bool) *websocketChannel { - r, w := io.Pipe() - return &websocketChannel{conn, num, r, w, read, write} -} - -func (p *websocketChannel) Write(data []byte) (int, error) { - if !p.write { - return len(data), nil - } - return p.conn.write(p.num, data) -} - -// DataFromSocket is invoked by the connection receiver to move data from the connection -// into a specific channel. -func (p *websocketChannel) DataFromSocket(data []byte) (int, error) { - if !p.read { - return len(data), nil - } - - switch p.conn.codec { - case rawCodec: - return p.w.Write(data) - case base64Codec: - dst := make([]byte, len(data)) - n, err := base64.StdEncoding.Decode(dst, data) - if err != nil { - return 0, err - } - return p.w.Write(dst[:n]) - } - return 0, nil -} - -func (p *websocketChannel) Read(data []byte) (int, error) { - if !p.read { - return 0, io.EOF - } - return p.r.Read(data) -} - -func (p *websocketChannel) Close() error { - return p.w.Close() -} diff --git a/vendor/k8s.io/apiserver/pkg/util/wsstream/doc.go b/vendor/k8s.io/apiserver/pkg/util/wsstream/doc.go deleted file mode 100644 index 694ce81d20..0000000000 --- a/vendor/k8s.io/apiserver/pkg/util/wsstream/doc.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright 2015 The Kubernetes 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 wsstream contains utilities for streaming content over WebSockets. -// The Conn type allows callers to multiplex multiple read/write channels over -// a single websocket. The Reader type allows an io.Reader to be copied over -// a websocket channel as binary content. -package wsstream // import "k8s.io/apiserver/pkg/util/wsstream" diff --git a/vendor/k8s.io/apiserver/pkg/util/wsstream/stream.go b/vendor/k8s.io/apiserver/pkg/util/wsstream/stream.go deleted file mode 100644 index ba7e6a519a..0000000000 --- a/vendor/k8s.io/apiserver/pkg/util/wsstream/stream.go +++ /dev/null @@ -1,177 +0,0 @@ -/* -Copyright 2015 The Kubernetes 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 wsstream - -import ( - "encoding/base64" - "io" - "net/http" - "sync" - "time" - - "golang.org/x/net/websocket" - - "k8s.io/apimachinery/pkg/util/runtime" -) - -// The WebSocket subprotocol "binary.k8s.io" will only send messages to the -// client and ignore messages sent to the server. The received messages are -// the exact bytes written to the stream. Zero byte messages are possible. -const binaryWebSocketProtocol = "binary.k8s.io" - -// The WebSocket subprotocol "base64.binary.k8s.io" will only send messages to the -// client and ignore messages sent to the server. The received messages are -// a base64 version of the bytes written to the stream. Zero byte messages are -// possible. -const base64BinaryWebSocketProtocol = "base64.binary.k8s.io" - -// ReaderProtocolConfig describes a websocket subprotocol with one stream. -type ReaderProtocolConfig struct { - Binary bool -} - -// NewDefaultReaderProtocols returns a stream protocol map with the -// subprotocols "", "channel.k8s.io", "base64.channel.k8s.io". -func NewDefaultReaderProtocols() map[string]ReaderProtocolConfig { - return map[string]ReaderProtocolConfig{ - "": {Binary: true}, - binaryWebSocketProtocol: {Binary: true}, - base64BinaryWebSocketProtocol: {Binary: false}, - } -} - -// Reader supports returning an arbitrary byte stream over a websocket channel. -type Reader struct { - err chan error - r io.Reader - ping bool - timeout time.Duration - protocols map[string]ReaderProtocolConfig - selectedProtocol string - - handleCrash func(additionalHandlers ...func(interface{})) // overridable for testing -} - -// NewReader creates a WebSocket pipe that will copy the contents of r to a provided -// WebSocket connection. If ping is true, a zero length message will be sent to the client -// before the stream begins reading. -// -// The protocols parameter maps subprotocol names to StreamProtocols. The empty string -// subprotocol name is used if websocket.Config.Protocol is empty. -func NewReader(r io.Reader, ping bool, protocols map[string]ReaderProtocolConfig) *Reader { - return &Reader{ - r: r, - err: make(chan error), - ping: ping, - protocols: protocols, - handleCrash: runtime.HandleCrash, - } -} - -// SetIdleTimeout sets the interval for both reads and writes before timeout. If not specified, -// there is no timeout on the reader. -func (r *Reader) SetIdleTimeout(duration time.Duration) { - r.timeout = duration -} - -func (r *Reader) handshake(config *websocket.Config, req *http.Request) error { - supportedProtocols := make([]string, 0, len(r.protocols)) - for p := range r.protocols { - supportedProtocols = append(supportedProtocols, p) - } - return handshake(config, req, supportedProtocols) -} - -// Copy the reader to the response. The created WebSocket is closed after this -// method completes. -func (r *Reader) Copy(w http.ResponseWriter, req *http.Request) error { - go func() { - defer r.handleCrash() - websocket.Server{Handshake: r.handshake, Handler: r.handle}.ServeHTTP(w, req) - }() - return <-r.err -} - -// handle implements a WebSocket handler. -func (r *Reader) handle(ws *websocket.Conn) { - // Close the connection when the client requests it, or when we finish streaming, whichever happens first - closeConnOnce := &sync.Once{} - closeConn := func() { - closeConnOnce.Do(func() { - ws.Close() - }) - } - - negotiated := ws.Config().Protocol - r.selectedProtocol = negotiated[0] - defer close(r.err) - defer closeConn() - - go func() { - defer runtime.HandleCrash() - // This blocks until the connection is closed. - // Client should not send anything. - IgnoreReceives(ws, r.timeout) - // Once the client closes, we should also close - closeConn() - }() - - r.err <- messageCopy(ws, r.r, !r.protocols[r.selectedProtocol].Binary, r.ping, r.timeout) -} - -func resetTimeout(ws *websocket.Conn, timeout time.Duration) { - if timeout > 0 { - ws.SetDeadline(time.Now().Add(timeout)) - } -} - -func messageCopy(ws *websocket.Conn, r io.Reader, base64Encode, ping bool, timeout time.Duration) error { - buf := make([]byte, 2048) - if ping { - resetTimeout(ws, timeout) - if base64Encode { - if err := websocket.Message.Send(ws, ""); err != nil { - return err - } - } else { - if err := websocket.Message.Send(ws, []byte{}); err != nil { - return err - } - } - } - for { - resetTimeout(ws, timeout) - n, err := r.Read(buf) - if err != nil { - if err == io.EOF { - return nil - } - return err - } - if n > 0 { - if base64Encode { - if err := websocket.Message.Send(ws, base64.StdEncoding.EncodeToString(buf[:n])); err != nil { - return err - } - } else { - if err := websocket.Message.Send(ws, buf[:n]); err != nil { - return err - } - } - } - } -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 61d5ec0205..959f6dafa3 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -778,7 +778,6 @@ k8s.io/apiserver/pkg/endpoints/responsewriter k8s.io/apiserver/pkg/features k8s.io/apiserver/pkg/server/httplog k8s.io/apiserver/pkg/util/feature -k8s.io/apiserver/pkg/util/wsstream # k8s.io/client-go v0.26.2 ## explicit; go 1.19 k8s.io/client-go/pkg/apis/clientauthentication From 59815fa44be5f20954e4acf02c91590f78c053f6 Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Sat, 10 Aug 2024 21:30:37 -0400 Subject: [PATCH 21/27] golangci-lint should only look for problems in new code Signed-off-by: Davanum Srinivas --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b9a3376c97..4342085294 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,7 @@ jobs: - uses: ./.github/actions/install-go - uses: golangci/golangci-lint-action@v4 with: + only-new-issues: true version: v1.56.1 skip-cache: true args: --timeout=8m From 9269d97b1d3c08afba0914028a1ae73220aaa6e4 Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Mon, 12 Aug 2024 13:54:46 -0400 Subject: [PATCH 22/27] hide wsstream under internal/ to prevent external use Signed-off-by: Davanum Srinivas --- pkg/cri/streaming/{ => internal}/wsstream/conn.go | 0 pkg/cri/streaming/{ => internal}/wsstream/conn_test.go | 0 pkg/cri/streaming/{ => internal}/wsstream/stream.go | 0 pkg/cri/streaming/{ => internal}/wsstream/stream_test.go | 0 pkg/cri/streaming/portforward/portforward.go | 4 ++-- pkg/cri/streaming/portforward/websocket.go | 4 ++-- pkg/cri/streaming/remotecommand/httpstream.go | 4 ++-- pkg/cri/streaming/remotecommand/websocket.go | 4 ++-- 8 files changed, 8 insertions(+), 8 deletions(-) rename pkg/cri/streaming/{ => internal}/wsstream/conn.go (100%) rename pkg/cri/streaming/{ => internal}/wsstream/conn_test.go (100%) rename pkg/cri/streaming/{ => internal}/wsstream/stream.go (100%) rename pkg/cri/streaming/{ => internal}/wsstream/stream_test.go (100%) diff --git a/pkg/cri/streaming/wsstream/conn.go b/pkg/cri/streaming/internal/wsstream/conn.go similarity index 100% rename from pkg/cri/streaming/wsstream/conn.go rename to pkg/cri/streaming/internal/wsstream/conn.go diff --git a/pkg/cri/streaming/wsstream/conn_test.go b/pkg/cri/streaming/internal/wsstream/conn_test.go similarity index 100% rename from pkg/cri/streaming/wsstream/conn_test.go rename to pkg/cri/streaming/internal/wsstream/conn_test.go diff --git a/pkg/cri/streaming/wsstream/stream.go b/pkg/cri/streaming/internal/wsstream/stream.go similarity index 100% rename from pkg/cri/streaming/wsstream/stream.go rename to pkg/cri/streaming/internal/wsstream/stream.go diff --git a/pkg/cri/streaming/wsstream/stream_test.go b/pkg/cri/streaming/internal/wsstream/stream_test.go similarity index 100% rename from pkg/cri/streaming/wsstream/stream_test.go rename to pkg/cri/streaming/internal/wsstream/stream_test.go diff --git a/pkg/cri/streaming/portforward/portforward.go b/pkg/cri/streaming/portforward/portforward.go index 4ab767fc0b..e6326c5d8a 100644 --- a/pkg/cri/streaming/portforward/portforward.go +++ b/pkg/cri/streaming/portforward/portforward.go @@ -37,10 +37,10 @@ import ( "net/http" "time" + "github.com/containerd/containerd/pkg/cri/streaming/internal/wsstream" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/runtime" - - "github.com/containerd/containerd/pkg/cri/streaming/wsstream" ) // PortForwarder knows how to forward content from a data stream to/from a port diff --git a/pkg/cri/streaming/portforward/websocket.go b/pkg/cri/streaming/portforward/websocket.go index 2def03b45b..6948f0579f 100644 --- a/pkg/cri/streaming/portforward/websocket.go +++ b/pkg/cri/streaming/portforward/websocket.go @@ -42,14 +42,14 @@ import ( "sync" "time" + "github.com/containerd/containerd/pkg/cri/streaming/internal/wsstream" + "k8s.io/klog/v2" api "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apiserver/pkg/server/httplog" - - "github.com/containerd/containerd/pkg/cri/streaming/wsstream" ) const ( diff --git a/pkg/cri/streaming/remotecommand/httpstream.go b/pkg/cri/streaming/remotecommand/httpstream.go index e586d94f61..371ef4f40c 100644 --- a/pkg/cri/streaming/remotecommand/httpstream.go +++ b/pkg/cri/streaming/remotecommand/httpstream.go @@ -41,6 +41,8 @@ import ( "net/http" "time" + "github.com/containerd/containerd/pkg/cri/streaming/internal/wsstream" + api "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -51,8 +53,6 @@ import ( "k8s.io/client-go/tools/remotecommand" "k8s.io/klog/v2" - - "github.com/containerd/containerd/pkg/cri/streaming/wsstream" ) // Options contains details about which streams are required for diff --git a/pkg/cri/streaming/remotecommand/websocket.go b/pkg/cri/streaming/remotecommand/websocket.go index ee324ae52a..b87678f4a6 100644 --- a/pkg/cri/streaming/remotecommand/websocket.go +++ b/pkg/cri/streaming/remotecommand/websocket.go @@ -37,10 +37,10 @@ import ( "net/http" "time" + "github.com/containerd/containerd/pkg/cri/streaming/internal/wsstream" + "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apiserver/pkg/server/httplog" - - "github.com/containerd/containerd/pkg/cri/streaming/wsstream" ) const ( From 09ca004dee1fe7752a652f474661e23d7e3489d4 Mon Sep 17 00:00:00 2001 From: Shengjing Zhu Date: Thu, 8 Aug 2024 17:20:03 +0800 Subject: [PATCH 23/27] Fix TestNewBinaryIOCleanup on Go 1.23 and Linux 5.4 When running the test on Ubuntu focal (kernel version 5.4), the symlink for pidfd is anon_inode:[pidfd]. Updates: #10345 Signed-off-by: Shengjing Zhu (cherry picked from commit 8ef73c5dd50e89a7c2fdb92343e11be77660510a) Signed-off-by: Shengjing Zhu --- pkg/process/io_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/process/io_test.go b/pkg/process/io_test.go index 471bef9c26..ed7c9c1357 100644 --- a/pkg/process/io_test.go +++ b/pkg/process/io_test.go @@ -88,7 +88,8 @@ func descriptorCount(t *testing.T) int { continue } - if strings.HasPrefix(sym, "pidfd:") { + if strings.Contains(sym, "pidfd") { + // Either pidfd:[70517] or anon_inode:[pidfd] (on Linux 5.4) files = append(files[:i], files[i+1:]...) } } From 3b263d082cb09a2911150135a760508527557a0e Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Thu, 15 Aug 2024 09:59:46 +0530 Subject: [PATCH 24/27] add go1.23.0, drop go1.21.x - switch release and build jobs to 1.22.6 from 1.21.13 - add go1.23.0 to CI matrix - drop go1.21 from CI Signed-off-by: Akhil Mohan --- .github/actions/install-go/action.yml | 2 +- .github/workflows/api-release.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- BUILDING.md | 2 +- Vagrantfile | 2 +- contrib/Dockerfile.test | 2 +- contrib/fuzz/oss_fuzz_build.sh | 4 ++-- go.mod | 2 +- integration/client/go.mod | 2 +- script/setup/prepare_env_windows.ps1 | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/actions/install-go/action.yml b/.github/actions/install-go/action.yml index bdaed8a5be..d860cda4db 100644 --- a/.github/actions/install-go/action.yml +++ b/.github/actions/install-go/action.yml @@ -3,7 +3,7 @@ description: "Reusable action to install Go, so there is one place to bump Go ve inputs: go-version: required: true - default: "1.21.13" + default: "1.22.6" description: "Go version to install" runs: diff --git a/.github/workflows/api-release.yml b/.github/workflows/api-release.yml index 71e56ec670..d8135f9030 100644 --- a/.github/workflows/api-release.yml +++ b/.github/workflows/api-release.yml @@ -6,7 +6,7 @@ on: name: API Release env: - GO_VERSION: "1.21.13" + GO_VERSION: "1.22.6" permissions: # added using https://github.com/step-security/secure-workflows contents: read diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4342085294..ad9d7dd434 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -192,7 +192,7 @@ jobs: strategy: matrix: os: [ubuntu-20.04, actuated-arm64-4cpu-16gb, macos-12, windows-2019, windows-2022] - go-version: ["1.21.13", "1.22.6"] + go-version: ["1.22.6", "1.23.0"] steps: - uses: actions/checkout@v4 - uses: ./.github/actions/install-go diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e424e6c4f0..20eefa3048 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ on: name: Release env: - GO_VERSION: "1.21.13" + GO_VERSION: "1.22.6" permissions: # added using https://github.com/step-security/secure-workflows contents: read diff --git a/BUILDING.md b/BUILDING.md index 1a688169bf..911e48742c 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -15,7 +15,7 @@ This doc includes: To build the `containerd` daemon, and the `ctr` simple test client, the following build system dependencies are required: -* Go 1.21.x or above +* Go 1.22.x or above * Protoc 3.x compiler and headers (download at the [Google protobuf releases page](https://github.com/protocolbuffers/protobuf/releases)) * Btrfs headers and libraries for your distribution. Note that building the btrfs driver can be disabled via the build tag `no_btrfs`, removing this dependency. diff --git a/Vagrantfile b/Vagrantfile index 75eb4f0d16..401f7198f0 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -104,7 +104,7 @@ EOF config.vm.provision "install-golang", type: "shell", run: "once" do |sh| sh.upload_path = "/tmp/vagrant-install-golang" sh.env = { - 'GO_VERSION': ENV['GO_VERSION'] || "1.21.13", + 'GO_VERSION': ENV['GO_VERSION'] || "1.22.6", } sh.inline = <<~SHELL #!/usr/bin/env bash diff --git a/contrib/Dockerfile.test b/contrib/Dockerfile.test index 4efe428b15..04dcebeab9 100644 --- a/contrib/Dockerfile.test +++ b/contrib/Dockerfile.test @@ -29,7 +29,7 @@ # docker run --privileged containerd-test # ------------------------------------------------------------------------------ -ARG GOLANG_VERSION=1.21.13 +ARG GOLANG_VERSION=1.22.6 ARG GOLANG_IMAGE=golang FROM ${GOLANG_IMAGE}:${GOLANG_VERSION} AS golang diff --git a/contrib/fuzz/oss_fuzz_build.sh b/contrib/fuzz/oss_fuzz_build.sh index 1c520b3bcf..9da5825a90 100755 --- a/contrib/fuzz/oss_fuzz_build.sh +++ b/contrib/fuzz/oss_fuzz_build.sh @@ -43,11 +43,11 @@ go run main.go --target_dir $SRC/containerd/images apt-get update && apt-get install -y wget cd $SRC -wget --quiet https://go.dev/dl/go1.21.13.linux-amd64.tar.gz +wget --quiet https://go.dev/dl/go1.22.6.linux-amd64.tar.gz mkdir temp-go rm -rf /root/.go/* -tar -C temp-go/ -xzf go1.21.13.linux-amd64.tar.gz +tar -C temp-go/ -xzf go1.22.6.linux-amd64.tar.gz mv temp-go/go/* /root/.go/ cd $SRC/containerd diff --git a/go.mod b/go.mod index a12092d072..e043998104 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/containerd/containerd -go 1.21 +go 1.22 require ( dario.cat/mergo v1.0.0 diff --git a/integration/client/go.mod b/integration/client/go.mod index 691214ef69..015222b5dd 100644 --- a/integration/client/go.mod +++ b/integration/client/go.mod @@ -1,6 +1,6 @@ module github.com/containerd/containerd/integration/client -go 1.21 +go 1.22 require ( github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 diff --git a/script/setup/prepare_env_windows.ps1 b/script/setup/prepare_env_windows.ps1 index 76fbcd76ab..eb0bdb38d3 100644 --- a/script/setup/prepare_env_windows.ps1 +++ b/script/setup/prepare_env_windows.ps1 @@ -5,7 +5,7 @@ # lived test environment. Set-MpPreference -DisableRealtimeMonitoring:$true -$PACKAGES= @{ mingw = "10.2.0"; git = ""; golang = "1.21.13"; make = ""; nssm = "" } +$PACKAGES= @{ mingw = "10.2.0"; git = ""; golang = "1.22.6"; make = ""; nssm = "" } Write-Host "Downloading chocolatey package" curl.exe -L "https://packages.chocolatey.org/chocolatey.0.10.15.nupkg" -o 'c:\choco.zip' From c7602808814d6ca235d37323ff37cde9bdf9d6bb Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Thu, 15 Aug 2024 10:15:30 +0530 Subject: [PATCH 25/27] update golangci-lint to 1.60.1 Backport of 939135ae3674a7363e0156f5af1275bac8ed1224 and 20ee6de0b57dabdb2e399aa16d176dad8326084c Signed-off-by: Akhil Mohan --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad9d7dd434..333f725770 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: - uses: golangci/golangci-lint-action@v4 with: only-new-issues: true - version: v1.56.1 + version: v1.60.1 skip-cache: true args: --timeout=8m From d63bd846458b7c504dc0efe59f275fee8e29322d Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 23 Aug 2024 22:06:21 +0200 Subject: [PATCH 26/27] go.mod: keep minimum go version at go1.21 Commit 3b263d082cb09a2911150135a760508527557a0e updated the version of Go used to build containerd to go1.22, but also updated the version in go.mod. As there's currently not a requirement for go1.22, we can keep this at the minimum required version, and revert it to go1.21. Signed-off-by: Sebastiaan van Stijn --- go.mod | 2 +- integration/client/go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index e043998104..a12092d072 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/containerd/containerd -go 1.22 +go 1.21 require ( dario.cat/mergo v1.0.0 diff --git a/integration/client/go.mod b/integration/client/go.mod index 015222b5dd..691214ef69 100644 --- a/integration/client/go.mod +++ b/integration/client/go.mod @@ -1,6 +1,6 @@ module github.com/containerd/containerd/integration/client -go 1.22 +go 1.21 require ( github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 From 975f279eeb3033d05e1b1089b11bd678cc73efd7 Mon Sep 17 00:00:00 2001 From: Chris Henzie Date: Fri, 23 Aug 2024 11:52:17 -0700 Subject: [PATCH 27/27] Prepare release notes for v1.7.21 Signed-off-by: Chris Henzie --- releases/v1.7.21.toml | 27 +++++++++++++++++++++++++++ version/version.go | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 releases/v1.7.21.toml diff --git a/releases/v1.7.21.toml b/releases/v1.7.21.toml new file mode 100644 index 0000000000..6e30bb534a --- /dev/null +++ b/releases/v1.7.21.toml @@ -0,0 +1,27 @@ +# commit to be tagged for new release +commit = "HEAD" + +# project_name is used to refer to the project in the notes +project_name = "containerd" + +# github_repo is the github project, only github is currently supported +github_repo = "containerd/containerd" + +# match_deps is a pattern to determine which dependencies should be included +# as part of this release. The changelog will also include changes for these +# dependencies based on the change in the dependency's version. +match_deps = "^github.com/(containerd/[a-zA-Z0-9-]+)$" + +# previous release of this project for determining changes +previous = "v1.7.20" + +# pre_release is whether to include a disclaimer about being a pre-release +pre_release = false + +# preface is the description of the release which precedes the author list +# and changelog. This description could include highlights as well as any +# description of changes. Use markdown formatting. +preface = """\ +The twenty-first patch release for containerd 1.7 contains various fixes +and updates. +""" diff --git a/version/version.go b/version/version.go index d9fdca5665..315867d21f 100644 --- a/version/version.go +++ b/version/version.go @@ -23,7 +23,7 @@ var ( Package = "github.com/containerd/containerd" // Version holds the complete version number. Filled in at linking time. - Version = "1.7.20+unknown" + Version = "1.7.21+unknown" // Revision is filled with the VCS (e.g. git) revision being used to build // the program at linking time.