handler.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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. "github.com/golang/glog"
  22. cadvisorapi "github.com/google/cadvisor/info/v1"
  23. cadvisorapiv2 "github.com/google/cadvisor/info/v2"
  24. "github.com/emicklei/go-restful"
  25. "k8s.io/kubernetes/pkg/api"
  26. "k8s.io/kubernetes/pkg/kubelet/cm"
  27. kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
  28. "k8s.io/kubernetes/pkg/types"
  29. "k8s.io/kubernetes/pkg/volume"
  30. )
  31. // Host methods required by stats handlers.
  32. type StatsProvider interface {
  33. GetContainerInfo(podFullName string, uid types.UID, containerName string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error)
  34. GetContainerInfoV2(name string, options cadvisorapiv2.RequestOptions) (map[string]cadvisorapiv2.ContainerInfo, error)
  35. GetRawContainerInfo(containerName string, req *cadvisorapi.ContainerInfoRequest, subcontainers bool) (map[string]*cadvisorapi.ContainerInfo, error)
  36. GetPodByName(namespace, name string) (*api.Pod, bool)
  37. GetNode() (*api.Node, error)
  38. GetNodeConfig() cm.NodeConfig
  39. ImagesFsInfo() (cadvisorapiv2.FsInfo, error)
  40. RootFsInfo() (cadvisorapiv2.FsInfo, error)
  41. ListVolumesForPod(podUID types.UID) (map[string]volume.Volume, bool)
  42. GetPods() []*api.Pod
  43. }
  44. type handler struct {
  45. provider StatsProvider
  46. summaryProvider SummaryProvider
  47. }
  48. func CreateHandlers(provider StatsProvider, summaryProvider SummaryProvider) *restful.WebService {
  49. h := &handler{provider, summaryProvider}
  50. ws := &restful.WebService{}
  51. ws.Path("/stats/").
  52. Produces(restful.MIME_JSON)
  53. endpoints := []struct {
  54. path string
  55. handler restful.RouteFunction
  56. }{
  57. {"", h.handleStats},
  58. {"/summary", h.handleSummary},
  59. {"/container", h.handleSystemContainer},
  60. {"/{podName}/{containerName}", h.handlePodContainer},
  61. {"/{namespace}/{podName}/{uid}/{containerName}", h.handlePodContainer},
  62. }
  63. for _, e := range endpoints {
  64. for _, method := range []string{"GET", "POST"} {
  65. ws.Route(ws.
  66. Method(method).
  67. Path(e.path).
  68. To(e.handler))
  69. }
  70. }
  71. return ws
  72. }
  73. type StatsRequest struct {
  74. // The name of the container for which to request stats.
  75. // Default: /
  76. ContainerName string `json:"containerName,omitempty"`
  77. // Max number of stats to return.
  78. // If start and end time are specified this limit is ignored.
  79. // Default: 60
  80. NumStats int `json:"num_stats,omitempty"`
  81. // Start time for which to query information.
  82. // If omitted, the beginning of time is assumed.
  83. Start time.Time `json:"start,omitempty"`
  84. // End time for which to query information.
  85. // If omitted, current time is assumed.
  86. End time.Time `json:"end,omitempty"`
  87. // Whether to also include information from subcontainers.
  88. // Default: false.
  89. Subcontainers bool `json:"subcontainers,omitempty"`
  90. }
  91. func (r *StatsRequest) cadvisorRequest() *cadvisorapi.ContainerInfoRequest {
  92. return &cadvisorapi.ContainerInfoRequest{
  93. NumStats: r.NumStats,
  94. Start: r.Start,
  95. End: r.End,
  96. }
  97. }
  98. func parseStatsRequest(request *restful.Request) (StatsRequest, error) {
  99. // Default request.
  100. query := StatsRequest{
  101. NumStats: 60,
  102. }
  103. err := json.NewDecoder(request.Request.Body).Decode(&query)
  104. if err != nil && err != io.EOF {
  105. return query, err
  106. }
  107. return query, nil
  108. }
  109. // Handles root container stats requests to /stats
  110. func (h *handler) handleStats(request *restful.Request, response *restful.Response) {
  111. query, err := parseStatsRequest(request)
  112. if err != nil {
  113. handleError(response, "/stats", err)
  114. return
  115. }
  116. // Root container stats.
  117. statsMap, err := h.provider.GetRawContainerInfo("/", query.cadvisorRequest(), false)
  118. if err != nil {
  119. handleError(response, fmt.Sprintf("/stats %v", query), err)
  120. return
  121. }
  122. writeResponse(response, statsMap["/"])
  123. }
  124. // Handles stats summary requests to /stats/summary
  125. func (h *handler) handleSummary(request *restful.Request, response *restful.Response) {
  126. summary, err := h.summaryProvider.Get()
  127. if err != nil {
  128. handleError(response, "/stats/summary", err)
  129. } else {
  130. writeResponse(response, summary)
  131. }
  132. }
  133. // Handles non-kubernetes container stats requests to /stats/container/
  134. func (h *handler) handleSystemContainer(request *restful.Request, response *restful.Response) {
  135. query, err := parseStatsRequest(request)
  136. if err != nil {
  137. handleError(response, "/stats/container", err)
  138. return
  139. }
  140. // Non-Kubernetes container stats.
  141. containerName := path.Join("/", query.ContainerName)
  142. stats, err := h.provider.GetRawContainerInfo(
  143. containerName, query.cadvisorRequest(), query.Subcontainers)
  144. if err != nil {
  145. if _, ok := stats[containerName]; ok {
  146. // If the failure is partial, log it and return a best-effort response.
  147. glog.Errorf("Partial failure issuing GetRawContainerInfo(%v): %v", query, err)
  148. } else {
  149. handleError(response, fmt.Sprintf("/stats/container %v", query), err)
  150. return
  151. }
  152. }
  153. writeResponse(response, stats)
  154. }
  155. // Handles kubernetes pod/container stats requests to:
  156. // /stats/<pod name>/<container name>
  157. // /stats/<namespace>/<pod name>/<uid>/<container name>
  158. func (h *handler) handlePodContainer(request *restful.Request, response *restful.Response) {
  159. query, err := parseStatsRequest(request)
  160. if err != nil {
  161. handleError(response, request.Request.URL.String(), err)
  162. return
  163. }
  164. // Default parameters.
  165. params := map[string]string{
  166. "namespace": api.NamespaceDefault,
  167. "uid": "",
  168. }
  169. for k, v := range request.PathParameters() {
  170. params[k] = v
  171. }
  172. if params["podName"] == "" || params["containerName"] == "" {
  173. response.WriteErrorString(http.StatusBadRequest,
  174. fmt.Sprintf("Invalid pod container request: %v", params))
  175. return
  176. }
  177. pod, ok := h.provider.GetPodByName(params["namespace"], params["podName"])
  178. if !ok {
  179. glog.V(4).Infof("Container not found: %v", params)
  180. response.WriteError(http.StatusNotFound, kubecontainer.ErrContainerNotFound)
  181. return
  182. }
  183. stats, err := h.provider.GetContainerInfo(
  184. kubecontainer.GetPodFullName(pod),
  185. types.UID(params["uid"]),
  186. params["containerName"],
  187. query.cadvisorRequest())
  188. if err != nil {
  189. handleError(response, fmt.Sprintf("%s %v", request.Request.URL.String(), query), err)
  190. return
  191. }
  192. writeResponse(response, stats)
  193. }
  194. func writeResponse(response *restful.Response, stats interface{}) {
  195. if err := response.WriteAsJson(stats); err != nil {
  196. glog.Errorf("Error writing response: %v", err)
  197. }
  198. }
  199. // handleError serializes an error object into an HTTP response.
  200. // request is provided for logging.
  201. func handleError(response *restful.Response, request string, err error) {
  202. switch err {
  203. case kubecontainer.ErrContainerNotFound:
  204. response.WriteError(http.StatusNotFound, err)
  205. default:
  206. msg := fmt.Sprintf("Internal Error: %v", err)
  207. glog.Errorf("HTTP InternalServerError serving %s: %s", request, msg)
  208. response.WriteErrorString(http.StatusInternalServerError, msg)
  209. }
  210. }