k8s.io/kubernetes@v1.29.3/pkg/kubelet/server/stats/handler.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes 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  //go:generate mockgen -source=handler.go -destination=testing/mock_stats_provider.go -package=testing Provider
    18  package stats
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"net/http"
    24  
    25  	restful "github.com/emicklei/go-restful/v3"
    26  	cadvisorapi "github.com/google/cadvisor/info/v1"
    27  	cadvisorv2 "github.com/google/cadvisor/info/v2"
    28  	"k8s.io/klog/v2"
    29  
    30  	v1 "k8s.io/api/core/v1"
    31  	"k8s.io/apimachinery/pkg/types"
    32  	statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
    33  	"k8s.io/kubernetes/pkg/kubelet/cm"
    34  	kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
    35  	"k8s.io/kubernetes/pkg/volume"
    36  )
    37  
    38  // Provider hosts methods required by stats handlers.
    39  type Provider interface {
    40  	// The following stats are provided by either CRI or cAdvisor.
    41  	//
    42  	// ListPodStats returns the stats of all the containers managed by pods.
    43  	ListPodStats(ctx context.Context) ([]statsapi.PodStats, error)
    44  	// ListPodStatsAndUpdateCPUNanoCoreUsage updates the cpu nano core usage for
    45  	// the containers and returns the stats for all the pod-managed containers.
    46  	ListPodCPUAndMemoryStats(ctx context.Context) ([]statsapi.PodStats, error)
    47  	// ListPodStatsAndUpdateCPUNanoCoreUsage returns the stats of all the
    48  	// containers managed by pods and force update the cpu usageNanoCores.
    49  	// This is a workaround for CRI runtimes that do not integrate with
    50  	// cadvisor. See https://github.com/kubernetes/kubernetes/issues/72788
    51  	// for more details.
    52  	ListPodStatsAndUpdateCPUNanoCoreUsage(ctx context.Context) ([]statsapi.PodStats, error)
    53  	// ImageFsStats returns the stats of the image filesystem.
    54  	// Kubelet allows three options for container filesystems
    55  	// Everything is on node fs (so no image filesystem)
    56  	// Container storage is on a dedicated disk (imageFs is separate from root)
    57  	// Container Filesystem is on root and Images are stored on ImageFs
    58  	// First return parameter is the image filesystem and
    59  	// second parameter is the container filesystem
    60  	ImageFsStats(ctx context.Context) (imageFs *statsapi.FsStats, containerFs *statsapi.FsStats, callErr error)
    61  	// The following stats are provided by cAdvisor.
    62  	//
    63  	// GetCgroupStats returns the stats and the networking usage of the cgroup
    64  	// with the specified cgroupName.
    65  	GetCgroupStats(cgroupName string, updateStats bool) (*statsapi.ContainerStats, *statsapi.NetworkStats, error)
    66  	// GetCgroupCPUAndMemoryStats returns the CPU and memory stats of the cgroup with the specified cgroupName.
    67  	GetCgroupCPUAndMemoryStats(cgroupName string, updateStats bool) (*statsapi.ContainerStats, error)
    68  
    69  	// RootFsStats returns the stats of the node root filesystem.
    70  	RootFsStats() (*statsapi.FsStats, error)
    71  
    72  	// The following stats are provided by cAdvisor for legacy usage.
    73  	//
    74  	// GetContainerInfo returns the information of the container with the
    75  	// containerName managed by the pod with the uid.
    76  	GetContainerInfo(ctx context.Context, podFullName string, uid types.UID, containerName string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error)
    77  	// GetRawContainerInfo returns the information of the container with the
    78  	// containerName. If subcontainers is true, this function will return the
    79  	// information of all the sub-containers as well.
    80  	GetRawContainerInfo(containerName string, req *cadvisorapi.ContainerInfoRequest, subcontainers bool) (map[string]*cadvisorapi.ContainerInfo, error)
    81  	// GetRequestedContainersInfo returns the information of the container with
    82  	// the containerName, and with the specified cAdvisor options.
    83  	GetRequestedContainersInfo(containerName string, options cadvisorv2.RequestOptions) (map[string]*cadvisorapi.ContainerInfo, error)
    84  
    85  	// The following information is provided by Kubelet.
    86  	//
    87  	// GetPodByName returns the spec of the pod with the name in the specified
    88  	// namespace.
    89  	GetPodByName(namespace, name string) (*v1.Pod, bool)
    90  	// GetNode returns the spec of the local node.
    91  	GetNode() (*v1.Node, error)
    92  	// GetNodeConfig returns the configuration of the local node.
    93  	GetNodeConfig() cm.NodeConfig
    94  	// ListVolumesForPod returns the stats of the volume used by the pod with
    95  	// the podUID.
    96  	ListVolumesForPod(podUID types.UID) (map[string]volume.Volume, bool)
    97  	// ListBlockVolumesForPod returns the stats of the volume used by the
    98  	// pod with the podUID.
    99  	ListBlockVolumesForPod(podUID types.UID) (map[string]volume.BlockVolume, bool)
   100  	// GetPods returns the specs of all the pods running on this node.
   101  	GetPods() []*v1.Pod
   102  
   103  	// RlimitStats returns the rlimit stats of system.
   104  	RlimitStats() (*statsapi.RlimitStats, error)
   105  
   106  	// GetPodCgroupRoot returns the literal cgroupfs value for the cgroup containing all pods
   107  	GetPodCgroupRoot() string
   108  
   109  	// GetPodByCgroupfs provides the pod that maps to the specified cgroup literal, as well
   110  	// as whether the pod was found.
   111  	GetPodByCgroupfs(cgroupfs string) (*v1.Pod, bool)
   112  }
   113  
   114  type handler struct {
   115  	provider        Provider
   116  	summaryProvider SummaryProvider
   117  }
   118  
   119  // CreateHandlers creates the REST handlers for the stats.
   120  func CreateHandlers(rootPath string, provider Provider, summaryProvider SummaryProvider) *restful.WebService {
   121  	h := &handler{provider, summaryProvider}
   122  
   123  	ws := &restful.WebService{}
   124  	ws.Path(rootPath).
   125  		Produces(restful.MIME_JSON)
   126  
   127  	endpoints := []struct {
   128  		path    string
   129  		handler restful.RouteFunction
   130  	}{
   131  		{"/summary", h.handleSummary},
   132  	}
   133  
   134  	for _, e := range endpoints {
   135  		for _, method := range []string{"GET", "POST"} {
   136  			ws.Route(ws.
   137  				Method(method).
   138  				Path(e.path).
   139  				To(e.handler))
   140  		}
   141  	}
   142  
   143  	return ws
   144  }
   145  
   146  // Handles stats summary requests to /stats/summary
   147  // If "only_cpu_and_memory" GET param is true then only cpu and memory is returned in response.
   148  func (h *handler) handleSummary(request *restful.Request, response *restful.Response) {
   149  	ctx := request.Request.Context()
   150  	onlyCPUAndMemory := false
   151  	err := request.Request.ParseForm()
   152  	if err != nil {
   153  		handleError(response, "/stats/summary", fmt.Errorf("parse form failed: %w", err))
   154  		return
   155  	}
   156  	if onlyCluAndMemoryParam, found := request.Request.Form["only_cpu_and_memory"]; found &&
   157  		len(onlyCluAndMemoryParam) == 1 && onlyCluAndMemoryParam[0] == "true" {
   158  		onlyCPUAndMemory = true
   159  	}
   160  	var summary *statsapi.Summary
   161  	if onlyCPUAndMemory {
   162  		summary, err = h.summaryProvider.GetCPUAndMemoryStats(ctx)
   163  	} else {
   164  		// external calls to the summary API use cached stats
   165  		forceStatsUpdate := false
   166  		summary, err = h.summaryProvider.Get(ctx, forceStatsUpdate)
   167  	}
   168  	if err != nil {
   169  		handleError(response, "/stats/summary", err)
   170  	} else {
   171  		writeResponse(response, summary)
   172  	}
   173  }
   174  
   175  func writeResponse(response *restful.Response, stats interface{}) {
   176  	if err := response.WriteAsJson(stats); err != nil {
   177  		klog.ErrorS(err, "Error writing response")
   178  	}
   179  }
   180  
   181  // handleError serializes an error object into an HTTP response.
   182  // request is provided for logging.
   183  func handleError(response *restful.Response, request string, err error) {
   184  	switch err {
   185  	case kubecontainer.ErrContainerNotFound:
   186  		response.WriteError(http.StatusNotFound, err)
   187  	default:
   188  		msg := fmt.Sprintf("Internal Error: %v", err)
   189  		klog.ErrorS(err, "HTTP InternalServerError serving", "request", request)
   190  		response.WriteErrorString(http.StatusInternalServerError, msg)
   191  	}
   192  }