diff --git a/cmd/containerd/server/server.go b/cmd/containerd/server/server.go index 60e494889869d..35cb027d26549 100644 --- a/cmd/containerd/server/server.go +++ b/cmd/containerd/server/server.go @@ -92,22 +92,34 @@ func CreateTopLevelDirectories(config *srvconfig.Config) error { if err := os.Chmod(config.TempDir, 0o700); err != nil && !errors.Is(err, os.ErrPermission) { return err } - if runtime.GOOS == "windows" { - // On Windows, the Host Compute Service (vmcompute) will read the - // TEMP/TMP setting from the calling process when creating the - // tempdir to extract an image layer to. This allows the - // administrator to align the tempdir location with the same volume - // as the snapshot dir to avoid a copy operation when moving the - // extracted layer to the snapshot dir location. - os.Setenv("TEMP", config.TempDir) - os.Setenv("TMP", config.TempDir) - } else { - os.Setenv("TMPDIR", config.TempDir) - } + setTempDirEnv(config.TempDir) } return nil } +// setTempDirEnv points the process' temp-directory environment variables at +// tempDir so that components (and the OS) honor the configured temp location. +func setTempDirEnv(tempDir string) { + if runtime.GOOS == "windows" { + // On Windows, the Host Compute Service (vmcompute) will read the + // TEMP/TMP setting from the calling process when creating the + // tempdir to extract an image layer to. This allows the + // administrator to align the tempdir location with the same volume + // as the snapshot dir to avoid a copy operation when moving the + // extracted layer to the snapshot dir location. + os.Setenv("TEMP", tempDir) + os.Setenv("TMP", tempDir) + // Since Go 1.21, os.MkdirTemp/os.TempDir resolve the temp dir via + // Windows' GetTempPath2W. For processes running as SYSTEM (as + // containerd does under the SCM), that API reads SystemTemp rather + // than TEMP/TMP, so set it too to keep the override effective. + // https://cs.opensource.google/go/go/+/refs/tags/go1.21.0:src/os/file_windows.go + os.Setenv("SystemTemp", tempDir) + } else { + os.Setenv("TMPDIR", tempDir) + } +} + // New creates and initializes a new containerd server func New(ctx context.Context, config *srvconfig.Config) (*Server, error) { if err := apply(ctx, config); err != nil { diff --git a/cmd/containerd/server/server_test.go b/cmd/containerd/server/server_test.go index 769a1107431e0..afa3024a155ea 100644 --- a/cmd/containerd/server/server_test.go +++ b/cmd/containerd/server/server_test.go @@ -20,6 +20,7 @@ import ( "context" "iter" "os" + "runtime" "slices" "testing" @@ -172,3 +173,25 @@ func TestMigration(t *testing.T) { t.Fatal(err) } } + +func TestSetTempDirEnv(t *testing.T) { + const tempDir = "/tmp/path/for/testing/temp" + + var keys []string + if runtime.GOOS == "windows" { + keys = []string{"TEMP", "TMP", "SystemTemp"} + } else { + keys = []string{"TMPDIR"} + } + for _, k := range keys { + t.Setenv(k, "") + } + + setTempDirEnv(tempDir) + + for _, k := range keys { + if got := os.Getenv(k); got != tempDir { + t.Errorf("expected %s=%q, got %q", k, tempDir, got) + } + } +} diff --git a/internal/cri/nri/nri_api_linux.go b/internal/cri/nri/nri_api_linux.go index 1e1c69ffa1cc8..b1f6eacead09c 100644 --- a/internal/cri/nri/nri_api_linux.go +++ b/internal/cri/nri/nri_api_linux.go @@ -682,7 +682,8 @@ func (p *criPodSandbox) GetPid() uint32 { } func (p *criPodSandbox) GetIPs() []string { - if p.IP == "" { + // IP and AdditionalIPs are promoted fields from the embedded Sandbox struct. + if p.Sandbox == nil || p.IP == "" { return nil } ips := append([]string{p.IP}, p.AdditionalIPs...) diff --git a/internal/cri/nri/nri_api_linux_test.go b/internal/cri/nri/nri_api_linux_test.go new file mode 100644 index 0000000000000..5959087121136 --- /dev/null +++ b/internal/cri/nri/nri_api_linux_test.go @@ -0,0 +1,81 @@ +/* + 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 nri + +import ( + "testing" + + sstore "github.com/containerd/containerd/v2/internal/cri/store/sandbox" + "github.com/stretchr/testify/assert" +) + +func TestCRIPodSandboxGetIPs(t *testing.T) { + testCases := []struct { + name string + pod *criPodSandbox + expected []string + }{ + { + name: "nil Sandbox", + pod: &criPodSandbox{ + Sandbox: nil, + }, + expected: nil, + }, + { + name: "empty primary IP", + pod: &criPodSandbox{ + Sandbox: &sstore.Sandbox{ + Metadata: sstore.Metadata{ + IP: "", + }, + }, + }, + expected: nil, + }, + { + name: "single primary IP", + pod: &criPodSandbox{ + Sandbox: &sstore.Sandbox{ + Metadata: sstore.Metadata{ + IP: "10.0.0.1", + }, + }, + }, + expected: []string{"10.0.0.1"}, + }, + { + name: "primary and additional IPs", + pod: &criPodSandbox{ + Sandbox: &sstore.Sandbox{ + Metadata: sstore.Metadata{ + IP: "10.0.0.1", + AdditionalIPs: []string{"fd00::1"}, + }, + }, + }, + expected: []string{"10.0.0.1", "fd00::1"}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ips := tc.pod.GetIPs() + assert.Equal(t, tc.expected, ips) + }) + } +}