handler.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package stats
  14. import (
  15. "encoding/json"
  16. "fmt"
  17. "io"
  18. "net/http"
  19. "path"
  20. "time"
  21. restful "github.com/emicklei/go-restful"
  22. cadvisorapi "github.com/google/cadvisor/info/v1"
  23. "k8s.io/klog"
  24. "k8s.io/api/core/v1"
  25. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  26. "k8s.io/apimachinery/pkg/types"
  27. statsapi "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
  28. "k8s.io/kubernetes/pkg/kubelet/cm"
  29. kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
  30. "k8s.io/kubernetes/pkg/volume"
  31. )
  32. // Provider hosts methods required by stats handlers.
  33. type Provider interface {
  34. // The following stats are provided by either CRI or cAdvisor.
  35. //
  36. // ListPodStats returns the stats of all the containers managed by pods.
  37. ListPodStats() ([]statsapi.PodStats, error)
  38. // ListPodStatsAndUpdateCPUNanoCoreUsage updates the cpu nano core usage for
  39. // the containers and returns the stats for all the pod-managed containers.
  40. ListPodCPUAndMemoryStats() ([]statsapi.PodStats, error)
  41. // ListPodStatsAndUpdateCPUNanoCoreUsage returns the stats of all the
  42. // containers managed by pods and force update the cpu usageNanoCores.
  43. // This is a workaround for CRI runtimes that do not integrate with
  44. // cadvisor. See https://github.com/kubernetes/kubernetes/issues/72788
  45. // for more details.
  46. ListPodStatsAndUpdateCPUNanoCoreUsage() ([]statsapi.PodStats, error)
  47. // ImageFsStats returns the stats of the image filesystem.
  48. ImageFsStats() (*statsapi.FsStats, error)
  49. // The following stats are provided by cAdvisor.
  50. //
  51. // GetCgroupStats returns the stats and the networking usage of the cgroup
  52. // with the specified cgroupName.
  53. GetCgroupStats(cgroupName string, updateStats bool) (*statsapi.ContainerStats, *statsapi.NetworkStats, error)
  54. // GetCgroupCPUAndMemoryStats returns the CPU and memory stats of the cgroup with the specified cgroupName.
  55. GetCgroupCPUAndMemoryStats(cgroupName string, updateStats bool) (*statsapi.ContainerStats, error)
  56. // RootFsStats returns the stats of the node root filesystem.
  57. RootFsStats() (*statsapi.FsStats, error)
  58. // The following stats are provided by cAdvisor for legacy usage.
  59. //
  60. // GetContainerInfo returns the information of the container with the
  61. // containerName managed by the pod with the uid.
  62. GetContainerInfo(podFullName string, uid types.UID, containerName string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error)
  63. // GetRawContainerInfo returns the information of the container with the
  64. // containerName. If subcontainers is true, this function will return the
  65. // information of all the sub-containers as well.
  66. GetRawContainerInfo(containerName string, req *cadvisorapi.ContainerInfoRequest, subcontainers bool) (map[string]*cadvisorapi.ContainerInfo, error)
  67. // The following information is provided by Kubelet.
  68. //
  69. // GetPodByName returns the spec of the pod with the name in the specified
  70. // namespace.
  71. GetPodByName(namespace, name string) (*v1.Pod, bool)
  72. // GetNode returns the spec of the local node.
  73. GetNode() (*v1.Node, error)
  74. // GetNodeConfig returns the configuration of the local node.
  75. GetNodeConfig() cm.NodeConfig
  76. // ListVolumesForPod returns the stats of the volume used by the pod with
  77. // the podUID.
  78. ListVolumesForPod(podUID types.UID) (map[string]volume.Volume, bool)
  79. // GetPods returns the specs of all the pods running on this node.
  80. GetPods() []*v1.Pod
  81. // RlimitStats returns the rlimit stats of system.
  82. RlimitStats() (*statsapi.RlimitStats, error)
  83. // GetPodCgroupRoot returns the literal cgroupfs value for the cgroup containing all pods
  84. GetPodCgroupRoot() string
  85. // GetPodByCgroupfs provides the pod that maps to the specified cgroup literal, as well
  86. // as whether the pod was found.
  87. GetPodByCgroupfs(cgroupfs string) (*v1.Pod, bool)
  88. }
  89. type handler struct {
  90. provider Provider
  91. summaryProvider SummaryProvider
  92. }
  93. // CreateHandlers creates the REST handlers for the stats.
  94. func CreateHandlers(rootPath string, provider Provider, summaryProvider SummaryProvider, enableCAdvisorJSONEndpoints bool) *restful.WebService {
  95. h := &handler{provider, summaryProvider}
  96. ws := &restful.WebService{}
  97. ws.Path(rootPath).
  98. Produces(restful.MIME_JSON)
  99. type endpoint struct {
  100. path string
  101. handler restful.RouteFunction
  102. }
  103. endpoints := []endpoint{
  104. {"/summary", h.handleSummary},
  105. }
  106. if enableCAdvisorJSONEndpoints {
  107. endpoints = append(endpoints,
  108. endpoint{"", h.handleStats},
  109. endpoint{"/container", h.handleSystemContainer},
  110. endpoint{"/{podName}/{containerName}", h.handlePodContainer},
  111. endpoint{"/{namespace}/{podName}/{uid}/{containerName}", h.handlePodContainer},
  112. )
  113. }
  114. for _, e := range endpoints {
  115. for _, method := range []string{"GET", "POST"} {
  116. ws.Route(ws.
  117. Method(method).
  118. Path(e.path).
  119. To(e.handler))
  120. }
  121. }
  122. return ws
  123. }
  124. type statsRequest struct {
  125. // The name of the container for which to request stats.
  126. // Default: /
  127. // +optional
  128. ContainerName string `json:"containerName,omitempty"`
  129. // Max number of stats to return.
  130. // If start and end time are specified this limit is ignored.
  131. // Default: 60
  132. // +optional
  133. NumStats int `json:"num_stats,omitempty"`
  134. // Start time for which to query information.
  135. // If omitted, the beginning of time is assumed.
  136. // +optional
  137. Start time.Time `json:"start,omitempty"`
  138. // End time for which to query information.
  139. // If omitted, current time is assumed.
  140. // +optional
  141. End time.Time `json:"end,omitempty"`
  142. // Whether to also include information from subcontainers.
  143. // Default: false.
  144. // +optional
  145. Subcontainers bool `json:"subcontainers,omitempty"`
  146. }
  147. func (r *statsRequest) cadvisorRequest() *cadvisorapi.ContainerInfoRequest {
  148. return &cadvisorapi.ContainerInfoRequest{
  149. NumStats: r.NumStats,
  150. Start: r.Start,
  151. End: r.End,
  152. }
  153. }
  154. func parseStatsRequest(request *restful.Request) (statsRequest, error) {
  155. // Default request.
  156. query := statsRequest{
  157. NumStats: 60,
  158. }
  159. err := json.NewDecoder(request.Request.Body).Decode(&query)
  160. if err != nil && err != io.EOF {
  161. return query, err
  162. }
  163. return query, nil
  164. }
  165. // Handles root container stats requests to /stats
  166. func (h *handler) handleStats(request *restful.Request, response *restful.Response) {
  167. query, err := parseStatsRequest(request)
  168. if err != nil {
  169. handleError(response, "/stats", err)
  170. return
  171. }
  172. // Root container stats.
  173. statsMap, err := h.provider.GetRawContainerInfo("/", query.cadvisorRequest(), false)
  174. if err != nil {
  175. handleError(response, fmt.Sprintf("/stats %v", query), err)
  176. return
  177. }
  178. writeResponse(response, statsMap["/"])
  179. }
  180. // Handles stats summary requests to /stats/summary
  181. // If "only_cpu_and_memory" GET param is true then only cpu and memory is returned in response.
  182. func (h *handler) handleSummary(request *restful.Request, response *restful.Response) {
  183. onlyCPUAndMemory := false
  184. request.Request.ParseForm()
  185. if onlyCluAndMemoryParam, found := request.Request.Form["only_cpu_and_memory"]; found &&
  186. len(onlyCluAndMemoryParam) == 1 && onlyCluAndMemoryParam[0] == "true" {
  187. onlyCPUAndMemory = true
  188. }
  189. var summary *statsapi.Summary
  190. var err error
  191. if onlyCPUAndMemory {
  192. summary, err = h.summaryProvider.GetCPUAndMemoryStats()
  193. } else {
  194. // external calls to the summary API use cached stats
  195. forceStatsUpdate := false
  196. summary, err = h.summaryProvider.Get(forceStatsUpdate)
  197. }
  198. if err != nil {
  199. handleError(response, "/stats/summary", err)
  200. } else {
  201. writeResponse(response, summary)
  202. }
  203. }
  204. // Handles non-kubernetes container stats requests to /stats/container/
  205. func (h *handler) handleSystemContainer(request *restful.Request, response *restful.Response) {
  206. query, err := parseStatsRequest(request)
  207. if err != nil {
  208. handleError(response, "/stats/container", err)
  209. return
  210. }
  211. // Non-Kubernetes container stats.
  212. containerName := path.Join("/", query.ContainerName)
  213. stats, err := h.provider.GetRawContainerInfo(
  214. containerName, query.cadvisorRequest(), query.Subcontainers)
  215. if err != nil {
  216. if _, ok := stats[containerName]; ok {
  217. // If the failure is partial, log it and return a best-effort response.
  218. klog.Errorf("Partial failure issuing GetRawContainerInfo(%v): %v", query, err)
  219. } else {
  220. handleError(response, fmt.Sprintf("/stats/container %v", query), err)
  221. return
  222. }
  223. }
  224. writeResponse(response, stats)
  225. }
  226. // Handles kubernetes pod/container stats requests to:
  227. // /stats/<pod name>/<container name>
  228. // /stats/<namespace>/<pod name>/<uid>/<container name>
  229. func (h *handler) handlePodContainer(request *restful.Request, response *restful.Response) {
  230. query, err := parseStatsRequest(request)
  231. if err != nil {
  232. handleError(response, request.Request.URL.String(), err)
  233. return
  234. }
  235. // Default parameters.
  236. params := map[string]string{
  237. "namespace": metav1.NamespaceDefault,
  238. "uid": "",
  239. }
  240. for k, v := range request.PathParameters() {
  241. params[k] = v
  242. }
  243. if params["podName"] == "" || params["containerName"] == "" {
  244. response.WriteErrorString(http.StatusBadRequest,
  245. fmt.Sprintf("Invalid pod container request: %v", params))
  246. return
  247. }
  248. pod, ok := h.provider.GetPodByName(params["namespace"], params["podName"])
  249. if !ok {
  250. klog.V(4).Infof("Container not found: %v", params)
  251. response.WriteError(http.StatusNotFound, kubecontainer.ErrContainerNotFound)
  252. return
  253. }
  254. stats, err := h.provider.GetContainerInfo(
  255. kubecontainer.GetPodFullName(pod),
  256. types.UID(params["uid"]),
  257. params["containerName"],
  258. query.cadvisorRequest())
  259. if err != nil {
  260. handleError(response, fmt.Sprintf("%s %v", request.Request.URL.String(), query), err)
  261. return
  262. }
  263. writeResponse(response, stats)
  264. }
  265. func writeResponse(response *restful.Response, stats interface{}) {
  266. if err := response.WriteAsJson(stats); err != nil {
  267. klog.Errorf("Error writing response: %v", err)
  268. }
  269. }
  270. // handleError serializes an error object into an HTTP response.
  271. // request is provided for logging.
  272. func handleError(response *restful.Response, request string, err error) {
  273. switch err {
  274. case kubecontainer.ErrContainerNotFound:
  275. response.WriteError(http.StatusNotFound, err)
  276. default:
  277. msg := fmt.Sprintf("Internal Error: %v", err)
  278. klog.Errorf("HTTP InternalServerError serving %s: %s", request, msg)
  279. response.WriteErrorString(http.StatusInternalServerError, msg)
  280. }
  281. }