Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package containerd for openSUSE:Factory checked in at 2024-09-12 16:54:01 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/containerd (Old) and /work/SRC/openSUSE:Factory/.containerd.new.17570 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "containerd" Thu Sep 12 16:54:01 2024 rev:70 rq:1200080 version:1.7.22 Changes: -------- --- /work/SRC/openSUSE:Factory/containerd/containerd.changes 2024-09-06 17:18:21.839440126 +0200 +++ /work/SRC/openSUSE:Factory/.containerd.new.17570/containerd.changes 2024-09-12 16:54:06.291231009 +0200 @@ -1,0 +2,9 @@ +Wed Sep 11 08:58:49 UTC 2024 - Aleksa Sarai <asarai@suse.com> + +- Update to containerd v1.7.22. Upstream release notes: + <https://github.com/containerd/containerd/releases/tag/v1.7.22> +- Bump minimum Go version to 1.22. +- Rebase patches: + * 0001-BUILD-SLE12-revert-btrfs-depend-on-kernel-UAPI-inste.patch + +------------------------------------------------------------------- Old: ---- containerd-1.7.21_472731909fa3.tar.xz New: ---- containerd-1.7.22_7f7fdf5fed64.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ containerd.spec ++++++ --- /var/tmp/diff_new_pack.g1IeYw/_old 2024-09-12 16:54:07.919298843 +0200 +++ /var/tmp/diff_new_pack.g1IeYw/_new 2024-09-12 16:54:07.931299342 +0200 @@ -23,14 +23,14 @@ %endif # MANUAL: Update the git_version. -%define git_version 472731909fa34bd7bc9c087e4c27943f9835f111 -%define git_short 472731909fa3 +%define git_version 7f7fdf5fed64eb6a7caf99b3e12efcf9d60e311c +%define git_short 7f7fdf5fed64 %global provider_prefix github.com/containerd/containerd %global import_path %{provider_prefix} Name: containerd -Version: 1.7.21 +Version: 1.7.22 Release: 0 Summary: Standalone OCI Container Daemon License: Apache-2.0 @@ -43,7 +43,7 @@ Patch1: 0001-BUILD-SLE12-revert-btrfs-depend-on-kernel-UAPI-inste.patch BuildRequires: fdupes BuildRequires: glibc-devel-static -BuildRequires: go >= 1.19 +BuildRequires: go >= 1.22 BuildRequires: go-go-md2man BuildRequires: golang-packaging BuildRequires: libbtrfs-devel >= 3.8 ++++++ 0001-BUILD-SLE12-revert-btrfs-depend-on-kernel-UAPI-inste.patch ++++++ --- /var/tmp/diff_new_pack.g1IeYw/_old 2024-09-12 16:54:08.159308842 +0200 +++ /var/tmp/diff_new_pack.g1IeYw/_new 2024-09-12 16:54:08.191310176 +0200 @@ -1,4 +1,4 @@ -From 876958fd21984b77e0a70d6ac5763e757ca976ef Mon Sep 17 00:00:00 2001 +From 0d0424334da85c9192a29a4e108094f7c28bbab5 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai <cyphar@cyphar.com> Date: Wed, 22 May 2024 12:58:32 -0700 Subject: [PATCH] BUILD: SLE12: revert "btrfs: depend on kernel UAPI instead of ++++++ _service ++++++ --- /var/tmp/diff_new_pack.g1IeYw/_old 2024-09-12 16:54:08.371317676 +0200 +++ /var/tmp/diff_new_pack.g1IeYw/_new 2024-09-12 16:54:08.407319176 +0200 @@ -3,8 +3,8 @@ <param name="url">https://github.com/containerd/containerd.git</param> <param name="scm">git</param> <param name="filename">containerd</param> - <param name="versionformat">1.7.21_%h</param> - <param name="revision">v1.7.21</param> + <param name="versionformat">1.7.22_%h</param> + <param name="revision">v1.7.22</param> <param name="exclude">.git</param> </service> <service name="recompress" mode="manual"> ++++++ containerd-1.7.21_472731909fa3.tar.xz -> containerd-1.7.22_7f7fdf5fed64.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/.github/actions/install-go/action.yml new/containerd-1.7.22_7f7fdf5fed64/.github/actions/install-go/action.yml --- old/containerd-1.7.21_472731909fa3/.github/actions/install-go/action.yml 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/.github/actions/install-go/action.yml 2024-09-09 22:09:31.000000000 +0200 @@ -3,7 +3,7 @@ inputs: go-version: required: true - default: "1.22.6" + default: "1.22.7" description: "Go version to install" runs: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/.github/workflows/api-release.yml new/containerd-1.7.22_7f7fdf5fed64/.github/workflows/api-release.yml --- old/containerd-1.7.21_472731909fa3/.github/workflows/api-release.yml 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/.github/workflows/api-release.yml 2024-09-09 22:09:31.000000000 +0200 @@ -6,7 +6,7 @@ name: API Release env: - GO_VERSION: "1.22.6" + GO_VERSION: "1.22.7" permissions: # added using https://github.com/step-security/secure-workflows contents: read diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/.github/workflows/ci.yml new/containerd-1.7.22_7f7fdf5fed64/.github/workflows/ci.yml --- old/containerd-1.7.21_472731909fa3/.github/workflows/ci.yml 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/.github/workflows/ci.yml 2024-09-09 22:09:31.000000000 +0200 @@ -192,7 +192,7 @@ strategy: matrix: os: [ubuntu-20.04, actuated-arm64-4cpu-16gb, macos-12, windows-2019, windows-2022] - go-version: ["1.22.6", "1.23.0"] + go-version: ["1.22.7", "1.23.1"] steps: - uses: actions/checkout@v4 - uses: ./.github/actions/install-go diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/.github/workflows/release.yml new/containerd-1.7.22_7f7fdf5fed64/.github/workflows/release.yml --- old/containerd-1.7.21_472731909fa3/.github/workflows/release.yml 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/.github/workflows/release.yml 2024-09-09 22:09:31.000000000 +0200 @@ -13,7 +13,7 @@ name: Release env: - GO_VERSION: "1.22.6" + GO_VERSION: "1.22.7" permissions: # added using https://github.com/step-security/secure-workflows contents: read diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/Vagrantfile new/containerd-1.7.22_7f7fdf5fed64/Vagrantfile --- old/containerd-1.7.21_472731909fa3/Vagrantfile 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/Vagrantfile 2024-09-09 22:09:31.000000000 +0200 @@ -104,7 +104,7 @@ 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.22.6", + 'GO_VERSION': ENV['GO_VERSION'] || "1.22.7", } sh.inline = <<~SHELL #!/usr/bin/env bash diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/contrib/Dockerfile.test new/containerd-1.7.22_7f7fdf5fed64/contrib/Dockerfile.test --- old/containerd-1.7.21_472731909fa3/contrib/Dockerfile.test 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/contrib/Dockerfile.test 2024-09-09 22:09:31.000000000 +0200 @@ -29,7 +29,7 @@ # docker run --privileged containerd-test # ------------------------------------------------------------------------------ -ARG GOLANG_VERSION=1.22.6 +ARG GOLANG_VERSION=1.22.7 ARG GOLANG_IMAGE=golang FROM ${GOLANG_IMAGE}:${GOLANG_VERSION} AS golang diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/contrib/fuzz/oss_fuzz_build.sh new/containerd-1.7.22_7f7fdf5fed64/contrib/fuzz/oss_fuzz_build.sh --- old/containerd-1.7.21_472731909fa3/contrib/fuzz/oss_fuzz_build.sh 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/contrib/fuzz/oss_fuzz_build.sh 2024-09-09 22:09:31.000000000 +0200 @@ -43,11 +43,11 @@ apt-get update && apt-get install -y wget cd $SRC -wget --quiet https://go.dev/dl/go1.22.6.linux-amd64.tar.gz +wget --quiet https://go.dev/dl/go1.22.7.linux-amd64.tar.gz mkdir temp-go rm -rf /root/.go/* -tar -C temp-go/ -xzf go1.22.6.linux-amd64.tar.gz +tar -C temp-go/ -xzf go1.22.7.linux-amd64.tar.gz mv temp-go/go/* /root/.go/ cd $SRC/containerd diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/integration/client/container_linux_test.go new/containerd-1.7.22_7f7fdf5fed64/integration/client/container_linux_test.go --- old/containerd-1.7.21_472731909fa3/integration/client/container_linux_test.go 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/integration/client/container_linux_test.go 2024-09-09 22:09:31.000000000 +0200 @@ -37,7 +37,9 @@ . "github.com/containerd/containerd" "github.com/containerd/containerd/cio" "github.com/containerd/containerd/containers" + "github.com/containerd/containerd/integration/failpoint" "github.com/containerd/containerd/oci" + "github.com/containerd/containerd/pkg/fifosync" "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/runtime/linux/runctypes" "github.com/containerd/containerd/runtime/v2/runc/options" @@ -45,6 +47,7 @@ "github.com/containerd/errdefs" "github.com/opencontainers/runtime-spec/specs-go" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sys/unix" ) @@ -1595,3 +1598,210 @@ }) } } + +// TestIssue10589 is used as regression case for issue 10589. +// +// This issue was caused by a race between init exits and new exec process tracking inside the shim. The test operates +// by controlling the time between when the shim invokes "runc exec" and when the actual "runc exec" is triggered. This +// allows validating that races for shim state tracking between pre- and post-start of the exec process do not exist. +// +// The workflow is as follows: +// 1. Create a container as normal +// 2. Make an exec1 using runc-fp with delayexec +// 3. Wait until the exec is waiting to start (triggered by delayexec) +// 4. Kill the container init process (signalling it is easiest) +// 5. Make an exec2 using runc-fp with delayexec +// 6. Wait until the exec is waiting to start +// 7. Allow exec1 to proceed +// 8. Allow exec2 to proceed +// 9. See that the container has exited and all execs have exited too +// +// https://github.com/containerd/containerd/issues/10589 +func TestIssue10589(t *testing.T) { + if f := os.Getenv("RUNC_FLAVOR"); f != "" && f != "runc" { + t.Skip("test requires runc") + } + if rt := os.Getenv("TEST_RUNTIME"); rt != "" && rt != plugin.RuntimeRuncV2 { + t.Skip("test requires io.containerd.runc.v2") + } + + client, err := newClient(t, address) + require.NoError(t, err) + t.Cleanup(func() { + client.Close() + }) + + var ( + image Image + ctx, cancel = testContext(t) + id = t.Name() + ) + t.Cleanup(cancel) + + image, err = client.GetImage(ctx, testImage) + require.NoError(t, err) + + // 1. Create a sleeping container + t.Log("1. Create a sleeping container") + container, err := client.NewContainer(ctx, id, + WithNewSnapshot(id, image), + WithNewSpec(oci.WithImageConfig(image), + withProcessArgs("sleep", "inf"), + oci.WithAnnotations(map[string]string{ + "oci.runc.failpoint.profile": "delayExec", + }), + ), + WithRuntime(client.Runtime(), &options.Options{ + BinaryName: "runc-fp", + }), + ) + require.NoError(t, err, "create container") + t.Cleanup(func() { + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + err := container.Delete(ctx, WithSnapshotCleanup) + if err != nil { + t.Log("delete err", err) + } + cancel() + }) + + task, err := container.NewTask(ctx, empty()) + require.NoError(t, err, "create task") + t.Cleanup(func() { + ctx, cancel := context.WithTimeout(ctx, 2*time.Second) + st, err := task.Delete(ctx, WithProcessKill) + t.Log("exit status", st) + if err != nil { + t.Log("kill err", err) + } + cancel() + }) + + err = task.Start(ctx) + require.NoError(t, err, "start container") + + status, err := task.Status(ctx) + require.NoError(t, err, "container status") + require.Equal(t, Running, status.Status) + + // 2. Create an exec + t.Log("2. Create exec1") + exec1ReadyFifo, err := fifosync.NewWaiter(filepath.Join(t.TempDir(), "exec1-ready.fifo"), 0600) + require.NoError(t, err, "create exec1 ready fifo") + exec1DelayFifo, err := fifosync.NewTrigger(filepath.Join(t.TempDir(), "exec1-delay.fifo"), 0600) + require.NoError(t, err, "create exec1 delay fifo") + exec1, err := task.Exec(ctx, "exec1", &specs.Process{ + Args: []string{"/bin/sleep", "301"}, + Cwd: "/", + Env: []string{ + failpoint.DelayExecReadyEnv + "=" + exec1ReadyFifo.Name(), + failpoint.DelayExecDelayEnv + "=" + exec1DelayFifo.Name(), + }, + }, cio.NullIO) + require.NoError(t, err, "create exec1") + + exec1done := make(chan struct{}) + go func() { + defer close(exec1done) + t.Log("Starting exec1") + err := exec1.Start(ctx) + assert.Error(t, err, "start exec1") + t.Logf("error starting exec1: %s", err) + }() + + // 3. Wait until the exec is waiting to start + t.Log("3. Wait until exec1 is waiting to start") + err = exec1ReadyFifo.Wait() + require.NoError(t, err, "open exec1 fifo") + + // 4. Kill the container init process + t.Log("4. Kill the container init process") + target := task.Pid() + t.Logf("Killing main pid (%v) of container %s", target, container.ID()) + syscall.Kill(int(target), syscall.SIGKILL) + status, err = task.Status(ctx) + require.NoError(t, err, "container status") + t.Log("container status", status.Status) + + // 5. Make an exec (2) using this failpoint + t.Log("5. Create exec2") + exec2ReadyFifo, err := fifosync.NewWaiter(filepath.Join(t.TempDir(), "exec2-ready.fifo"), 0600) + require.NoError(t, err, "create exec2 ready fifo: %q", exec2ReadyFifo) + exec2DelayFifo, err := fifosync.NewTrigger(filepath.Join(t.TempDir(), "exec2-delay.fifo"), 0600) + require.NoError(t, err, "create exec2 delay fifo: %q", exec2DelayFifo) + exec2, err := task.Exec(ctx, "exec2", &specs.Process{ + Args: []string{"/bin/sleep", "302"}, + Cwd: "/", + Env: []string{ + failpoint.DelayExecReadyEnv + "=" + exec2ReadyFifo.Name(), + failpoint.DelayExecDelayEnv + "=" + exec2DelayFifo.Name(), + }, + }, cio.NullIO) + require.NoError(t, err, "create exec2") + + exec2done := make(chan struct{}) + didExec2Run := true + go func() { + defer close(exec2done) + t.Log("Starting exec2") + err := exec2.Start(ctx) + assert.Error(t, err, "start exec2") + t.Logf("error starting exec2: %s", err) + }() + + // 6. Wait until the exec is waiting to start + t.Log("6. Wait until exec2 is waiting to start") + exec2ready := make(chan struct{}) + go func() { + exec2ReadyFifo.Wait() + close(exec2ready) + }() + select { + case <-exec2ready: + case <-exec2done: + didExec2Run = false + } + + // 7. Allow exec=1 to proceed + t.Log("7. Allow exec=1 to proceed") + err = exec1DelayFifo.Trigger() + assert.NoError(t, err, "trigger exec1 fifo") + status, err = task.Status(ctx) + require.NoError(t, err, "container status") + t.Log("container status", status.Status) + <-exec1done + status, err = task.Status(ctx) + require.NoError(t, err, "container status") + t.Log("container status", status.Status) + + // 8. Allow exec=2 to proceed + if didExec2Run { + t.Log("8. Allow exec2 to proceed") + err = exec2DelayFifo.Trigger() + assert.NoError(t, err, "trigger exec2 fifo") + status, err = task.Status(ctx) + require.NoError(t, err, "container status") + t.Log("container status", status.Status) + <-exec2done + status, err = task.Status(ctx) + require.NoError(t, err, "container status") + t.Log("container status", status.Status) + } else { + t.Log("8. Skip exec2") + } + + // 9. Validate + t.Log("9. Validate") + status, err = exec1.Status(ctx) + require.NoError(t, err, "exec1 status") + t.Logf("exec1 status: %s", status.Status) + assert.Equal(t, Created, status.Status) + status, err = exec2.Status(ctx) + require.NoError(t, err, "exec2 status") + t.Logf("exec2 status: %s", status.Status) + assert.Equal(t, Created, status.Status) + status, err = task.Status(ctx) + t.Logf("task status: %s", status.Status) + require.NoError(t, err, "container status") + assert.Equal(t, Stopped, status.Status) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/integration/failpoint/cmd/runc-fp/delayexec.go new/containerd-1.7.22_7f7fdf5fed64/integration/failpoint/cmd/runc-fp/delayexec.go --- old/containerd-1.7.21_472731909fa3/integration/failpoint/cmd/runc-fp/delayexec.go 1970-01-01 01:00:00.000000000 +0100 +++ new/containerd-1.7.22_7f7fdf5fed64/integration/failpoint/cmd/runc-fp/delayexec.go 2024-09-09 22:09:31.000000000 +0200 @@ -0,0 +1,131 @@ +//go:build linux + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package main + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "strings" + + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/sirupsen/logrus" + + "github.com/containerd/containerd/integration/failpoint" + "github.com/containerd/containerd/pkg/fifosync" +) + +// delayExec delays an "exec" command until a trigger is received from the calling test program. This can be used to +// test races around container lifecycle and exec processes. +func delayExec(ctx context.Context, method invoker) error { + isExec := strings.Contains(strings.Join(os.Args, ","), ",exec,") + if !isExec { + if err := method(ctx); err != nil { + return err + } + return nil + } + logrus.Debug("EXEC!") + + if err := delay(); err != nil { + return err + } + if err := method(ctx); err != nil { + return err + } + return nil +} + +func delay() error { + ready, delay, err := fifoFromProcessEnv() + if err != nil { + return err + } + if err := ready.Trigger(); err != nil { + return err + } + return delay.Wait() +} + +// fifoFromProcessEnv finds a fifo specified in the environment variables of an exec process +func fifoFromProcessEnv() (fifosync.Trigger, fifosync.Waiter, error) { + env, err := processEnvironment() + if err != nil { + return nil, nil, err + } + + readyName, ok := env[failpoint.DelayExecReadyEnv] + if !ok { + return nil, nil, fmt.Errorf("fifo: failed to find %q env var in %v", failpoint.DelayExecReadyEnv, env) + } + delayName, ok := env[failpoint.DelayExecDelayEnv] + if !ok { + return nil, nil, fmt.Errorf("fifo: failed to find %q env var in %v", failpoint.DelayExecDelayEnv, env) + } + logrus.WithField("ready", readyName).WithField("delay", delayName).Debug("Found FIFOs!") + readyFIFO, err := fifosync.NewTrigger(readyName, 0600) + if err != nil { + return nil, nil, err + } + delayFIFO, err := fifosync.NewWaiter(delayName, 0600) + if err != nil { + return nil, nil, err + } + return readyFIFO, delayFIFO, nil +} + +func processEnvironment() (map[string]string, error) { + idx := 2 + for ; idx < len(os.Args); idx++ { + if os.Args[idx] == "--process" { + break + } + } + + if idx >= len(os.Args)-1 || os.Args[idx] != "--process" { + return nil, errors.New("env: option --process required") + } + + specFile := os.Args[idx+1] + f, err := os.OpenFile(specFile, os.O_RDONLY, 0o644) + if err != nil { + return nil, fmt.Errorf("env: failed to open %s: %w", specFile, err) + } + + b, err := io.ReadAll(f) + if err != nil { + return nil, fmt.Errorf("env: failed to read spec from %q", specFile) + } + var spec specs.Process + if err := json.Unmarshal(b, &spec); err != nil { + return nil, fmt.Errorf("env: failed to unmarshal spec from %q: %w", specFile, err) + } + + // XXX: env vars can be specified multiple times, but we only keep one + env := make(map[string]string) + for _, e := range spec.Env { + k, v, _ := strings.Cut(e, "=") + env[k] = v + } + + return env, nil +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/integration/failpoint/cmd/runc-fp/main.go new/containerd-1.7.22_7f7fdf5fed64/integration/failpoint/cmd/runc-fp/main.go --- old/containerd-1.7.21_472731909fa3/integration/failpoint/cmd/runc-fp/main.go 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/integration/failpoint/cmd/runc-fp/main.go 2024-09-09 22:09:31.000000000 +0200 @@ -25,8 +25,9 @@ "os/exec" "syscall" - "github.com/containerd/containerd/oci" "github.com/sirupsen/logrus" + + "github.com/containerd/containerd/oci" ) const ( @@ -40,6 +41,7 @@ var ( failpointProfiles = map[string]invokerInterceptor{ "issue9103": issue9103KillInitAfterCreate, + "delayExec": delayExec, } ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/integration/failpoint/const.go new/containerd-1.7.22_7f7fdf5fed64/integration/failpoint/const.go --- old/containerd-1.7.21_472731909fa3/integration/failpoint/const.go 1970-01-01 01:00:00.000000000 +0100 +++ new/containerd-1.7.22_7f7fdf5fed64/integration/failpoint/const.go 2024-09-09 22:09:31.000000000 +0200 @@ -0,0 +1,22 @@ +/* + 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 failpoint + +const ( + DelayExecReadyEnv = "_RUNC_FP_DELAY_EXEC_READY" + DelayExecDelayEnv = "_RUNC_FP_DELAY_EXEC_DELAY" +) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/pkg/cri/sbserver/container_stats_list.go new/containerd-1.7.22_7f7fdf5fed64/pkg/cri/sbserver/container_stats_list.go --- old/containerd-1.7.21_472731909fa3/pkg/cri/sbserver/container_stats_list.go 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/pkg/cri/sbserver/container_stats_list.go 2024-09-09 22:09:31.000000000 +0200 @@ -192,6 +192,11 @@ return 0, nil } + // can't go backwards, this value might come in as 0 if the container was just removed + if currentUsageCoreNanoSeconds < oldStats.UsageCoreNanoSeconds { + return 0, nil + } + newUsageNanoCores := uint64(float64(currentUsageCoreNanoSeconds-oldStats.UsageCoreNanoSeconds) / float64(nanoSeconds) * float64(time.Second/time.Nanosecond)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/pkg/cri/sbserver/container_stats_list_test.go new/containerd-1.7.22_7f7fdf5fed64/pkg/cri/sbserver/container_stats_list_test.go --- old/containerd-1.7.21_472731909fa3/pkg/cri/sbserver/container_stats_list_test.go 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/pkg/cri/sbserver/container_stats_list_test.go 2024-09-09 22:09:31.000000000 +0200 @@ -35,45 +35,56 @@ func TestContainerMetricsCPUNanoCoreUsage(t *testing.T) { c := newTestCRIService() timestamp := time.Now() - secondAfterTimeStamp := timestamp.Add(time.Second) - ID := "ID" + tenSecondAftertimeStamp := timestamp.Add(time.Second * 10) for desc, test := range map[string]struct { + id string + desc string firstCPUValue uint64 secondCPUValue uint64 expectedNanoCoreUsageFirst uint64 expectedNanoCoreUsageSecond uint64 }{ "metrics": { + id: "id1", + desc: "metrics", firstCPUValue: 50, secondCPUValue: 500, expectedNanoCoreUsageFirst: 0, - expectedNanoCoreUsageSecond: 450, + expectedNanoCoreUsageSecond: 45, + }, + "no metrics in second CPU sample": { + id: "id2", + desc: "metrics", + firstCPUValue: 234235, + secondCPUValue: 0, + expectedNanoCoreUsageFirst: 0, + expectedNanoCoreUsageSecond: 0, }, } { t.Run(desc, func(t *testing.T) { container, err := containerstore.NewContainer( - containerstore.Metadata{ID: ID}, + containerstore.Metadata{ID: test.id}, ) assert.NoError(t, err) assert.Nil(t, container.Stats) err = c.containerStore.Add(container) assert.NoError(t, err) - cpuUsage, err := c.getUsageNanoCores(ID, false, test.firstCPUValue, timestamp) + cpuUsage, err := c.getUsageNanoCores(test.id, false, test.firstCPUValue, timestamp) assert.NoError(t, err) - container, err = c.containerStore.Get(ID) + container, err = c.containerStore.Get(test.id) assert.NoError(t, err) assert.NotNil(t, container.Stats) assert.Equal(t, test.expectedNanoCoreUsageFirst, cpuUsage) - cpuUsage, err = c.getUsageNanoCores(ID, false, test.secondCPUValue, secondAfterTimeStamp) + cpuUsage, err = c.getUsageNanoCores(test.id, false, test.secondCPUValue, tenSecondAftertimeStamp) assert.NoError(t, err) assert.Equal(t, test.expectedNanoCoreUsageSecond, cpuUsage) - container, err = c.containerStore.Get(ID) + container, err = c.containerStore.Get(test.id) assert.NoError(t, err) assert.NotNil(t, container.Stats) }) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/pkg/cri/server/container_stats_list.go new/containerd-1.7.22_7f7fdf5fed64/pkg/cri/server/container_stats_list.go --- old/containerd-1.7.21_472731909fa3/pkg/cri/server/container_stats_list.go 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/pkg/cri/server/container_stats_list.go 2024-09-09 22:09:31.000000000 +0200 @@ -122,6 +122,11 @@ return 0, nil } + // can't go backwards, this value might come in as 0 if the container was just removed + if currentUsageCoreNanoSeconds < oldStats.UsageCoreNanoSeconds { + return 0, nil + } + newUsageNanoCores := uint64(float64(currentUsageCoreNanoSeconds-oldStats.UsageCoreNanoSeconds) / float64(nanoSeconds) * float64(time.Second/time.Nanosecond)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/pkg/cri/server/container_stats_list_test.go new/containerd-1.7.22_7f7fdf5fed64/pkg/cri/server/container_stats_list_test.go --- old/containerd-1.7.21_472731909fa3/pkg/cri/server/container_stats_list_test.go 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/pkg/cri/server/container_stats_list_test.go 2024-09-09 22:09:31.000000000 +0200 @@ -27,48 +27,58 @@ func TestContainerMetricsCPUNanoCoreUsage(t *testing.T) { c := newTestCRIService() timestamp := time.Now() - secondAfterTimeStamp := timestamp.Add(time.Second) - ID := "ID" + tenSecondAftertimeStamp := timestamp.Add(time.Second * 10) for desc, test := range map[string]struct { + id string + desc string firstCPUValue uint64 secondCPUValue uint64 expectedNanoCoreUsageFirst uint64 expectedNanoCoreUsageSecond uint64 }{ "metrics": { + id: "id1", + desc: "metrics", firstCPUValue: 50, secondCPUValue: 500, expectedNanoCoreUsageFirst: 0, - expectedNanoCoreUsageSecond: 450, + expectedNanoCoreUsageSecond: 45, + }, + "no metrics in second CPU sample": { + id: "id2", + desc: "metrics", + firstCPUValue: 234235, + secondCPUValue: 0, + expectedNanoCoreUsageFirst: 0, + expectedNanoCoreUsageSecond: 0, }, } { t.Run(desc, func(t *testing.T) { container, err := containerstore.NewContainer( - containerstore.Metadata{ID: ID}, + containerstore.Metadata{ID: test.id}, ) assert.NoError(t, err) assert.Nil(t, container.Stats) err = c.containerStore.Add(container) assert.NoError(t, err) - cpuUsage, err := c.getUsageNanoCores(ID, false, test.firstCPUValue, timestamp) + cpuUsage, err := c.getUsageNanoCores(test.id, false, test.firstCPUValue, timestamp) assert.NoError(t, err) - container, err = c.containerStore.Get(ID) + container, err = c.containerStore.Get(test.id) assert.NoError(t, err) assert.NotNil(t, container.Stats) assert.Equal(t, test.expectedNanoCoreUsageFirst, cpuUsage) - cpuUsage, err = c.getUsageNanoCores(ID, false, test.secondCPUValue, secondAfterTimeStamp) + cpuUsage, err = c.getUsageNanoCores(test.id, false, test.secondCPUValue, tenSecondAftertimeStamp) assert.NoError(t, err) assert.Equal(t, test.expectedNanoCoreUsageSecond, cpuUsage) - container, err = c.containerStore.Get(ID) + container, err = c.containerStore.Get(test.id) assert.NoError(t, err) assert.NotNil(t, container.Stats) }) } - } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/pkg/fifosync/fifo_unix.go new/containerd-1.7.22_7f7fdf5fed64/pkg/fifosync/fifo_unix.go --- old/containerd-1.7.21_472731909fa3/pkg/fifosync/fifo_unix.go 1970-01-01 01:00:00.000000000 +0100 +++ new/containerd-1.7.22_7f7fdf5fed64/pkg/fifosync/fifo_unix.go 2024-09-09 22:09:31.000000000 +0200 @@ -0,0 +1,125 @@ +//go:build unix + +/* + 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 fifosync provides a pattern on Unix-like operating systems for synchronizing across processes using Unix FIFOs +(named pipes). +*/ +package fifosync + +import ( + "errors" + "fmt" + "io" + "os" + + "golang.org/x/sys/unix" +) + +// Trigger is a FIFO which is used to signal another process to proceed. +type Trigger interface { + // Name returns the name of the trigger + Name() string + // Trigger triggers another process to proceed. + Trigger() error +} + +// Waiter is a FIFO which is used to wait for trigger provided by another process. +type Waiter interface { + // Name returns the name of the waiter + Name() string + // Wait waits for a trigger from another process. + Wait() error +} + +type fifo struct { + name string +} + +// NewTrigger creates a new Trigger +func NewTrigger(name string, mode uint32) (Trigger, error) { + return new(name, mode) +} + +// NewWaiter creates a new Waiter +func NewWaiter(name string, mode uint32) (Waiter, error) { + return new(name, mode) +} + +// New creates a new FIFO if it does not already exist. Use AsTrigger or AsWaiter to convert the new FIFO to a Trigger +// or Waiter. +func new(name string, mode uint32) (*fifo, error) { + s, err := os.Stat(name) + exist := true + if err != nil { + if !errors.Is(err, os.ErrNotExist) { + return nil, fmt.Errorf("fifo: failed to stat %q: %w", name, err) + } + exist = false + } + if s != nil && s.Mode()&os.ModeNamedPipe == 0 { + return nil, fmt.Errorf("fifo: not a named pipe: %q", name) + } + if !exist { + err = unix.Mkfifo(name, mode) + if err != nil && !errors.Is(err, unix.EEXIST) { + return nil, fmt.Errorf("fifo: failed to create %q: %w", name, err) + } + } + return &fifo{ + name: name, + }, nil +} + +func (f *fifo) Name() string { + return f.name +} + +// AsTrigger converts the FIFO to a Trigger. +func (f *fifo) AsTrigger() Trigger { + return f +} + +// Trigger triggers another process to proceed. +func (f *fifo) Trigger() error { + file, err := os.OpenFile(f.name, os.O_RDONLY, 0) + if err != nil { + return fmt.Errorf("fifo: failed to open %s: %w", f.name, err) + } + defer file.Close() + _, err = io.ReadAll(file) + return err +} + +// AsWaiter converts the FIFO to a Waiter. +func (f *fifo) AsWaiter() Waiter { + return f +} + +// Wait waits for a trigger from another process. +func (f *fifo) Wait() error { + fd, err := unix.Open(f.name, unix.O_WRONLY, 0) + if err != nil { + return fmt.Errorf("fifo: failed to open %s: %w", f.name, err) + } + defer unix.Close(fd) + if _, err := unix.Write(fd, []byte("0")); err != nil { + return fmt.Errorf("failed to write to %d: %w", fd, err) + } + return nil +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/releases/v1.7.22.toml new/containerd-1.7.22_7f7fdf5fed64/releases/v1.7.22.toml --- old/containerd-1.7.21_472731909fa3/releases/v1.7.22.toml 1970-01-01 01:00:00.000000000 +0100 +++ new/containerd-1.7.22_7f7fdf5fed64/releases/v1.7.22.toml 2024-09-09 22:09:31.000000000 +0200 @@ -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.21" + +# 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-second patch release for containerd 1.7 contains various fixes +and updates. +""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/runtime/v2/runc/task/service.go new/containerd-1.7.22_7f7fdf5fed64/runtime/v2/runc/task/service.go --- old/containerd-1.7.21_472731909fa3/runtime/v2/runc/task/service.go 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/runtime/v2/runc/task/service.go 2024-09-09 22:09:31.000000000 +0200 @@ -73,15 +73,17 @@ } go ep.Run(ctx) s := &service{ - context: ctx, - events: make(chan interface{}, 128), - ec: reaper.Default.Subscribe(), - ep: ep, - shutdown: sd, - containers: make(map[string]*runc.Container), - running: make(map[int][]containerProcess), - pendingExecs: make(map[*runc.Container]int), - exitSubscribers: make(map[*map[int][]runcC.Exit]struct{}), + context: ctx, + events: make(chan interface{}, 128), + ec: reaper.Default.Subscribe(), + ep: ep, + shutdown: sd, + containers: make(map[string]*runc.Container), + running: make(map[int][]containerProcess), + runningExecs: make(map[*runc.Container]int), + execCountSubscribers: make(map[*runc.Container]chan<- int), + containerInitExit: make(map[*runc.Container]runcC.Exit), + exitSubscribers: make(map[*map[int][]runcC.Exit]struct{}), } go s.processExits() runcC.Monitor = reaper.Default @@ -116,7 +118,19 @@ lifecycleMu sync.Mutex running map[int][]containerProcess // pid -> running process, guarded by lifecycleMu - pendingExecs map[*runc.Container]int // container -> num pending execs, guarded by lifecycleMu + runningExecs map[*runc.Container]int // container -> num running execs, guarded by lifecycleMu + // container -> subscription to exec exits/changes to s.runningExecs[container], + // guarded by lifecycleMu + execCountSubscribers map[*runc.Container]chan<- int + // container -> init exits, guarded by lifecycleMu + // Used to stash container init process exits, so that we can hold them + // until after we've made sure to publish all the container's exec exits. + // Also used to prevent starting new execs from being started if the + // container's init process (read: pid, not [process.Init]) has already been + // reaped by the shim. + // Note that this flag gets updated before the container's [process.Init.Status] + // is transitioned to "stopped". + containerInitExit map[*runc.Container]runcC.Exit // Subscriptions to exits for PIDs. Adding/deleting subscriptions and // dereferencing the subscription pointers must only be done while holding // lifecycleMu. @@ -137,8 +151,7 @@ // // The returned handleStarted closure records that the process has started so // that its exit can be handled efficiently. If the process has already exited, -// it handles the exit immediately. In addition, if the process is an exec and -// its container's init process has already exited, that exit is also processed. +// it handles the exit immediately. // handleStarted should be called after the event announcing the start of the // process has been published. Note that s.lifecycleMu must not be held when // calling handleStarted. @@ -173,44 +186,8 @@ pid = p.Pid() } - _, init := p.(*process.Init) s.lifecycleMu.Lock() - var initExits []runcC.Exit - var initCps []containerProcess - if !init { - s.pendingExecs[c]-- - - initPid := c.Pid() - iExits, initExited := exits[initPid] - if initExited && s.pendingExecs[c] == 0 { - // c's init process has exited before handleStarted was called and - // this is the last pending exec process start - we need to process - // the exit for the init process after processing this exec, so: - // - delete c from the s.pendingExecs map - // - keep the exits for the init pid to process later (after we process - // this exec's exits) - // - get the necessary containerProcesses for the init process (that we - // need to process the exits), and remove them from s.running (which we skipped - // doing in processExits). - delete(s.pendingExecs, c) - initExits = iExits - var skipped []containerProcess - for _, initPidCp := range s.running[initPid] { - if initPidCp.Container == c { - initCps = append(initCps, initPidCp) - } else { - skipped = append(skipped, initPidCp) - } - } - if len(skipped) == 0 { - delete(s.running, initPid) - } else { - s.running[initPid] = skipped - } - } - } - ees, exited := exits[pid] delete(s.exitSubscribers, &exits) exits = nil @@ -219,11 +196,6 @@ for _, ee := range ees { s.handleProcessExit(ee, c, p) } - for _, eee := range initExits { - for _, cp := range initCps { - s.handleProcessExit(eee, cp.Container, cp.Process) - } - } } else { // Process start was successful, add to `s.running`. s.running[pid] = append(s.running[pid], containerProcess{ @@ -304,7 +276,11 @@ if r.ExecID == "" { cinit = container } else { - s.pendingExecs[container]++ + if _, initExited := s.containerInitExit[container]; initExited { + s.lifecycleMu.Unlock() + return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container %s init process is not running", container.ID) + } + s.runningExecs[container]++ } handleStarted, cleanup := s.preStart(cinit) s.lifecycleMu.Unlock() @@ -312,6 +288,17 @@ p, err := container.Start(ctx, r) if err != nil { + // If we failed to even start the process, s.runningExecs + // won't get decremented in s.handleProcessExit. We still need + // to update it. + if r.ExecID != "" { + s.lifecycleMu.Lock() + s.runningExecs[container]-- + if ch, ok := s.execCountSubscribers[container]; ok { + ch <- s.runningExecs[container] + } + s.lifecycleMu.Unlock() + } handleStarted(container, p) return nil, errdefs.ToGRPC(err) } @@ -676,28 +663,23 @@ // Handle the exit for a created/started process. If there's more than // one, assume they've all exited. One of them will be the correct // process. - var cps, skipped []containerProcess + var cps []containerProcess for _, cp := range s.running[e.Pid] { _, init := cp.Process.(*process.Init) - if init && s.pendingExecs[cp.Container] != 0 { - // This exit relates to a container for which we have pending execs. In - // order to ensure order between execs and the init process for a given - // container, skip processing this exit here and let the `handleStarted` - // closure for the pending exec publish it. - skipped = append(skipped, cp) - } else { - cps = append(cps, cp) + if init { + s.containerInitExit[cp.Container] = e } + cps = append(cps, cp) } - if len(skipped) > 0 { - s.running[e.Pid] = skipped - } else { - delete(s.running, e.Pid) - } + delete(s.running, e.Pid) s.lifecycleMu.Unlock() for _, cp := range cps { - s.handleProcessExit(e, cp.Container, cp.Process) + if ip, ok := cp.Process.(*process.Init); ok { + s.handleInitExit(e, cp.Container, ip) + } else { + s.handleProcessExit(e, cp.Container, cp.Process) + } } } } @@ -706,18 +688,60 @@ s.events <- evt } -// s.mu must be locked when calling handleProcessExit -func (s *service) handleProcessExit(e runcC.Exit, c *runc.Container, p process.Process) { - if ip, ok := p.(*process.Init); ok { - // Ensure all children are killed - if runc.ShouldKillAllOnExit(s.context, c.Bundle) { - if err := ip.KillAll(s.context); err != nil { - logrus.WithError(err).WithField("id", ip.ID()). - Error("failed to kill init's children") - } +// handleInitExit processes container init process exits. +// This is handled separately from non-init exits, because there +// are some extra invariants we want to ensure in this case, namely: +// - for a given container, the init process exit MUST be the last exit published +// This is achieved by: +// - killing all running container processes (if the container has a shared pid +// namespace, otherwise all other processes have been reaped already). +// - waiting for the container's running exec counter to reach 0. +// - finally, publishing the init exit. +func (s *service) handleInitExit(e runcC.Exit, c *runc.Container, p *process.Init) { + // kill all running container processes + if runc.ShouldKillAllOnExit(s.context, c.Bundle) { + if err := p.KillAll(s.context); err != nil { + logrus.WithError(err).WithField("id", p.ID()). + Error("failed to kill init's children") } } + s.lifecycleMu.Lock() + numRunningExecs := s.runningExecs[c] + if numRunningExecs == 0 { + delete(s.runningExecs, c) + s.lifecycleMu.Unlock() + s.handleProcessExit(e, c, p) + return + } + + events := make(chan int, numRunningExecs) + s.execCountSubscribers[c] = events + + s.lifecycleMu.Unlock() + + go func() { + defer func() { + s.lifecycleMu.Lock() + defer s.lifecycleMu.Unlock() + delete(s.execCountSubscribers, c) + delete(s.runningExecs, c) + }() + + // wait for running processes to exit + for { + if runningExecs := <-events; runningExecs == 0 { + break + } + } + + // all running processes have exited now, and no new + // ones can start, so we can publish the init exit + s.handleProcessExit(e, c, p) + }() +} + +func (s *service) handleProcessExit(e runcC.Exit, c *runc.Container, p process.Process) { p.SetExited(e.Status) s.send(&eventstypes.TaskExit{ ContainerID: c.ID, @@ -726,6 +750,14 @@ ExitStatus: uint32(e.Status), ExitedAt: protobuf.ToTimestamp(p.ExitedAt()), }) + if _, init := p.(*process.Init); !init { + s.lifecycleMu.Lock() + s.runningExecs[c]-- + if ch, ok := s.execCountSubscribers[c]; ok { + ch <- s.runningExecs[c] + } + s.lifecycleMu.Unlock() + } } func (s *service) getContainerPids(ctx context.Context, container *runc.Container) ([]uint32, error) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/script/setup/prepare_env_windows.ps1 new/containerd-1.7.22_7f7fdf5fed64/script/setup/prepare_env_windows.ps1 --- old/containerd-1.7.21_472731909fa3/script/setup/prepare_env_windows.ps1 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/script/setup/prepare_env_windows.ps1 2024-09-09 22:09:31.000000000 +0200 @@ -5,7 +5,7 @@ # lived test environment. Set-MpPreference -DisableRealtimeMonitoring:$true -$PACKAGES= @{ mingw = "10.2.0"; git = ""; golang = "1.22.6"; make = ""; nssm = "" } +$PACKAGES= @{ mingw = "10.2.0"; git = ""; golang = "1.22.7"; make = ""; nssm = "" } Write-Host "Downloading chocolatey package" curl.exe -L "https://packages.chocolatey.org/chocolatey.0.10.15.nupkg" -o 'c:\choco.zip' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/script/setup/runc-version new/containerd-1.7.22_7f7fdf5fed64/script/setup/runc-version --- old/containerd-1.7.21_472731909fa3/script/setup/runc-version 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/script/setup/runc-version 2024-09-09 22:09:31.000000000 +0200 @@ -1 +1 @@ -v1.1.13 +v1.1.14 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/containerd-1.7.21_472731909fa3/version/version.go new/containerd-1.7.22_7f7fdf5fed64/version/version.go --- old/containerd-1.7.21_472731909fa3/version/version.go 2024-08-27 00:04:19.000000000 +0200 +++ new/containerd-1.7.22_7f7fdf5fed64/version/version.go 2024-09-09 22:09:31.000000000 +0200 @@ -23,7 +23,7 @@ Package = "github.com/containerd/containerd" // Version holds the complete version number. Filled in at linking time. - Version = "1.7.21+unknown" + Version = "1.7.22+unknown" // Revision is filled with the VCS (e.g. git) revision being used to build // the program at linking time.