func TestMsgPack(t *testing.T) { // register msg pack entity restful.RegisterEntityAccessor(MIME_MSGPACK, NewEntityAccessorMsgPack()) type Tool struct { Name string Vendor string } // Write httpWriter := httptest.NewRecorder() mpack := &Tool{Name: "json", Vendor: "apple"} resp := restful.NewResponse(httpWriter) resp.SetRequestAccepts("application/x-msgpack,*/*;q=0.8") err := resp.WriteEntity(mpack) if err != nil { t.Errorf("err %v", err) } // Read bodyReader := bytes.NewReader(httpWriter.Body.Bytes()) httpRequest, _ := http.NewRequest("GET", "/test", bodyReader) httpRequest.Header.Set("Content-Type", MIME_MSGPACK) request := restful.NewRequest(httpRequest) readMsgPack := new(Tool) err = request.ReadEntity(&readMsgPack) if err != nil { t.Errorf("err %v", err) } if equal := reflect.DeepEqual(mpack, readMsgPack); !equal { t.Fatalf("should not be error") } }
func TestFetchAggregations(t *testing.T) { api, src := prepApi() nowTime := time.Now().UTC().Truncate(time.Second) src.nowTime = nowTime nowFunc = func() time.Time { return nowTime } tests := []struct { test string bucketSize string start string end string labels string fun func(*restful.Request, *restful.Response) pathParams map[string]string expectedMetricReq metricReq expectedStatus int }{ { test: "cluster aggregations", fun: api.clusterAggregations, start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), pathParams: map[string]string{ "metric-name": "some-metric", "aggregations": "count,average", }, expectedMetricReq: metricReq{ name: "some-metric", start: nowTime.Add(-10 * time.Second), keys: []core.HistoricalKey{ {ObjectType: core.MetricSetTypeCluster}, }, }, }, { test: "node aggregations", fun: api.nodeAggregations, start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), pathParams: map[string]string{ "metric-name": "some-metric", "node-name": "node1", "aggregations": "count,average", }, expectedMetricReq: metricReq{ name: "some-metric", start: nowTime.Add(-10 * time.Second), keys: []core.HistoricalKey{ {ObjectType: core.MetricSetTypeNode, NodeName: "node1"}, }, }, }, { test: "namespace aggregations", fun: api.namespaceAggregations, start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), pathParams: map[string]string{ "metric-name": "some-metric", "namespace-name": "ns1", "aggregations": "count,average", }, expectedMetricReq: metricReq{ name: "some-metric", start: nowTime.Add(-10 * time.Second), keys: []core.HistoricalKey{ {ObjectType: core.MetricSetTypeNamespace, NamespaceName: "ns1"}, }, }, }, { test: "pod name aggregations", fun: api.podAggregations, start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), pathParams: map[string]string{ "metric-name": "some-metric", "namespace-name": "ns1", "pod-name": "pod1", "aggregations": "count,average", }, expectedMetricReq: metricReq{ name: "some-metric", start: nowTime.Add(-10 * time.Second), keys: []core.HistoricalKey{ {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod1"}, }, }, }, { test: "pod id aggregations", fun: api.podAggregations, start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), pathParams: map[string]string{ "metric-name": "some-metric", "pod-id": "pod-1-id", "aggregations": "count,average", }, expectedMetricReq: metricReq{ name: "some-metric", start: nowTime.Add(-10 * time.Second), keys: []core.HistoricalKey{ {ObjectType: core.MetricSetTypePod, PodId: "pod-1-id"}, }, }, }, { test: "pod name container aggregations", fun: api.podContainerAggregations, start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), pathParams: map[string]string{ "metric-name": "some-metric", "namespace-name": "ns1", "pod-name": "pod1", "container-name": "cont1", "aggregations": "count,average", }, expectedMetricReq: metricReq{ name: "some-metric", start: nowTime.Add(-10 * time.Second), keys: []core.HistoricalKey{ {ObjectType: core.MetricSetTypePodContainer, NamespaceName: "ns1", PodName: "pod1", ContainerName: "cont1"}, }, }, }, { test: "pod id container aggregations", fun: api.podContainerAggregations, start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), pathParams: map[string]string{ "metric-name": "some-metric", "pod-id": "pod-1-id", "container-name": "cont1", "aggregations": "count,average", }, expectedMetricReq: metricReq{ name: "some-metric", start: nowTime.Add(-10 * time.Second), keys: []core.HistoricalKey{ {ObjectType: core.MetricSetTypePodContainer, PodId: "pod-1-id", ContainerName: "cont1"}, }, }, }, { test: "system container aggregations", fun: api.freeContainerAggregations, start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), pathParams: map[string]string{ "metric-name": "some-metric", "node-name": "node1", "container-name": "cont1", "aggregations": "count,average", }, expectedMetricReq: metricReq{ name: "some-metric", start: nowTime.Add(-10 * time.Second), keys: []core.HistoricalKey{ {ObjectType: core.MetricSetTypeSystemContainer, NodeName: "node1", ContainerName: "cont1"}, }, }, }, { test: "aggregations with end and bucket", fun: api.clusterAggregations, pathParams: map[string]string{ "metric-name": "some-metric", "aggregations": "count,average", }, start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), end: nowTime.Format(time.RFC3339), bucketSize: "20s", expectedMetricReq: metricReq{ name: "some-metric", keys: []core.HistoricalKey{ {ObjectType: core.MetricSetTypeCluster}, }, start: nowTime.Add(-10 * time.Second), end: nowTime, bucketSize: 20 * time.Second, }, }, { test: "aggregations with labels", fun: api.clusterAggregations, pathParams: map[string]string{ "metric-name": "some-metric", "aggregations": "count,average", }, labels: "somelbl:v1,otherlbl:v2.3:4", start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), expectedMetricReq: metricReq{ name: "some-metric", keys: []core.HistoricalKey{ {ObjectType: core.MetricSetTypeCluster}, }, start: nowTime.Add(-10 * time.Second), labels: map[string]string{"somelbl": "v1", "otherlbl": "v2.3:4"}, }, }, { test: "aggregations with bad start time", fun: api.clusterAggregations, start: "afdsfd", expectedStatus: http.StatusBadRequest, }, { test: "aggregations with fetch error", fun: api.clusterAggregations, start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), pathParams: map[string]string{ "metric-name": "invalid", "aggregations": "count,average", }, expectedStatus: http.StatusInternalServerError, }, { test: "aggregations with no start time", fun: api.clusterAggregations, expectedStatus: http.StatusBadRequest, }, } assert := assert.New(t) restful.DefaultResponseMimeType = restful.MIME_JSON // doesn't particularly correspond to the query -- we're just using it to // test conversion between internal types countVal := uint64(10) expectedNormalVals := types.MetricAggregationResult{ BucketSize: 10 * time.Second, Buckets: []types.MetricAggregationBucket{ { Timestamp: src.nowTime.Add(-10 * time.Second), Count: &countVal, }, }, } aggList := []core.AggregationType{ core.AggregationTypeCount, core.AggregationTypeAverage, } for _, test := range tests { queryParams := make(url.Values) queryParams.Add("start", test.start) queryParams.Add("end", test.end) queryParams.Add("bucket", test.bucketSize) queryParams.Add("labels", test.labels) u := &url.URL{RawQuery: queryParams.Encode()} req := restful.NewRequest(&http.Request{URL: u}) pathParams := req.PathParameters() for k, v := range test.pathParams { pathParams[k] = v } recorder := &fakeRespRecorder{ data: new(bytes.Buffer), headers: make(http.Header), } resp := restful.NewResponse(recorder) test.fun(req, resp) if test.expectedStatus != 0 { assert.Equal(test.expectedStatus, recorder.status, "for test %q: status should have been an error status", test.test) } else { if !assert.Equal(http.StatusOK, recorder.status, "for test %q: status should have been OK (200)", test.test) { continue } actualReq := src.metricRequests[len(src.metricRequests)-1] if test.expectedMetricReq.end.IsZero() { test.expectedMetricReq.end = nowTime } test.expectedMetricReq.aggregations = aggList assert.Equal(test.expectedMetricReq, actualReq, "for test %q: expected a different metric request to have been placed", test.test) actualVals := types.MetricAggregationResult{} if err := json.Unmarshal(recorder.data.Bytes(), &actualVals); err != nil { t.Fatalf("Unexpected error: %v", err) } assert.Equal(expectedNormalVals, actualVals, "for test %q: should have gotten expected JSON", test.test) } } listTests := []struct { test string start string labels string end string fun func(*restful.Request, *restful.Response) pathParams map[string]string expectedMetricReq metricReq expectedStatus int }{ { test: "pod id list aggregations", fun: api.podListAggregations, start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), pathParams: map[string]string{ "metric-name": "some-metric", "pod-id-list": "pod-id-1,pod-id-2,pod-id-3", "aggregations": "count,average", }, expectedMetricReq: metricReq{ name: "some-metric", keys: []core.HistoricalKey{ {ObjectType: core.MetricSetTypePod, PodId: "pod-id-1"}, {ObjectType: core.MetricSetTypePod, PodId: "pod-id-2"}, {ObjectType: core.MetricSetTypePod, PodId: "pod-id-3"}, }, start: nowTime.Add(-10 * time.Second), }, }, { test: "pod name list aggregations", fun: api.podListAggregations, start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), pathParams: map[string]string{ "metric-name": "some-metric", "namespace-name": "ns1", "pod-list": "pod1,pod2,pod3", "aggregations": "count,average", }, expectedMetricReq: metricReq{ name: "some-metric", keys: []core.HistoricalKey{ {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod1"}, {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod2"}, {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod3"}, }, start: nowTime.Add(-10 * time.Second), }, }, { test: "pod list aggregations with labels", fun: api.podListAggregations, start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), labels: "somelbl:v1,otherlbl:v2.3:4", pathParams: map[string]string{ "metric-name": "some-metric", "namespace-name": "ns1", "pod-list": "pod1,pod2,pod3", "aggregations": "count,average", }, expectedMetricReq: metricReq{ name: "some-metric", keys: []core.HistoricalKey{ {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod1"}, {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod2"}, {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod3"}, }, start: nowTime.Add(-10 * time.Second), labels: map[string]string{"somelbl": "v1", "otherlbl": "v2.3:4"}, }, }, { test: "pod list aggregations with bad start time", fun: api.podListAggregations, start: "afdsfd", expectedStatus: http.StatusBadRequest, }, { test: "pod list aggregations with fetch error", fun: api.podListAggregations, start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), pathParams: map[string]string{ "metric-name": "invalid", "aggregations": "count,average", }, expectedStatus: http.StatusInternalServerError, }, { test: "pod list aggregations with no start time", fun: api.podListAggregations, expectedStatus: http.StatusBadRequest, }, } expectedListVals := types.MetricAggregationResultList{ Items: []types.MetricAggregationResult{ expectedNormalVals, expectedNormalVals, expectedNormalVals, }, } for _, test := range listTests { queryParams := make(url.Values) queryParams.Add("start", test.start) queryParams.Add("end", test.end) queryParams.Add("labels", test.labels) u := &url.URL{RawQuery: queryParams.Encode()} req := restful.NewRequest(&http.Request{URL: u}) pathParams := req.PathParameters() for k, v := range test.pathParams { pathParams[k] = v } recorder := &fakeRespRecorder{ data: new(bytes.Buffer), headers: make(http.Header), } resp := restful.NewResponse(recorder) test.fun(req, resp) if test.expectedStatus != 0 { assert.Equal(test.expectedStatus, recorder.status, "for test %q: status should have been an error status", test.test) } else { if !assert.Equal(http.StatusOK, recorder.status, "for test %q: status should have been OK (200)", test.test) { continue } actualReq := src.metricRequests[len(src.metricRequests)-1] if test.expectedMetricReq.end.IsZero() { test.expectedMetricReq.end = nowTime } test.expectedMetricReq.aggregations = aggList assert.Equal(test.expectedMetricReq, actualReq, "for test %q: expected a different metric request to have been placed", test.test) actualVals := types.MetricAggregationResultList{} if err := json.Unmarshal(recorder.data.Bytes(), &actualVals); err != nil { t.Fatalf("Unexpected error: %v", err) } assert.Equal(expectedListVals, actualVals, "for test %q: should have gotten expected JSON", test.test) } } }
func TestListObjects(t *testing.T) { api, src := prepApi() src.nodes = []string{"node1", "node2"} src.namespaces = []string{"ns1", "ns2"} src.podsForNamespace = map[string][]string{ "ns1": {"pod1", "pod2"}, } src.containersForNode = map[string][]string{ "node1": {"x/y/z", "a/b/c"}, } tests := []struct { name string fun func(request *restful.Request, response *restful.Response) pathParams map[string]string expectedNames []string }{ { name: "nodes", fun: api.nodeList, expectedNames: []string{"node1", "node2"}, }, { name: "namespaces", fun: api.namespaceList, expectedNames: []string{"ns1", "ns2"}, }, { name: "pods in namespace", fun: api.namespacePodList, pathParams: map[string]string{"namespace-name": "ns1"}, expectedNames: []string{"pod1", "pod2"}, }, { name: "free containers on node", fun: api.nodeSystemContainerList, pathParams: map[string]string{ "node-name": "node1", }, expectedNames: []string{"x/y/z", "a/b/c"}, }, } assert := assert.New(t) restful.DefaultResponseMimeType = restful.MIME_JSON for _, test := range tests { req := restful.NewRequest(&http.Request{}) pathParams := req.PathParameters() for k, v := range test.pathParams { pathParams[k] = v } recorder := &fakeRespRecorder{ data: new(bytes.Buffer), headers: make(http.Header), } resp := restful.NewResponse(recorder) test.fun(req, resp) actualNames := []string{} if err := json.Unmarshal(recorder.data.Bytes(), &actualNames); err != nil { t.Fatalf("Unexpected error: %v", err) } assert.Equal(http.StatusOK, recorder.status, "status should have been OK (200)") assert.Equal(test.expectedNames, actualNames, "should have gotten expected JSON") } for _, test := range tests { if len(test.pathParams) == 0 { // don't test tests with no parameters for invalid parameters continue } req := restful.NewRequest(&http.Request{}) pathParams := req.PathParameters() for k := range test.pathParams { pathParams[k] = "some-other-value" } recorder := &fakeRespRecorder{ data: new(bytes.Buffer), headers: make(http.Header), } resp := restful.NewResponse(recorder) test.fun(req, resp) assert.Equal(http.StatusInternalServerError, recorder.status, "status should have been InternalServerError (500)") } }
func TestAvailableMetrics(t *testing.T) { api, src := prepApi() src.metricNames = map[core.HistoricalKey][]string{ core.HistoricalKey{ ObjectType: core.MetricSetTypeCluster, }: {"cm1", "cm2"}, core.HistoricalKey{ ObjectType: core.MetricSetTypeNode, NodeName: "somenode1", }: {"nm1", "nm2"}, core.HistoricalKey{ ObjectType: core.MetricSetTypeNamespace, NamespaceName: "somens1", }: {"nsm1", "nsm2"}, core.HistoricalKey{ ObjectType: core.MetricSetTypePod, NamespaceName: "somens1", PodName: "somepod1", }: {"pm1", "pm2"}, core.HistoricalKey{ ObjectType: core.MetricSetTypePodContainer, NamespaceName: "somens1", PodName: "somepod1", ContainerName: "somecont1", }: {"pcm1", "pcm2"}, core.HistoricalKey{ ObjectType: core.MetricSetTypeSystemContainer, NodeName: "somenode1", ContainerName: "somecont1", }: {"ncm1", "ncm2"}, } tests := []struct { name string fun func(request *restful.Request, response *restful.Response) pathParams map[string]string expectedNames []string }{ { name: "cluster metrics", fun: api.availableClusterMetrics, pathParams: map[string]string{}, expectedNames: []string{"cm1", "cm2"}, }, { name: "node metrics", fun: api.availableNodeMetrics, pathParams: map[string]string{"node-name": "somenode1"}, expectedNames: []string{"nm1", "nm2"}, }, { name: "namespace metrics", fun: api.availableNamespaceMetrics, pathParams: map[string]string{"namespace-name": "somens1"}, expectedNames: []string{"nsm1", "nsm2"}, }, { name: "pod metrics", fun: api.availablePodMetrics, pathParams: map[string]string{ "namespace-name": "somens1", "pod-name": "somepod1", }, expectedNames: []string{"pm1", "pm2"}, }, { name: "pod container metrics", fun: api.availablePodContainerMetrics, pathParams: map[string]string{ "namespace-name": "somens1", "pod-name": "somepod1", "container-name": "somecont1", }, expectedNames: []string{"pcm1", "pcm2"}, }, { name: "free container metrics", fun: api.availableFreeContainerMetrics, pathParams: map[string]string{ "node-name": "somenode1", "container-name": "somecont1", }, expectedNames: []string{"ncm1", "ncm2"}, }, } assert := assert.New(t) restful.DefaultResponseMimeType = restful.MIME_JSON for _, test := range tests { req := restful.NewRequest(&http.Request{}) pathParams := req.PathParameters() for k, v := range test.pathParams { pathParams[k] = v } recorder := &fakeRespRecorder{ data: new(bytes.Buffer), headers: make(http.Header), } resp := restful.NewResponse(recorder) test.fun(req, resp) actualNames := []string{} if err := json.Unmarshal(recorder.data.Bytes(), &actualNames); err != nil { t.Fatalf("Unexpected error: %v", err) } assert.Equal(http.StatusOK, recorder.status, "status should have been OK (200)") assert.Equal(test.expectedNames, actualNames, "should have gotten expected JSON") } for _, test := range tests { if len(test.pathParams) == 0 { // don't test tests with no parameters for invalid parameters continue } req := restful.NewRequest(&http.Request{}) pathParams := req.PathParameters() for k := range test.pathParams { pathParams[k] = "some-other-value" } recorder := &fakeRespRecorder{ data: new(bytes.Buffer), headers: make(http.Header), } resp := restful.NewResponse(recorder) test.fun(req, resp) assert.Equal(http.StatusInternalServerError, recorder.status, "status should have been InternalServerError (500)") } }