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