1/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package remote
18
19import (
20	"context"
21	"errors"
22	"fmt"
23	"strings"
24	"time"
25
26	"google.golang.org/grpc"
27	"google.golang.org/grpc/codes"
28	"google.golang.org/grpc/status"
29	"k8s.io/klog/v2"
30
31	"k8s.io/component-base/logs/logreduction"
32	internalapi "k8s.io/cri-api/pkg/apis"
33	runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
34	"k8s.io/kubernetes/pkg/kubelet/cri/remote/util"
35	"k8s.io/kubernetes/pkg/probe/exec"
36	utilexec "k8s.io/utils/exec"
37)
38
39// remoteRuntimeService is a gRPC implementation of internalapi.RuntimeService.
40type remoteRuntimeService struct {
41	timeout       time.Duration
42	runtimeClient runtimeapi.RuntimeServiceClient
43	// Cache last per-container error message to reduce log spam
44	logReduction *logreduction.LogReduction
45}
46
47const (
48	// How frequently to report identical errors
49	identicalErrorDelay = 1 * time.Minute
50)
51
52// NewRemoteRuntimeService creates a new internalapi.RuntimeService.
53func NewRemoteRuntimeService(endpoint string, connectionTimeout time.Duration) (internalapi.RuntimeService, error) {
54	klog.V(3).InfoS("Connecting to runtime service", "endpoint", endpoint)
55	addr, dialer, err := util.GetAddressAndDialer(endpoint)
56	if err != nil {
57		return nil, err
58	}
59	ctx, cancel := context.WithTimeout(context.Background(), connectionTimeout)
60	defer cancel()
61
62	conn, err := grpc.DialContext(ctx, addr, grpc.WithInsecure(), grpc.WithContextDialer(dialer), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)))
63	if err != nil {
64		klog.ErrorS(err, "Connect remote runtime failed", "address", addr)
65		return nil, err
66	}
67
68	return &remoteRuntimeService{
69		timeout:       connectionTimeout,
70		runtimeClient: runtimeapi.NewRuntimeServiceClient(conn),
71		logReduction:  logreduction.NewLogReduction(identicalErrorDelay),
72	}, nil
73}
74
75// Version returns the runtime name, runtime version and runtime API version.
76func (r *remoteRuntimeService) Version(apiVersion string) (*runtimeapi.VersionResponse, error) {
77	klog.V(10).InfoS("[RemoteRuntimeService] Version", "apiVersion", apiVersion, "timeout", r.timeout)
78
79	ctx, cancel := getContextWithTimeout(r.timeout)
80	defer cancel()
81
82	typedVersion, err := r.runtimeClient.Version(ctx, &runtimeapi.VersionRequest{
83		Version: apiVersion,
84	})
85	if err != nil {
86		klog.ErrorS(err, "Version from runtime service failed")
87		return nil, err
88	}
89
90	klog.V(10).InfoS("[RemoteRuntimeService] Version Response", "apiVersion", typedVersion)
91
92	if typedVersion.Version == "" || typedVersion.RuntimeName == "" || typedVersion.RuntimeApiVersion == "" || typedVersion.RuntimeVersion == "" {
93		return nil, fmt.Errorf("not all fields are set in VersionResponse (%q)", *typedVersion)
94	}
95
96	return typedVersion, err
97}
98
99// RunPodSandbox creates and starts a pod-level sandbox. Runtimes should ensure
100// the sandbox is in ready state.
101func (r *remoteRuntimeService) RunPodSandbox(config *runtimeapi.PodSandboxConfig, runtimeHandler string) (string, error) {
102	// Use 2 times longer timeout for sandbox operation (4 mins by default)
103	// TODO: Make the pod sandbox timeout configurable.
104	timeout := r.timeout * 2
105
106	klog.V(10).InfoS("[RemoteRuntimeService] RunPodSandbox", "config", config, "runtimeHandler", runtimeHandler, "timeout", timeout)
107
108	ctx, cancel := getContextWithTimeout(timeout)
109	defer cancel()
110
111	resp, err := r.runtimeClient.RunPodSandbox(ctx, &runtimeapi.RunPodSandboxRequest{
112		Config:         config,
113		RuntimeHandler: runtimeHandler,
114	})
115	if err != nil {
116		klog.ErrorS(err, "RunPodSandbox from runtime service failed")
117		return "", err
118	}
119
120	if resp.PodSandboxId == "" {
121		errorMessage := fmt.Sprintf("PodSandboxId is not set for sandbox %q", config.GetMetadata())
122		err := errors.New(errorMessage)
123		klog.ErrorS(err, "RunPodSandbox failed")
124		return "", err
125	}
126
127	klog.V(10).InfoS("[RemoteRuntimeService] RunPodSandbox Response", "podSandboxID", resp.PodSandboxId)
128
129	return resp.PodSandboxId, nil
130}
131
132// StopPodSandbox stops the sandbox. If there are any running containers in the
133// sandbox, they should be forced to termination.
134func (r *remoteRuntimeService) StopPodSandbox(podSandBoxID string) error {
135	klog.V(10).InfoS("[RemoteRuntimeService] StopPodSandbox", "podSandboxID", podSandBoxID, "timeout", r.timeout)
136
137	ctx, cancel := getContextWithTimeout(r.timeout)
138	defer cancel()
139
140	_, err := r.runtimeClient.StopPodSandbox(ctx, &runtimeapi.StopPodSandboxRequest{
141		PodSandboxId: podSandBoxID,
142	})
143	if err != nil {
144		klog.ErrorS(err, "StopPodSandbox from runtime service failed", "podSandboxID", podSandBoxID)
145		return err
146	}
147
148	klog.V(10).InfoS("[RemoteRuntimeService] StopPodSandbox Response", "podSandboxID", podSandBoxID)
149
150	return nil
151}
152
153// RemovePodSandbox removes the sandbox. If there are any containers in the
154// sandbox, they should be forcibly removed.
155func (r *remoteRuntimeService) RemovePodSandbox(podSandBoxID string) error {
156	klog.V(10).InfoS("[RemoteRuntimeService] RemovePodSandbox", "podSandboxID", podSandBoxID, "timeout", r.timeout)
157	ctx, cancel := getContextWithTimeout(r.timeout)
158	defer cancel()
159
160	_, err := r.runtimeClient.RemovePodSandbox(ctx, &runtimeapi.RemovePodSandboxRequest{
161		PodSandboxId: podSandBoxID,
162	})
163	if err != nil {
164		klog.ErrorS(err, "RemovePodSandbox from runtime service failed", "podSandboxID", podSandBoxID)
165		return err
166	}
167
168	klog.V(10).InfoS("[RemoteRuntimeService] RemovePodSandbox Response", "podSandboxID", podSandBoxID)
169
170	return nil
171}
172
173// PodSandboxStatus returns the status of the PodSandbox.
174func (r *remoteRuntimeService) PodSandboxStatus(podSandBoxID string) (*runtimeapi.PodSandboxStatus, error) {
175	klog.V(10).InfoS("[RemoteRuntimeService] PodSandboxStatus", "podSandboxID", podSandBoxID, "timeout", r.timeout)
176	ctx, cancel := getContextWithTimeout(r.timeout)
177	defer cancel()
178
179	resp, err := r.runtimeClient.PodSandboxStatus(ctx, &runtimeapi.PodSandboxStatusRequest{
180		PodSandboxId: podSandBoxID,
181	})
182	if err != nil {
183		return nil, err
184	}
185
186	klog.V(10).InfoS("[RemoteRuntimeService] PodSandboxStatus Response", "podSandboxID", podSandBoxID, "status", resp.Status)
187
188	if resp.Status != nil {
189		if err := verifySandboxStatus(resp.Status); err != nil {
190			return nil, err
191		}
192	}
193
194	return resp.Status, nil
195}
196
197// ListPodSandbox returns a list of PodSandboxes.
198func (r *remoteRuntimeService) ListPodSandbox(filter *runtimeapi.PodSandboxFilter) ([]*runtimeapi.PodSandbox, error) {
199	klog.V(10).InfoS("[RemoteRuntimeService] ListPodSandbox", "filter", filter, "timeout", r.timeout)
200	ctx, cancel := getContextWithTimeout(r.timeout)
201	defer cancel()
202
203	resp, err := r.runtimeClient.ListPodSandbox(ctx, &runtimeapi.ListPodSandboxRequest{
204		Filter: filter,
205	})
206	if err != nil {
207		klog.ErrorS(err, "ListPodSandbox with filter from runtime service failed", "filter", filter)
208		return nil, err
209	}
210
211	klog.V(10).InfoS("[RemoteRuntimeService] ListPodSandbox Response", "filter", filter, "items", resp.Items)
212
213	return resp.Items, nil
214}
215
216// CreateContainer creates a new container in the specified PodSandbox.
217func (r *remoteRuntimeService) CreateContainer(podSandBoxID string, config *runtimeapi.ContainerConfig, sandboxConfig *runtimeapi.PodSandboxConfig) (string, error) {
218	klog.V(10).InfoS("[RemoteRuntimeService] CreateContainer", "podSandboxID", podSandBoxID, "timeout", r.timeout)
219	ctx, cancel := getContextWithTimeout(r.timeout)
220	defer cancel()
221
222	resp, err := r.runtimeClient.CreateContainer(ctx, &runtimeapi.CreateContainerRequest{
223		PodSandboxId:  podSandBoxID,
224		Config:        config,
225		SandboxConfig: sandboxConfig,
226	})
227	if err != nil {
228		klog.ErrorS(err, "CreateContainer in sandbox from runtime service failed", "podSandboxID", podSandBoxID)
229		return "", err
230	}
231
232	klog.V(10).InfoS("[RemoteRuntimeService] CreateContainer", "podSandboxID", podSandBoxID, "containerID", resp.ContainerId)
233	if resp.ContainerId == "" {
234		errorMessage := fmt.Sprintf("ContainerId is not set for container %q", config.GetMetadata())
235		err := errors.New(errorMessage)
236		klog.ErrorS(err, "CreateContainer failed")
237		return "", err
238	}
239
240	return resp.ContainerId, nil
241}
242
243// StartContainer starts the container.
244func (r *remoteRuntimeService) StartContainer(containerID string) error {
245	klog.V(10).InfoS("[RemoteRuntimeService] StartContainer", "containerID", containerID, "timeout", r.timeout)
246	ctx, cancel := getContextWithTimeout(r.timeout)
247	defer cancel()
248
249	_, err := r.runtimeClient.StartContainer(ctx, &runtimeapi.StartContainerRequest{
250		ContainerId: containerID,
251	})
252	if err != nil {
253		klog.ErrorS(err, "StartContainer from runtime service failed", "containerID", containerID)
254		return err
255	}
256	klog.V(10).InfoS("[RemoteRuntimeService] StartContainer Response", "containerID", containerID)
257
258	return nil
259}
260
261// StopContainer stops a running container with a grace period (i.e., timeout).
262func (r *remoteRuntimeService) StopContainer(containerID string, timeout int64) error {
263	klog.V(10).InfoS("[RemoteRuntimeService] StopContainer", "containerID", containerID, "timeout", timeout)
264	// Use timeout + default timeout (2 minutes) as timeout to leave extra time
265	// for SIGKILL container and request latency.
266	t := r.timeout + time.Duration(timeout)*time.Second
267	ctx, cancel := getContextWithTimeout(t)
268	defer cancel()
269
270	r.logReduction.ClearID(containerID)
271	_, err := r.runtimeClient.StopContainer(ctx, &runtimeapi.StopContainerRequest{
272		ContainerId: containerID,
273		Timeout:     timeout,
274	})
275	if err != nil {
276		klog.ErrorS(err, "StopContainer from runtime service failed", "containerID", containerID)
277		return err
278	}
279	klog.V(10).InfoS("[RemoteRuntimeService] StopContainer Response", "containerID", containerID)
280
281	return nil
282}
283
284// RemoveContainer removes the container. If the container is running, the container
285// should be forced to removal.
286func (r *remoteRuntimeService) RemoveContainer(containerID string) error {
287	klog.V(10).InfoS("[RemoteRuntimeService] RemoveContainer", "containerID", containerID, "timeout", r.timeout)
288	ctx, cancel := getContextWithTimeout(r.timeout)
289	defer cancel()
290
291	r.logReduction.ClearID(containerID)
292	_, err := r.runtimeClient.RemoveContainer(ctx, &runtimeapi.RemoveContainerRequest{
293		ContainerId: containerID,
294	})
295	if err != nil {
296		klog.ErrorS(err, "RemoveContainer from runtime service failed", "containerID", containerID)
297		return err
298	}
299	klog.V(10).InfoS("[RemoteRuntimeService] RemoveContainer Response", "containerID", containerID)
300
301	return nil
302}
303
304// ListContainers lists containers by filters.
305func (r *remoteRuntimeService) ListContainers(filter *runtimeapi.ContainerFilter) ([]*runtimeapi.Container, error) {
306	klog.V(10).InfoS("[RemoteRuntimeService] ListContainers", "filter", filter, "timeout", r.timeout)
307	ctx, cancel := getContextWithTimeout(r.timeout)
308	defer cancel()
309
310	resp, err := r.runtimeClient.ListContainers(ctx, &runtimeapi.ListContainersRequest{
311		Filter: filter,
312	})
313	if err != nil {
314		klog.ErrorS(err, "ListContainers with filter from runtime service failed", "filter", filter)
315		return nil, err
316	}
317	klog.V(10).InfoS("[RemoteRuntimeService] ListContainers Response", "filter", filter, "containers", resp.Containers)
318
319	return resp.Containers, nil
320}
321
322// ContainerStatus returns the container status.
323func (r *remoteRuntimeService) ContainerStatus(containerID string) (*runtimeapi.ContainerStatus, error) {
324	klog.V(10).InfoS("[RemoteRuntimeService] ContainerStatus", "containerID", containerID, "timeout", r.timeout)
325	ctx, cancel := getContextWithTimeout(r.timeout)
326	defer cancel()
327
328	resp, err := r.runtimeClient.ContainerStatus(ctx, &runtimeapi.ContainerStatusRequest{
329		ContainerId: containerID,
330	})
331	if err != nil {
332		// Don't spam the log with endless messages about the same failure.
333		if r.logReduction.ShouldMessageBePrinted(err.Error(), containerID) {
334			klog.ErrorS(err, "ContainerStatus from runtime service failed", "containerID", containerID)
335		}
336		return nil, err
337	}
338	r.logReduction.ClearID(containerID)
339	klog.V(10).InfoS("[RemoteRuntimeService] ContainerStatus Response", "containerID", containerID, "status", resp.Status)
340
341	if resp.Status != nil {
342		if err := verifyContainerStatus(resp.Status); err != nil {
343			klog.ErrorS(err, "verify ContainerStatus failed", "containerID", containerID)
344			return nil, err
345		}
346	}
347
348	return resp.Status, nil
349}
350
351// UpdateContainerResources updates a containers resource config
352func (r *remoteRuntimeService) UpdateContainerResources(containerID string, resources *runtimeapi.LinuxContainerResources) error {
353	klog.V(10).InfoS("[RemoteRuntimeService] UpdateContainerResources", "containerID", containerID, "timeout", r.timeout)
354	ctx, cancel := getContextWithTimeout(r.timeout)
355	defer cancel()
356
357	_, err := r.runtimeClient.UpdateContainerResources(ctx, &runtimeapi.UpdateContainerResourcesRequest{
358		ContainerId: containerID,
359		Linux:       resources,
360	})
361	if err != nil {
362		klog.ErrorS(err, "UpdateContainerResources from runtime service failed", "containerID", containerID)
363		return err
364	}
365	klog.V(10).InfoS("[RemoteRuntimeService] UpdateContainerResources Response", "containerID", containerID)
366
367	return nil
368}
369
370// ExecSync executes a command in the container, and returns the stdout output.
371// If command exits with a non-zero exit code, an error is returned.
372func (r *remoteRuntimeService) ExecSync(containerID string, cmd []string, timeout time.Duration) (stdout []byte, stderr []byte, err error) {
373	klog.V(10).InfoS("[RemoteRuntimeService] ExecSync", "containerID", containerID, "timeout", timeout)
374	// Do not set timeout when timeout is 0.
375	var ctx context.Context
376	var cancel context.CancelFunc
377	if timeout != 0 {
378		// Use timeout + default timeout (2 minutes) as timeout to leave some time for
379		// the runtime to do cleanup.
380		ctx, cancel = getContextWithTimeout(r.timeout + timeout)
381	} else {
382		ctx, cancel = getContextWithCancel()
383	}
384	defer cancel()
385
386	timeoutSeconds := int64(timeout.Seconds())
387	req := &runtimeapi.ExecSyncRequest{
388		ContainerId: containerID,
389		Cmd:         cmd,
390		Timeout:     timeoutSeconds,
391	}
392	resp, err := r.runtimeClient.ExecSync(ctx, req)
393	if err != nil {
394		klog.ErrorS(err, "ExecSync cmd from runtime service failed", "containerID", containerID, "cmd", cmd)
395
396		// interpret DeadlineExceeded gRPC errors as timedout probes
397		if status.Code(err) == codes.DeadlineExceeded {
398			err = exec.NewTimeoutError(fmt.Errorf("command %q timed out", strings.Join(cmd, " ")), timeout)
399		}
400
401		return nil, nil, err
402	}
403
404	klog.V(10).InfoS("[RemoteRuntimeService] ExecSync Response", "containerID", containerID, "exitCode", resp.ExitCode)
405	err = nil
406	if resp.ExitCode != 0 {
407		err = utilexec.CodeExitError{
408			Err:  fmt.Errorf("command '%s' exited with %d: %s", strings.Join(cmd, " "), resp.ExitCode, resp.Stderr),
409			Code: int(resp.ExitCode),
410		}
411	}
412
413	return resp.Stdout, resp.Stderr, err
414}
415
416// Exec prepares a streaming endpoint to execute a command in the container, and returns the address.
417func (r *remoteRuntimeService) Exec(req *runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, error) {
418	klog.V(10).InfoS("[RemoteRuntimeService] Exec", "timeout", r.timeout)
419	ctx, cancel := getContextWithTimeout(r.timeout)
420	defer cancel()
421
422	resp, err := r.runtimeClient.Exec(ctx, req)
423	if err != nil {
424		klog.ErrorS(err, "Exec cmd from runtime service failed", "containerID", req.ContainerId, "cmd", req.Cmd)
425		return nil, err
426	}
427	klog.V(10).InfoS("[RemoteRuntimeService] Exec Response")
428
429	if resp.Url == "" {
430		errorMessage := "URL is not set"
431		err := errors.New(errorMessage)
432		klog.ErrorS(err, "Exec failed")
433		return nil, err
434	}
435
436	return resp, nil
437}
438
439// Attach prepares a streaming endpoint to attach to a running container, and returns the address.
440func (r *remoteRuntimeService) Attach(req *runtimeapi.AttachRequest) (*runtimeapi.AttachResponse, error) {
441	klog.V(10).InfoS("[RemoteRuntimeService] Attach", "containerID", req.ContainerId, "timeout", r.timeout)
442	ctx, cancel := getContextWithTimeout(r.timeout)
443	defer cancel()
444
445	resp, err := r.runtimeClient.Attach(ctx, req)
446	if err != nil {
447		klog.ErrorS(err, "Attach container from runtime service failed", "containerID", req.ContainerId)
448		return nil, err
449	}
450	klog.V(10).InfoS("[RemoteRuntimeService] Attach Response", "containerID", req.ContainerId)
451
452	if resp.Url == "" {
453		errorMessage := "URL is not set"
454		err := errors.New(errorMessage)
455		klog.ErrorS(err, "Attach failed")
456		return nil, err
457	}
458	return resp, nil
459}
460
461// PortForward prepares a streaming endpoint to forward ports from a PodSandbox, and returns the address.
462func (r *remoteRuntimeService) PortForward(req *runtimeapi.PortForwardRequest) (*runtimeapi.PortForwardResponse, error) {
463	klog.V(10).InfoS("[RemoteRuntimeService] PortForward", "podSandboxID", req.PodSandboxId, "port", req.Port, "timeout", r.timeout)
464	ctx, cancel := getContextWithTimeout(r.timeout)
465	defer cancel()
466
467	resp, err := r.runtimeClient.PortForward(ctx, req)
468	if err != nil {
469		klog.ErrorS(err, "PortForward from runtime service failed", "podSandboxID", req.PodSandboxId)
470		return nil, err
471	}
472	klog.V(10).InfoS("[RemoteRuntimeService] PortForward Response", "podSandboxID", req.PodSandboxId)
473
474	if resp.Url == "" {
475		errorMessage := "URL is not set"
476		err := errors.New(errorMessage)
477		klog.ErrorS(err, "PortForward failed")
478		return nil, err
479	}
480
481	return resp, nil
482}
483
484// UpdateRuntimeConfig updates the config of a runtime service. The only
485// update payload currently supported is the pod CIDR assigned to a node,
486// and the runtime service just proxies it down to the network plugin.
487func (r *remoteRuntimeService) UpdateRuntimeConfig(runtimeConfig *runtimeapi.RuntimeConfig) error {
488	klog.V(10).InfoS("[RemoteRuntimeService] UpdateRuntimeConfig", "runtimeConfig", runtimeConfig, "timeout", r.timeout)
489	ctx, cancel := getContextWithTimeout(r.timeout)
490	defer cancel()
491
492	// Response doesn't contain anything of interest. This translates to an
493	// Event notification to the network plugin, which can't fail, so we're
494	// really looking to surface destination unreachable.
495	_, err := r.runtimeClient.UpdateRuntimeConfig(ctx, &runtimeapi.UpdateRuntimeConfigRequest{
496		RuntimeConfig: runtimeConfig,
497	})
498
499	if err != nil {
500		return err
501	}
502	klog.V(10).InfoS("[RemoteRuntimeService] UpdateRuntimeConfig Response", "runtimeConfig", runtimeConfig)
503
504	return nil
505}
506
507// Status returns the status of the runtime.
508func (r *remoteRuntimeService) Status() (*runtimeapi.RuntimeStatus, error) {
509	klog.V(10).InfoS("[RemoteRuntimeService] Status", "timeout", r.timeout)
510	ctx, cancel := getContextWithTimeout(r.timeout)
511	defer cancel()
512
513	resp, err := r.runtimeClient.Status(ctx, &runtimeapi.StatusRequest{})
514	if err != nil {
515		klog.ErrorS(err, "Status from runtime service failed")
516		return nil, err
517	}
518
519	klog.V(10).InfoS("[RemoteRuntimeService] Status Response", "status", resp.Status)
520
521	if resp.Status == nil || len(resp.Status.Conditions) < 2 {
522		errorMessage := "RuntimeReady or NetworkReady condition are not set"
523		err := errors.New(errorMessage)
524		klog.ErrorS(err, "Status failed")
525		return nil, err
526	}
527
528	return resp.Status, nil
529}
530
531// ContainerStats returns the stats of the container.
532func (r *remoteRuntimeService) ContainerStats(containerID string) (*runtimeapi.ContainerStats, error) {
533	klog.V(10).InfoS("[RemoteRuntimeService] ContainerStats", "containerID", containerID, "timeout", r.timeout)
534	ctx, cancel := getContextWithTimeout(r.timeout)
535	defer cancel()
536
537	resp, err := r.runtimeClient.ContainerStats(ctx, &runtimeapi.ContainerStatsRequest{
538		ContainerId: containerID,
539	})
540	if err != nil {
541		if r.logReduction.ShouldMessageBePrinted(err.Error(), containerID) {
542			klog.ErrorS(err, "ContainerStats from runtime service failed", "containerID", containerID)
543		}
544		return nil, err
545	}
546	r.logReduction.ClearID(containerID)
547	klog.V(10).InfoS("[RemoteRuntimeService] ContainerStats Response", "containerID", containerID, "stats", resp.GetStats())
548
549	return resp.GetStats(), nil
550}
551
552// ListContainerStats returns the list of ContainerStats given the filter.
553func (r *remoteRuntimeService) ListContainerStats(filter *runtimeapi.ContainerStatsFilter) ([]*runtimeapi.ContainerStats, error) {
554	klog.V(10).InfoS("[RemoteRuntimeService] ListContainerStats", "filter", filter)
555	// Do not set timeout, because writable layer stats collection takes time.
556	// TODO(random-liu): Should we assume runtime should cache the result, and set timeout here?
557	ctx, cancel := getContextWithCancel()
558	defer cancel()
559
560	resp, err := r.runtimeClient.ListContainerStats(ctx, &runtimeapi.ListContainerStatsRequest{
561		Filter: filter,
562	})
563	if err != nil {
564		klog.ErrorS(err, "ListContainerStats with filter from runtime service failed", "filter", filter)
565		return nil, err
566	}
567	klog.V(10).InfoS("[RemoteRuntimeService] ListContainerStats Response", "filter", filter, "stats", resp.GetStats())
568
569	return resp.GetStats(), nil
570}
571
572// ReopenContainerLog reopens the container log file.
573func (r *remoteRuntimeService) ReopenContainerLog(containerID string) error {
574	klog.V(10).InfoS("[RemoteRuntimeService] ReopenContainerLog", "containerID", containerID, "timeout", r.timeout)
575	ctx, cancel := getContextWithTimeout(r.timeout)
576	defer cancel()
577
578	_, err := r.runtimeClient.ReopenContainerLog(ctx, &runtimeapi.ReopenContainerLogRequest{ContainerId: containerID})
579	if err != nil {
580		klog.ErrorS(err, "ReopenContainerLog from runtime service failed", "containerID", containerID)
581		return err
582	}
583
584	klog.V(10).InfoS("[RemoteRuntimeService] ReopenContainerLog Response", "containerID", containerID)
585	return nil
586}
587