diff --git a/API.md b/API.md index 42dc7f44..66a7df9d 100644 --- a/API.md +++ b/API.md @@ -41,6 +41,7 @@ - [List NICs of certain node](#list-nics-of-certain-node) - [List Pod](#list-pod) - [Get Pod](#get-pod) + - [List Containers](#list-containers) - [Get Container](#get-container) - [List Services](#list-services) - [Get Service](#get-service) @@ -655,7 +656,7 @@ For each Pod, we have fileds need to handle. - Always,OnFailure,Never 9. networkType: the string options for network type, support "host", "custom" and "cluster". 10. nodeAffinity: the string array to indicate whchi nodes I want my Pod can run in. -11. envVars: the environment variables for containers and it's map (string to stirng) form. +12. envVars: the environment variables for containers and it's map (string to stirng) form. Example: @@ -999,8 +1000,7 @@ Response Data: "createAt": 1532573834, "status": "Ready", "os": "Ubuntu 16.04.4 LTS", - "kernelVersion": "4.4.0-133-generic", - "dockerVersion": "17.6.2", + "kernelVersion": "4.4.0-128-generic", "kubeproxyVersion": "v1.11.0", "kubernetesVersion": "v1.11.0", "labels": { @@ -1010,18 +1010,15 @@ Response Data: } }, "resource": { - "cpuRequests": 1.3, - "cpuLimits": 0, - "memoryRequests": 146800640, - "memoryLimits": 356515840, - "memoryTotalHugepages": 1024, - "memoryFreeHugepages": 512, - "memoryHugepagesSize": 2097152, + "cpuRequests": 1.05, + "cpuLimits": 0.6, + "memoryRequests": 283115520, + "memoryLimits": 3009413000, "allocatableCPU": 2, - "allocatableMemory": 2948079600, + "allocatableMemory": 1891131400, "allocatablePods": 110, "capacityCPU": 2, - "capacityMemory": 5200421000, + "capacityMemory": 4143472600, "capacityPods": 110 }, "nics": { @@ -1255,8 +1252,38 @@ Response Data: } ``` +### List Containers +**GET /v1/monitoring/container?namespace=\.\*&node=\.\*&podpo=\.*** + +Example: +``` +curl -X GET http://localhost:7890/v1/monitoring/containers +``` + +Response Data: +``` json +{ + "node-exporter": { ... }, + "prometheus": { ... }, + "tiller": { ... }, + "vortex-server": { ... }, + ... +``` + +Example: +``` +curl -X GET http://localhost:7890/v1/monitoring/containers\?namespace\=vortex\&node\=vortex-dev\&pod\=vortex-server-6945b797bb-jbszk +``` + +Response Data: +``` json +{ + "vortex-server": { ... } + } +``` + ### Get Container -**Get /v1/monitoring/pods/{pod}/{container}** +**Get /v1/monitoring/container/{id}** Example: ``` @@ -1267,18 +1294,22 @@ Response Data: ``` json { "detail": { - "containerName": "test1", - "createAt": 1535361241, - "status": "running", - "restartTime": 4, - "pod": "atest", - "namespace": "default", + "containerName": "prometheus", + "createAt": 0, + "pod": "prometheus-7f759794cb-52t54", + "namespace": "vortex", "node": "vortex-dev", - "image": "busybox:latest", + "image": "prom/prometheus:v2.2.1", "command": [ - "sleep", - "3600" - ] + "/bin/prometheus" + ], + "vNic": "" + }, + "status": { + "status": "running", + "waitingReason": "", + "terminatedReason": "", + "restartTime": 0 }, "resource": { "cpuUsagePercentage": [ diff --git a/src/prometheuscontroller/controller.go b/src/prometheuscontroller/controller.go index 625d1162..76c2957f 100644 --- a/src/prometheuscontroller/controller.go +++ b/src/prometheuscontroller/controller.go @@ -344,7 +344,7 @@ podStatusCheckingLoop: } // GetContainer will get container -func GetContainer(sp *serviceprovider.Container, podId string, containerId string) (entity.ContainerMetrics, error) { +func GetContainer(sp *serviceprovider.Container, id string) (entity.ContainerMetrics, error) { container := entity.ContainerMetrics{} // basic info from kube-state-metrics @@ -352,7 +352,7 @@ func GetContainer(sp *serviceprovider.Container, podId string, containerId strin expression.Metrics = []string{ "kube_pod_container_info", "kube_pod_container_status_restarts_total"} - expression.QueryLabels = map[string]string{"container": containerId, "pod": podId} + expression.QueryLabels = map[string]string{"container": id} str := basicExpr(expression.Metrics) str = queryExpr(str, expression.QueryLabels) @@ -365,7 +365,7 @@ func GetContainer(sp *serviceprovider.Container, podId string, containerId strin switch result.Metric["__name__"] { case "kube_pod_container_info": - container.Detail.ContainerName = containerId + container.Detail.ContainerName = id container.Detail.Pod = string(result.Metric["pod"]) container.Detail.Image = string(result.Metric["image"]) container.Detail.Namespace = string(result.Metric["namespace"]) @@ -380,7 +380,7 @@ func GetContainer(sp *serviceprovider.Container, podId string, containerId strin expression.Metrics = []string{ "container_start_time_seconds", "container_last_seen"} - expression.QueryLabels = map[string]string{"container_label_io_kubernetes_container_name": containerId, "container_label_io_kubernetes_pod_name": podId} + expression.QueryLabels = map[string]string{"container_label_io_kubernetes_container_name": id} str = basicExpr(expression.Metrics) str = queryExpr(str, expression.QueryLabels) @@ -404,7 +404,7 @@ func GetContainer(sp *serviceprovider.Container, podId string, containerId strin container.Detail.Status = "" expression.Metrics = []string{"kube_pod_container_status.*"} - expression.QueryLabels = map[string]string{"container": containerId, "pod": podId} + expression.QueryLabels = map[string]string{"container": id} str = basicExpr(expression.Metrics) str = queryExpr(str, expression.QueryLabels) @@ -443,7 +443,7 @@ containerStatusCheckingLoop: } for _, obj := range pod.Spec.Containers { - if obj.Name == containerId { + if obj.Name == id { container.Detail.Command = obj.Command break } @@ -455,7 +455,7 @@ containerStatusCheckingLoop: // Memory resource expression.Metrics = []string{"container_memory_usage_bytes"} - expression.QueryLabels = map[string]string{"container_label_io_kubernetes_container_name": containerId, "container_label_io_kubernetes_pod_name": podId} + expression.QueryLabels = map[string]string{"container_label_io_kubernetes_container_name": id} str = basicExpr(expression.Metrics) str = queryExpr(str, expression.QueryLabels) @@ -471,7 +471,7 @@ containerStatusCheckingLoop: // CPU resource expression.Metrics = []string{"container_cpu_usage_seconds_total"} - expression.QueryLabels = map[string]string{"container_label_io_kubernetes_container_name": containerId, "container_label_io_kubernetes_pod_name": podId} + expression.QueryLabels = map[string]string{"container_label_io_kubernetes_container_name": id} str = basicExpr(expression.Metrics) str = queryExpr(str, expression.QueryLabels) diff --git a/src/prometheuscontroller/controller_test.go b/src/prometheuscontroller/controller_test.go index 06b9b219..4b307eeb 100644 --- a/src/prometheuscontroller/controller_test.go +++ b/src/prometheuscontroller/controller_test.go @@ -136,10 +136,9 @@ func (suite *PrometheusExpressionTestSuite) TestGetContainer() { namespace := "vortex" pods, err := suite.sp.KubeCtl.GetPods(namespace) suite.NoError(err) - podName := pods[0].Name containerName := pods[0].Status.ContainerStatuses[0].Name - container, err := GetContainer(suite.sp, podName, containerName) + container, err := GetContainer(suite.sp, containerName) suite.NoError(err) suite.Equal(containerName, container.Detail.ContainerName) } diff --git a/src/server/handler_prometheus.go b/src/server/handler_prometheus.go index b463f7fa..f0ddfb6f 100644 --- a/src/server/handler_prometheus.go +++ b/src/server/handler_prometheus.go @@ -10,10 +10,9 @@ import ( func getContainerMetricsHandler(ctx *web.Context) { sp, req, resp := ctx.ServiceProvider, ctx.Request, ctx.Response - podId := req.PathParameter("pod") - containerId := req.PathParameter("container") + id := req.PathParameter("id") - container, err := pc.GetContainer(sp, podId, containerId) + container, err := pc.GetContainer(sp, id) if err != nil { response.InternalServerError(req.Request, resp.ResponseWriter, err) return @@ -24,7 +23,7 @@ func getContainerMetricsHandler(ctx *web.Context) { func getPodMetricsHandler(ctx *web.Context) { sp, req, resp := ctx.ServiceProvider, ctx.Request, ctx.Response - id := req.PathParameter("pod") + id := req.PathParameter("id") pod, err := pc.GetPod(sp, id) if err != nil { @@ -37,7 +36,7 @@ func getPodMetricsHandler(ctx *web.Context) { func getServiceMetricsHandler(ctx *web.Context) { sp, req, resp := ctx.ServiceProvider, ctx.Request, ctx.Response - id := req.PathParameter("service") + id := req.PathParameter("id") service, err := pc.GetService(sp, id) if err != nil { @@ -50,7 +49,7 @@ func getServiceMetricsHandler(ctx *web.Context) { func getControllerMetricsHandler(ctx *web.Context) { sp, req, resp := ctx.ServiceProvider, ctx.Request, ctx.Response - id := req.PathParameter("controller") + id := req.PathParameter("id") controller, err := pc.GetController(sp, id) if err != nil { @@ -63,7 +62,7 @@ func getControllerMetricsHandler(ctx *web.Context) { func getNodeMetricsHandler(ctx *web.Context) { sp, req, resp := ctx.ServiceProvider, ctx.Request, ctx.Response - id := req.PathParameter("node") + id := req.PathParameter("id") node, err := pc.GetNode(sp, id) if err != nil { @@ -74,6 +73,43 @@ func getNodeMetricsHandler(ctx *web.Context) { resp.WriteEntity(node) } +func listContainerMetricsHandler(ctx *web.Context) { + sp, req, resp := ctx.ServiceProvider, ctx.Request, ctx.Response + + query := query.New(req.Request.URL.Query()) + queryLabels := map[string]string{} + + if node, ok := query.Str("node"); ok { + queryLabels["node"] = node + } + + if namespace, ok := query.Str("namespace"); ok { + queryLabels["namespace"] = namespace + } + + if pod, ok := query.Str("pod"); ok { + queryLabels["pod"] = pod + } + + containerNameList, err := pc.ListContainerName(sp, queryLabels) + if err != nil { + response.InternalServerError(req.Request, resp.ResponseWriter, err) + return + } + + containerList := map[string]entity.ContainerMetrics{} + for _, containerName := range containerNameList { + container, err := pc.GetContainer(sp, containerName) + if err != nil { + response.InternalServerError(req.Request, resp.ResponseWriter, err) + return + } + containerList[containerName] = container + } + + resp.WriteEntity(containerList) +} + func listPodMetricsHandler(ctx *web.Context) { sp, req, resp := ctx.ServiceProvider, ctx.Request, ctx.Response @@ -194,7 +230,7 @@ func listNodeMetricsHandler(ctx *web.Context) { func listNodeNicsMetricsHandler(ctx *web.Context) { sp, req, resp := ctx.ServiceProvider, ctx.Request, ctx.Response - id := req.PathParameter("node") + id := req.PathParameter("id") nicList, err := pc.ListNodeNICs(sp, id) if err != nil { diff --git a/src/server/handler_prometheus_test.go b/src/server/handler_prometheus_test.go index c58f080b..02c268d1 100644 --- a/src/server/handler_prometheus_test.go +++ b/src/server/handler_prometheus_test.go @@ -106,14 +106,29 @@ func (suite *PrometheusTestSuite) TestGetPodMetrics() { assertResponseCode(suite.T(), http.StatusOK, httpWriter) } +func (suite *PrometheusTestSuite) TestListContainerMetrics() { + httpRequest, err := http.NewRequest("GET", "http://localhost:7890/v1/monitoring/containers/", nil) + suite.NoError(err) + + httpWriter := httptest.NewRecorder() + suite.wc.Dispatch(httpWriter, httpRequest) + assertResponseCode(suite.T(), http.StatusOK, httpWriter) + + httpRequest, err = http.NewRequest("GET", "http://localhost:7890/v1/monitoring/containers?node=.*&namespace=.*&pod=.*", nil) + suite.NoError(err) + + httpWriter = httptest.NewRecorder() + suite.wc.Dispatch(httpWriter, httpRequest) + assertResponseCode(suite.T(), http.StatusOK, httpWriter) +} + func (suite *PrometheusTestSuite) TestGetContainerMetrics() { namespace := "vortex" pods, err := suite.sp.KubeCtl.GetPods(namespace) suite.NoError(err) - podName := pods[0].Name containerName := pods[0].Status.ContainerStatuses[0].Name - httpRequest, err := http.NewRequest("GET", "http://localhost:7890/v1/monitoring/pods/"+podName+"/"+containerName, nil) + httpRequest, err := http.NewRequest("GET", "http://localhost:7890/v1/monitoring/containers/"+containerName, nil) suite.NoError(err) httpWriter := httptest.NewRecorder() diff --git a/src/server/route.go b/src/server/route.go index 24966349..b7c8ee19 100644 --- a/src/server/route.go +++ b/src/server/route.go @@ -162,19 +162,20 @@ func newMonitoringService(sp *serviceprovider.Container) *restful.WebService { webService.Path("/v1/monitoring").Consumes(restful.MIME_JSON, restful.MIME_JSON).Produces(restful.MIME_JSON, restful.MIME_JSON) // node webService.Route(webService.GET("/nodes").To(handler.RESTfulServiceHandler(sp, listNodeMetricsHandler))) - webService.Route(webService.GET("/nodes/{node}").To(handler.RESTfulServiceHandler(sp, getNodeMetricsHandler))) - webService.Route(webService.GET("/nodes/{node}/nics").To(handler.RESTfulServiceHandler(sp, listNodeNicsMetricsHandler))) + webService.Route(webService.GET("/nodes/{id}").To(handler.RESTfulServiceHandler(sp, getNodeMetricsHandler))) + webService.Route(webService.GET("/nodes/{id}/nics").To(handler.RESTfulServiceHandler(sp, listNodeNicsMetricsHandler))) // pod webService.Route(webService.GET("/pods").To(handler.RESTfulServiceHandler(sp, listPodMetricsHandler))) - webService.Route(webService.GET("/pods/{pod}").To(handler.RESTfulServiceHandler(sp, getPodMetricsHandler))) - //container - webService.Route(webService.GET("/pods/{pod}/{container}").To(handler.RESTfulServiceHandler(sp, getContainerMetricsHandler))) + webService.Route(webService.GET("/pods/{id}").To(handler.RESTfulServiceHandler(sp, getPodMetricsHandler))) + // container + webService.Route(webService.GET("/containers").To(handler.RESTfulServiceHandler(sp, listContainerMetricsHandler))) + webService.Route(webService.GET("/containers/{id}").To(handler.RESTfulServiceHandler(sp, getContainerMetricsHandler))) // service webService.Route(webService.GET("/services").To(handler.RESTfulServiceHandler(sp, listServiceMetricsHandler))) - webService.Route(webService.GET("/services/{service}").To(handler.RESTfulServiceHandler(sp, getServiceMetricsHandler))) + webService.Route(webService.GET("/services/{id}").To(handler.RESTfulServiceHandler(sp, getServiceMetricsHandler))) // controller webService.Route(webService.GET("/controllers").To(handler.RESTfulServiceHandler(sp, listControllerMetricsHandler))) - webService.Route(webService.GET("/controllers/{controller}").To(handler.RESTfulServiceHandler(sp, getControllerMetricsHandler))) + webService.Route(webService.GET("/controllers/{id}").To(handler.RESTfulServiceHandler(sp, getControllerMetricsHandler))) return webService }