Switch to ginkgo/gomega for tests

This switches over to ginkgo/gomega for tests, which makes writing
certain tests easier/more fluent in the future.
This commit is contained in:
Solly Ross 2018-09-25 14:27:33 -04:00
parent c5801455ec
commit cc08a1fb41
260 changed files with 184637 additions and 6412 deletions

View file

@ -0,0 +1,29 @@
/*
Copyright 2018 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.
*/
package provider_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestProvider(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Custom Metrics Provider Suite")
}

View file

@ -19,13 +19,11 @@ package provider
import (
"context"
"fmt"
"sort"
"testing"
"time"
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/runtime/schema"
fakedyn "k8s.io/client-go/dynamic/fake"
@ -87,13 +85,13 @@ func (c *fakePromClient) QueryRange(_ context.Context, r prom.Range, query prom.
return prom.QueryResult{}, nil
}
func setupPrometheusProvider(t *testing.T) (provider.CustomMetricsProvider, *fakePromClient) {
func setupPrometheusProvider() (provider.CustomMetricsProvider, *fakePromClient) {
fakeProm := &fakePromClient{}
fakeKubeClient := &fakedyn.FakeDynamicClient{}
cfg := config.DefaultConfig(1*time.Minute, "")
namers, err := NamersFromConfig(cfg, restMapper())
require.NoError(t, err)
Expect(err).NotTo(HaveOccurred())
prov, _ := NewPrometheusProvider(restMapper(), fakeKubeClient, fakeProm, namers, fakeProviderUpdateInterval)
@ -129,39 +127,35 @@ func setupPrometheusProvider(t *testing.T) (provider.CustomMetricsProvider, *fak
return prov, fakeProm
}
func TestListAllMetrics(t *testing.T) {
// setup
prov, fakeProm := setupPrometheusProvider(t)
var _ = Describe("Custom Metrics Provider", func() {
It("should be able to list all metrics", func() {
By("setting up the provider")
prov, fakeProm := setupPrometheusProvider()
// assume we have no updates
require.Len(t, prov.ListAllMetrics(), 0, "assume: should have no metrics updates at the start")
By("ensuring that no metrics are present before we start listing")
Expect(prov.ListAllMetrics()).To(BeEmpty())
// set the acceptible interval (now until the next update, with a bit of wiggle room)
startTime := pmodel.Now().Add(-1*fakeProviderUpdateInterval - fakeProviderUpdateInterval/10)
fakeProm.acceptibleInterval = pmodel.Interval{Start: startTime, End: 0}
By("setting the acceptible interval to now until the next update, with a bit of wiggle room")
startTime := pmodel.Now().Add(-1*fakeProviderUpdateInterval - fakeProviderUpdateInterval/10)
fakeProm.acceptibleInterval = pmodel.Interval{Start: startTime, End: 0}
// update the metrics (without actually calling RunUntil, so we can avoid timing issues)
lister := prov.(*prometheusProvider).SeriesRegistry.(*cachingMetricsLister)
require.NoError(t, lister.updateMetrics())
By("updating the list of available metrics")
// don't call RunUntil to avoid timing issue
lister := prov.(*prometheusProvider).SeriesRegistry.(*cachingMetricsLister)
Expect(lister.updateMetrics()).To(Succeed())
// list/sort the metrics
actualMetrics := prov.ListAllMetrics()
sort.Sort(metricInfoSorter(actualMetrics))
expectedMetrics := []provider.CustomMetricInfo{
{schema.GroupResource{Resource: "services"}, true, "ingress_hits"},
{schema.GroupResource{Group: "extensions", Resource: "ingresses"}, true, "ingress_hits"},
{schema.GroupResource{Resource: "pods"}, true, "ingress_hits"},
{schema.GroupResource{Resource: "namespaces"}, false, "ingress_hits"},
{schema.GroupResource{Resource: "services"}, true, "service_proxy_packets"},
{schema.GroupResource{Resource: "namespaces"}, false, "service_proxy_packets"},
{schema.GroupResource{Group: "extensions", Resource: "deployments"}, true, "work_queue_wait"},
{schema.GroupResource{Resource: "namespaces"}, false, "work_queue_wait"},
{schema.GroupResource{Resource: "namespaces"}, false, "some_usage"},
{schema.GroupResource{Resource: "pods"}, true, "some_usage"},
}
sort.Sort(metricInfoSorter(expectedMetrics))
// assert that we got what we expected
assert.Equal(t, expectedMetrics, actualMetrics)
}
By("listing all metrics, and checking that they contain the expected results")
Expect(prov.ListAllMetrics()).To(ConsistOf(
provider.CustomMetricInfo{schema.GroupResource{Resource: "services"}, true, "ingress_hits"},
provider.CustomMetricInfo{schema.GroupResource{Group: "extensions", Resource: "ingresses"}, true, "ingress_hits"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "ingress_hits"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "ingress_hits"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "services"}, true, "service_proxy_packets"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "service_proxy_packets"},
provider.CustomMetricInfo{schema.GroupResource{Group: "extensions", Resource: "deployments"}, true, "work_queue_wait"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "work_queue_wait"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "some_usage"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_usage"},
))
})
})

View file

@ -17,14 +17,13 @@ limitations under the License.
package provider
import (
"sort"
"testing"
"fmt"
"time"
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
pmodel "github.com/prometheus/common/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
coreapi "k8s.io/api/core/v1"
extapi "k8s.io/api/extensions/v1beta1"
apimeta "k8s.io/apimachinery/pkg/api/meta"
@ -51,10 +50,10 @@ func restMapper() apimeta.RESTMapper {
return mapper
}
func setupMetricNamer(t testing.TB) []MetricNamer {
func setupMetricNamer() []MetricNamer {
cfg := config.DefaultConfig(1*time.Minute, "kube_")
namers, err := NamersFromConfig(cfg, restMapper())
require.NoError(t, err)
Expect(err).NotTo(HaveOccurred())
return namers
}
@ -117,204 +116,153 @@ var seriesRegistryTestSeries = [][]prom.Series{
},
}
func TestSeriesRegistry(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
type regTestCase struct {
title string
info provider.CustomMetricInfo
namespace string
resourceNames []string
namers := setupMetricNamer(t)
registry := &basicSeriesRegistry{
mapper: restMapper(),
}
expectedQuery string
}
// set up the registry
require.NoError(registry.SetSeries(seriesRegistryTestSeries, namers))
var _ = Describe("Series Registry", func() {
var (
registry *basicSeriesRegistry
)
// make sure each metric got registered and can form queries
testCases := []struct {
title string
info provider.CustomMetricInfo
namespace string
resourceNames []string
BeforeEach(func() {
namers := setupMetricNamer()
registry = &basicSeriesRegistry{
mapper: restMapper(),
}
Expect(registry.SetSeries(seriesRegistryTestSeries, namers)).To(Succeed())
})
expectedQuery string
}{
// container metrics
{
title: "container metrics gauge / multiple resource names",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_usage"},
namespace: "somens",
resourceNames: []string{"somepod1", "somepod2"},
Context("with the default configuration rules", func() {
// make sure each metric got registered and can form queries
testCases := []regTestCase{
// container metrics
{
title: "container metrics gauge / multiple resource names",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_usage"},
namespace: "somens",
resourceNames: []string{"somepod1", "somepod2"},
expectedQuery: "sum(container_some_usage{namespace=\"somens\",pod_name=~\"somepod1|somepod2\",container_name!=\"POD\"}) by (pod_name)",
},
{
title: "container metrics counter",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_count"},
namespace: "somens",
resourceNames: []string{"somepod1", "somepod2"},
expectedQuery: "sum(container_some_usage{namespace=\"somens\",pod_name=~\"somepod1|somepod2\",container_name!=\"POD\"}) by (pod_name)",
},
{
title: "container metrics counter",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_count"},
namespace: "somens",
resourceNames: []string{"somepod1", "somepod2"},
expectedQuery: "sum(rate(container_some_count_total{namespace=\"somens\",pod_name=~\"somepod1|somepod2\",container_name!=\"POD\"}[1m])) by (pod_name)",
},
{
title: "container metrics seconds counter",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_time"},
namespace: "somens",
resourceNames: []string{"somepod1", "somepod2"},
expectedQuery: "sum(rate(container_some_count_total{namespace=\"somens\",pod_name=~\"somepod1|somepod2\",container_name!=\"POD\"}[1m])) by (pod_name)",
},
{
title: "container metrics seconds counter",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_time"},
namespace: "somens",
resourceNames: []string{"somepod1", "somepod2"},
expectedQuery: "sum(rate(container_some_time_seconds_total{namespace=\"somens\",pod_name=~\"somepod1|somepod2\",container_name!=\"POD\"}[1m])) by (pod_name)",
},
// namespaced metrics
{
title: "namespaced metrics counter / multidimensional (service)",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "service"}, true, "ingress_hits"},
namespace: "somens",
resourceNames: []string{"somesvc"},
expectedQuery: "sum(rate(container_some_time_seconds_total{namespace=\"somens\",pod_name=~\"somepod1|somepod2\",container_name!=\"POD\"}[1m])) by (pod_name)",
},
// namespaced metrics
{
title: "namespaced metrics counter / multidimensional (service)",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "service"}, true, "ingress_hits"},
namespace: "somens",
resourceNames: []string{"somesvc"},
expectedQuery: "sum(rate(ingress_hits_total{kube_namespace=\"somens\",kube_service=\"somesvc\"}[1m])) by (kube_service)",
},
{
title: "namespaced metrics counter / multidimensional (ingress)",
info: provider.CustomMetricInfo{schema.GroupResource{Group: "extensions", Resource: "ingress"}, true, "ingress_hits"},
namespace: "somens",
resourceNames: []string{"someingress"},
expectedQuery: "sum(rate(ingress_hits_total{kube_namespace=\"somens\",kube_service=\"somesvc\"}[1m])) by (kube_service)",
},
{
title: "namespaced metrics counter / multidimensional (ingress)",
info: provider.CustomMetricInfo{schema.GroupResource{Group: "extensions", Resource: "ingress"}, true, "ingress_hits"},
namespace: "somens",
resourceNames: []string{"someingress"},
expectedQuery: "sum(rate(ingress_hits_total{kube_namespace=\"somens\",kube_ingress=\"someingress\"}[1m])) by (kube_ingress)",
},
{
title: "namespaced metrics counter / multidimensional (pod)",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "pod"}, true, "ingress_hits"},
namespace: "somens",
resourceNames: []string{"somepod"},
expectedQuery: "sum(rate(ingress_hits_total{kube_namespace=\"somens\",kube_ingress=\"someingress\"}[1m])) by (kube_ingress)",
},
{
title: "namespaced metrics counter / multidimensional (pod)",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "pod"}, true, "ingress_hits"},
namespace: "somens",
resourceNames: []string{"somepod"},
expectedQuery: "sum(rate(ingress_hits_total{kube_namespace=\"somens\",kube_pod=\"somepod\"}[1m])) by (kube_pod)",
},
{
title: "namespaced metrics gauge",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "service"}, true, "service_proxy_packets"},
namespace: "somens",
resourceNames: []string{"somesvc"},
expectedQuery: "sum(rate(ingress_hits_total{kube_namespace=\"somens\",kube_pod=\"somepod\"}[1m])) by (kube_pod)",
},
{
title: "namespaced metrics gauge",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "service"}, true, "service_proxy_packets"},
namespace: "somens",
resourceNames: []string{"somesvc"},
expectedQuery: "sum(service_proxy_packets{kube_namespace=\"somens\",kube_service=\"somesvc\"}) by (kube_service)",
},
{
title: "namespaced metrics seconds counter",
info: provider.CustomMetricInfo{schema.GroupResource{Group: "extensions", Resource: "deployment"}, true, "work_queue_wait"},
namespace: "somens",
resourceNames: []string{"somedep"},
expectedQuery: "sum(service_proxy_packets{kube_namespace=\"somens\",kube_service=\"somesvc\"}) by (kube_service)",
},
{
title: "namespaced metrics seconds counter",
info: provider.CustomMetricInfo{schema.GroupResource{Group: "extensions", Resource: "deployment"}, true, "work_queue_wait"},
namespace: "somens",
resourceNames: []string{"somedep"},
expectedQuery: "sum(rate(work_queue_wait_seconds_total{kube_namespace=\"somens\",kube_deployment=\"somedep\"}[1m])) by (kube_deployment)",
},
// non-namespaced series
{
title: "root scoped metrics gauge",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "node"}, false, "node_gigawatts"},
resourceNames: []string{"somenode"},
expectedQuery: "sum(rate(work_queue_wait_seconds_total{kube_namespace=\"somens\",kube_deployment=\"somedep\"}[1m])) by (kube_deployment)",
},
// non-namespaced series
{
title: "root scoped metrics gauge",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "node"}, false, "node_gigawatts"},
resourceNames: []string{"somenode"},
expectedQuery: "sum(node_gigawatts{kube_node=\"somenode\"}) by (kube_node)",
},
{
title: "root scoped metrics counter",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "persistentvolume"}, false, "volume_claims"},
resourceNames: []string{"somepv"},
expectedQuery: "sum(node_gigawatts{kube_node=\"somenode\"}) by (kube_node)",
},
{
title: "root scoped metrics counter",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "persistentvolume"}, false, "volume_claims"},
resourceNames: []string{"somepv"},
expectedQuery: "sum(rate(volume_claims_total{kube_persistentvolume=\"somepv\"}[1m])) by (kube_persistentvolume)",
},
{
title: "root scoped metrics seconds counter",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "node"}, false, "node_fan"},
resourceNames: []string{"somenode"},
expectedQuery: "sum(rate(volume_claims_total{kube_persistentvolume=\"somepv\"}[1m])) by (kube_persistentvolume)",
},
{
title: "root scoped metrics seconds counter",
info: provider.CustomMetricInfo{schema.GroupResource{Resource: "node"}, false, "node_fan"},
resourceNames: []string{"somenode"},
expectedQuery: "sum(rate(node_fan_seconds_total{kube_node=\"somenode\"}[1m])) by (kube_node)",
},
}
for _, testCase := range testCases {
outputQuery, found := registry.QueryForMetric(testCase.info, testCase.namespace, testCase.resourceNames...)
if !assert.True(found, "%s: metric %v should available", testCase.title, testCase.info) {
continue
expectedQuery: "sum(rate(node_fan_seconds_total{kube_node=\"somenode\"}[1m])) by (kube_node)",
},
}
assert.Equal(prom.Selector(testCase.expectedQuery), outputQuery, "%s: metric %v should have produced the correct query for %v in namespace %s", testCase.title, testCase.info, testCase.resourceNames, testCase.namespace)
}
for _, tc := range testCases {
tc := tc // copy to avoid iteration variable issues
It(fmt.Sprintf("should build a query for %s", tc.title), func() {
By(fmt.Sprintf("composing the query for the %s metric on %v in namespace %s", tc.info, tc.resourceNames, tc.namespace))
outputQuery, found := registry.QueryForMetric(tc.info, tc.namespace, tc.resourceNames...)
Expect(found).To(BeTrue(), "metric %s should be available", tc.info)
allMetrics := registry.ListAllMetrics()
expectedMetrics := []provider.CustomMetricInfo{
{schema.GroupResource{Resource: "pods"}, true, "some_count"},
{schema.GroupResource{Resource: "namespaces"}, false, "some_count"},
{schema.GroupResource{Resource: "pods"}, true, "some_time"},
{schema.GroupResource{Resource: "namespaces"}, false, "some_time"},
{schema.GroupResource{Resource: "pods"}, true, "some_usage"},
{schema.GroupResource{Resource: "namespaces"}, false, "some_usage"},
{schema.GroupResource{Resource: "services"}, true, "ingress_hits"},
{schema.GroupResource{Group: "extensions", Resource: "ingresses"}, true, "ingress_hits"},
{schema.GroupResource{Resource: "pods"}, true, "ingress_hits"},
{schema.GroupResource{Resource: "namespaces"}, false, "ingress_hits"},
{schema.GroupResource{Resource: "services"}, true, "service_proxy_packets"},
{schema.GroupResource{Resource: "namespaces"}, false, "service_proxy_packets"},
{schema.GroupResource{Group: "extensions", Resource: "deployments"}, true, "work_queue_wait"},
{schema.GroupResource{Resource: "namespaces"}, false, "work_queue_wait"},
{schema.GroupResource{Resource: "nodes"}, false, "node_gigawatts"},
{schema.GroupResource{Resource: "persistentvolumes"}, false, "volume_claims"},
{schema.GroupResource{Resource: "nodes"}, false, "node_fan"},
}
// sort both for easy comparison
sort.Sort(metricInfoSorter(allMetrics))
sort.Sort(metricInfoSorter(expectedMetrics))
assert.Equal(expectedMetrics, allMetrics, "should have listed all expected metrics")
}
func BenchmarkSetSeries(b *testing.B) {
namers := setupMetricNamer(b)
registry := &basicSeriesRegistry{
mapper: restMapper(),
}
numDuplicates := 10000
newSeriesSlices := make([][]prom.Series, len(seriesRegistryTestSeries))
for i, seriesSlice := range seriesRegistryTestSeries {
newSlice := make([]prom.Series, len(seriesSlice)*numDuplicates)
for j, series := range seriesSlice {
for k := 0; k < numDuplicates; k++ {
newSlice[j*numDuplicates+k] = series
}
}
newSeriesSlices[i] = newSlice
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
registry.SetSeries(newSeriesSlices, namers)
}
}
// metricInfoSorter is a sort.Interface for sorting provider.CustomMetricInfos
type metricInfoSorter []provider.CustomMetricInfo
func (s metricInfoSorter) Len() int {
return len(s)
}
func (s metricInfoSorter) Less(i, j int) bool {
infoI := s[i]
infoJ := s[j]
if infoI.Metric == infoJ.Metric {
if infoI.GroupResource == infoJ.GroupResource {
return infoI.Namespaced
By("verifying that the query is as expected")
Expect(outputQuery).To(Equal(prom.Selector(tc.expectedQuery)))
})
}
if infoI.GroupResource.Group == infoJ.GroupResource.Group {
return infoI.GroupResource.Resource < infoJ.GroupResource.Resource
}
return infoI.GroupResource.Group < infoJ.GroupResource.Group
}
return infoI.Metric < infoJ.Metric
}
func (s metricInfoSorter) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
It("should list all metrics", func() {
Expect(registry.ListAllMetrics()).To(ConsistOf(
provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_count"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "some_count"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_time"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "some_time"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "some_usage"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "some_usage"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "services"}, true, "ingress_hits"},
provider.CustomMetricInfo{schema.GroupResource{Group: "extensions", Resource: "ingresses"}, true, "ingress_hits"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "pods"}, true, "ingress_hits"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "ingress_hits"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "services"}, true, "service_proxy_packets"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "service_proxy_packets"},
provider.CustomMetricInfo{schema.GroupResource{Group: "extensions", Resource: "deployments"}, true, "work_queue_wait"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "namespaces"}, false, "work_queue_wait"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "nodes"}, false, "node_gigawatts"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "persistentvolumes"}, false, "volume_claims"},
provider.CustomMetricInfo{schema.GroupResource{Resource: "nodes"}, false, "node_fan"},
))
})
})
})