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