From 0d907e015b784a4edeb17c0580bf1e408c966de7 Mon Sep 17 00:00:00 2001 From: Arnav Sankaran Date: Wed, 10 Jul 2019 14:00:20 -0700 Subject: [PATCH] Add ImageFSInfo, ContainerStats, and ListContainerStats impl for linux to dockershim --- pkg/kubelet/dockershim/docker_image_linux.go | 50 +++++++++++- pkg/kubelet/dockershim/docker_stats_linux.go | 84 +++++++++++++++++++- 2 files changed, 128 insertions(+), 6 deletions(-) diff --git a/pkg/kubelet/dockershim/docker_image_linux.go b/pkg/kubelet/dockershim/docker_image_linux.go index e68d83b19ab..0762d26334c 100644 --- a/pkg/kubelet/dockershim/docker_image_linux.go +++ b/pkg/kubelet/dockershim/docker_image_linux.go @@ -20,12 +20,58 @@ package dockershim import ( "context" - "fmt" + "path/filepath" + "os" + "time" + + "k8s.io/klog" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2" ) // ImageFsInfo returns information of the filesystem that is used to store images. func (ds *dockerService) ImageFsInfo(_ context.Context, r *runtimeapi.ImageFsInfoRequest) (*runtimeapi.ImageFsInfoResponse, error) { - return nil, fmt.Errorf("not implemented") + info, err := ds.client.Info() + if err != nil { + klog.Errorf("Failed to get docker info: %v", err) + return nil, err + } + + bytes, inodes, err := dirSize(filepath.Join(info.DockerRootDir, "image")) + if err != nil { + return nil, err + } + + return &runtimeapi.ImageFsInfoResponse{ + ImageFilesystems: []*runtimeapi.FilesystemUsage{ + &runtimeapi.FilesystemUsage{ + Timestamp: time.Now().Unix(), + FsId: &runtimeapi.FilesystemIdentifier{ + Mountpoint: info.DockerRootDir, + }, + UsedBytes: &runtimeapi.UInt64Value{ + Value: uint64(bytes), + }, + InodesUsed: &runtimeapi.UInt64Value{ + Value: uint64(inodes), + }, + }, + }, + }, nil +} + +func dirSize(path string) (int64, int64, error) { + bytes := int64(0) + inodes := int64(0) + err := filepath.Walk(path, func(dir string, info os.FileInfo, err error) error { + if err != nil { + return err + } + inodes += 1 + if !info.IsDir() { + bytes += info.Size() + } + return nil + }) + return bytes, inodes, err } diff --git a/pkg/kubelet/dockershim/docker_stats_linux.go b/pkg/kubelet/dockershim/docker_stats_linux.go index b78ff9c3c95..d0f3cd463c0 100644 --- a/pkg/kubelet/dockershim/docker_stats_linux.go +++ b/pkg/kubelet/dockershim/docker_stats_linux.go @@ -20,17 +20,93 @@ package dockershim import ( "context" - "fmt" + "time" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2" ) // ContainerStats returns stats for a container stats request based on container id. func (ds *dockerService) ContainerStats(_ context.Context, r *runtimeapi.ContainerStatsRequest) (*runtimeapi.ContainerStatsResponse, error) { - return nil, fmt.Errorf("not implemented") + stats, err := ds.getContainerStats(r.ContainerId) + if err != nil { + return nil, err + } + return &runtimeapi.ContainerStatsResponse{Stats: stats}, nil } // ListContainerStats returns stats for a list container stats request based on a filter. -func (ds *dockerService) ListContainerStats(_ context.Context, r *runtimeapi.ListContainerStatsRequest) (*runtimeapi.ListContainerStatsResponse, error) { - return nil, fmt.Errorf("not implemented") +func (ds *dockerService) ListContainerStats(ctx context.Context, r *runtimeapi.ListContainerStatsRequest) (*runtimeapi.ListContainerStatsResponse, error) { + containerStatsFilter := r.GetFilter() + filter := &runtimeapi.ContainerFilter{} + + if containerStatsFilter != nil { + filter.Id = containerStatsFilter.Id + filter.PodSandboxId = containerStatsFilter.PodSandboxId + filter.LabelSelector = containerStatsFilter.LabelSelector + } + + listResp, err := ds.ListContainers(ctx, &runtimeapi.ListContainersRequest{Filter: filter}) + if err != nil { + return nil, err + } + + var stats []*runtimeapi.ContainerStats + for _, container := range listResp.Containers { + containerStats, err := ds.getContainerStats(container.Id) + if err != nil { + return nil, err + } + + stats = append(stats, containerStats) + } + + return &runtimeapi.ListContainerStatsResponse{Stats: stats}, nil +} + +func (ds *dockerService) getContainerStats(containerID string) (*runtimeapi.ContainerStats, error) { + info, err := ds.client.Info() + if err != nil { + return nil, err + } + + statsJSON, err := ds.client.GetContainerStats(containerID) + if err != nil { + return nil, err + } + + containerJSON, err := ds.client.InspectContainerWithSize(containerID) + if err != nil { + return nil, err + } + + statusResp, err := ds.ContainerStatus(context.Background(), &runtimeapi.ContainerStatusRequest{ContainerId: containerID}) + if err != nil { + return nil, err + } + status := statusResp.GetStatus() + + dockerStats := statsJSON.Stats + timestamp := time.Now().UnixNano() + containerStats := &runtimeapi.ContainerStats{ + Attributes: &runtimeapi.ContainerAttributes{ + Id: containerID, + Metadata: status.Metadata, + Labels: status.Labels, + Annotations: status.Annotations, + }, + Cpu: &runtimeapi.CpuUsage{ + Timestamp: timestamp, + UsageCoreNanoSeconds: &runtimeapi.UInt64Value{Value: dockerStats.CPUStats.CPUUsage.TotalUsage}, + }, + Memory: &runtimeapi.MemoryUsage{ + Timestamp: timestamp, + WorkingSetBytes: &runtimeapi.UInt64Value{Value: dockerStats.MemoryStats.PrivateWorkingSet}, + }, + WritableLayer: &runtimeapi.FilesystemUsage{ + Timestamp: timestamp, + FsId: &runtimeapi.FilesystemIdentifier{Mountpoint: info.DockerRootDir}, + UsedBytes: &runtimeapi.UInt64Value{Value: uint64(*containerJSON.SizeRw)}, + }, + } + return containerStats, nil }