mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 19:01:49 +00:00
Add Snapshot Controller e2e metric tests
Signed-off-by: Grant Griffiths <ggriffiths@purestorage.com>
This commit is contained in:
parent
9739592e84
commit
564e531aa7
@ -22,6 +22,8 @@ spec:
|
|||||||
serviceAccount: volume-snapshot-controller
|
serviceAccount: volume-snapshot-controller
|
||||||
containers:
|
containers:
|
||||||
- name: volume-snapshot-controller
|
- name: volume-snapshot-controller
|
||||||
image: k8s.gcr.io/sig-storage/snapshot-controller:v3.0.2
|
image: k8s.gcr.io/sig-storage/snapshot-controller:v4.0.0
|
||||||
args:
|
args:
|
||||||
- "--v=5"
|
- "--v=5"
|
||||||
|
- "--metrics-path=/metrics"
|
||||||
|
- "--http-endpoint=:9102"
|
||||||
|
@ -259,7 +259,7 @@ func verifyRemainingObjects(f *framework.Framework, objects map[string]int) (boo
|
|||||||
func gatherMetrics(f *framework.Framework) {
|
func gatherMetrics(f *framework.Framework) {
|
||||||
ginkgo.By("Gathering metrics")
|
ginkgo.By("Gathering metrics")
|
||||||
var summary framework.TestDataSummary
|
var summary framework.TestDataSummary
|
||||||
grabber, err := e2emetrics.NewMetricsGrabber(f.ClientSet, f.KubemarkExternalClusterClientSet, false, false, true, false, false)
|
grabber, err := e2emetrics.NewMetricsGrabber(f.ClientSet, f.KubemarkExternalClusterClientSet, false, false, true, false, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
framework.Logf("Failed to create MetricsGrabber. Skipping metrics gathering.")
|
framework.Logf("Failed to create MetricsGrabber. Skipping metrics gathering.")
|
||||||
} else {
|
} else {
|
||||||
|
@ -296,7 +296,7 @@ func (f *Framework) BeforeEach() {
|
|||||||
|
|
||||||
gatherMetricsAfterTest := TestContext.GatherMetricsAfterTest == "true" || TestContext.GatherMetricsAfterTest == "master"
|
gatherMetricsAfterTest := TestContext.GatherMetricsAfterTest == "true" || TestContext.GatherMetricsAfterTest == "master"
|
||||||
if gatherMetricsAfterTest && TestContext.IncludeClusterAutoscalerMetrics {
|
if gatherMetricsAfterTest && TestContext.IncludeClusterAutoscalerMetrics {
|
||||||
grabber, err := e2emetrics.NewMetricsGrabber(f.ClientSet, f.KubemarkExternalClusterClientSet, !ProviderIs("kubemark"), false, false, false, TestContext.IncludeClusterAutoscalerMetrics)
|
grabber, err := e2emetrics.NewMetricsGrabber(f.ClientSet, f.KubemarkExternalClusterClientSet, !ProviderIs("kubemark"), false, false, false, TestContext.IncludeClusterAutoscalerMetrics, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Logf("Failed to create MetricsGrabber (skipping ClusterAutoscaler metrics gathering before test): %v", err)
|
Logf("Failed to create MetricsGrabber (skipping ClusterAutoscaler metrics gathering before test): %v", err)
|
||||||
} else {
|
} else {
|
||||||
@ -449,7 +449,7 @@ func (f *Framework) AfterEach() {
|
|||||||
ginkgo.By("Gathering metrics")
|
ginkgo.By("Gathering metrics")
|
||||||
// Grab apiserver, scheduler, controller-manager metrics and (optionally) nodes' kubelet metrics.
|
// Grab apiserver, scheduler, controller-manager metrics and (optionally) nodes' kubelet metrics.
|
||||||
grabMetricsFromKubelets := TestContext.GatherMetricsAfterTest != "master" && !ProviderIs("kubemark")
|
grabMetricsFromKubelets := TestContext.GatherMetricsAfterTest != "master" && !ProviderIs("kubemark")
|
||||||
grabber, err := e2emetrics.NewMetricsGrabber(f.ClientSet, f.KubemarkExternalClusterClientSet, grabMetricsFromKubelets, true, true, true, TestContext.IncludeClusterAutoscalerMetrics)
|
grabber, err := e2emetrics.NewMetricsGrabber(f.ClientSet, f.KubemarkExternalClusterClientSet, grabMetricsFromKubelets, true, true, true, TestContext.IncludeClusterAutoscalerMetrics, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Logf("Failed to create MetricsGrabber (skipping metrics gathering): %v", err)
|
Logf("Failed to create MetricsGrabber (skipping metrics gathering): %v", err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -139,7 +139,7 @@ func getKubeletMetricsFromNode(c clientset.Interface, nodeName string) (KubeletM
|
|||||||
if c == nil {
|
if c == nil {
|
||||||
return GrabKubeletMetricsWithoutProxy(nodeName, "/metrics")
|
return GrabKubeletMetricsWithoutProxy(nodeName, "/metrics")
|
||||||
}
|
}
|
||||||
grabber, err := NewMetricsGrabber(c, nil, true, false, false, false, false)
|
grabber, err := NewMetricsGrabber(c, nil, true, false, false, false, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return KubeletMetrics{}, err
|
return KubeletMetrics{}, err
|
||||||
}
|
}
|
||||||
|
@ -38,12 +38,15 @@ const (
|
|||||||
// kubeControllerManagerPort is the default port for the controller manager status server.
|
// kubeControllerManagerPort is the default port for the controller manager status server.
|
||||||
kubeControllerManagerPort = 10257
|
kubeControllerManagerPort = 10257
|
||||||
metricsProxyPod = "metrics-proxy"
|
metricsProxyPod = "metrics-proxy"
|
||||||
|
// snapshotControllerPort is the port for the snapshot controller
|
||||||
|
snapshotControllerPort = 9102
|
||||||
)
|
)
|
||||||
|
|
||||||
// Collection is metrics collection of components
|
// Collection is metrics collection of components
|
||||||
type Collection struct {
|
type Collection struct {
|
||||||
APIServerMetrics APIServerMetrics
|
APIServerMetrics APIServerMetrics
|
||||||
ControllerManagerMetrics ControllerManagerMetrics
|
ControllerManagerMetrics ControllerManagerMetrics
|
||||||
|
SnapshotControllerMetrics SnapshotControllerMetrics
|
||||||
KubeletMetrics map[string]KubeletMetrics
|
KubeletMetrics map[string]KubeletMetrics
|
||||||
SchedulerMetrics SchedulerMetrics
|
SchedulerMetrics SchedulerMetrics
|
||||||
ClusterAutoscalerMetrics ClusterAutoscalerMetrics
|
ClusterAutoscalerMetrics ClusterAutoscalerMetrics
|
||||||
@ -58,20 +61,25 @@ type Grabber struct {
|
|||||||
grabFromKubelets bool
|
grabFromKubelets bool
|
||||||
grabFromScheduler bool
|
grabFromScheduler bool
|
||||||
grabFromClusterAutoscaler bool
|
grabFromClusterAutoscaler bool
|
||||||
|
grabFromSnapshotController bool
|
||||||
kubeScheduler string
|
kubeScheduler string
|
||||||
waitForSchedulerReadyOnce sync.Once
|
waitForSchedulerReadyOnce sync.Once
|
||||||
kubeControllerManager string
|
kubeControllerManager string
|
||||||
waitForControllerManagerReadyOnce sync.Once
|
waitForControllerManagerReadyOnce sync.Once
|
||||||
|
snapshotController string
|
||||||
|
waitForSnapshotControllerReadyOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMetricsGrabber returns new metrics which are initialized.
|
// NewMetricsGrabber returns new metrics which are initialized.
|
||||||
func NewMetricsGrabber(c clientset.Interface, ec clientset.Interface, kubelets bool, scheduler bool, controllers bool, apiServer bool, clusterAutoscaler bool) (*Grabber, error) {
|
func NewMetricsGrabber(c clientset.Interface, ec clientset.Interface, kubelets bool, scheduler bool, controllers bool, apiServer bool, clusterAutoscaler bool, snapshotController bool) (*Grabber, error) {
|
||||||
|
|
||||||
kubeScheduler := ""
|
kubeScheduler := ""
|
||||||
kubeControllerManager := ""
|
kubeControllerManager := ""
|
||||||
|
snapshotControllerManager := ""
|
||||||
|
|
||||||
regKubeScheduler := regexp.MustCompile("kube-scheduler-.*")
|
regKubeScheduler := regexp.MustCompile("kube-scheduler-.*")
|
||||||
regKubeControllerManager := regexp.MustCompile("kube-controller-manager-.*")
|
regKubeControllerManager := regexp.MustCompile("kube-controller-manager-.*")
|
||||||
|
regSnapshotController := regexp.MustCompile("volume-snapshot-controller.*")
|
||||||
|
|
||||||
podList, err := c.CoreV1().Pods(metav1.NamespaceSystem).List(context.TODO(), metav1.ListOptions{})
|
podList, err := c.CoreV1().Pods(metav1.NamespaceSystem).List(context.TODO(), metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -87,7 +95,10 @@ func NewMetricsGrabber(c clientset.Interface, ec clientset.Interface, kubelets b
|
|||||||
if regKubeControllerManager.MatchString(pod.Name) {
|
if regKubeControllerManager.MatchString(pod.Name) {
|
||||||
kubeControllerManager = pod.Name
|
kubeControllerManager = pod.Name
|
||||||
}
|
}
|
||||||
if kubeScheduler != "" && kubeControllerManager != "" {
|
if regSnapshotController.MatchString(pod.Name) {
|
||||||
|
snapshotControllerManager = pod.Name
|
||||||
|
}
|
||||||
|
if kubeScheduler != "" && kubeControllerManager != "" && snapshotControllerManager != "" {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,6 +110,10 @@ func NewMetricsGrabber(c clientset.Interface, ec clientset.Interface, kubelets b
|
|||||||
controllers = false
|
controllers = false
|
||||||
klog.Warningf("Can't find kube-controller-manager pod. Grabbing metrics from kube-controller-manager is disabled.")
|
klog.Warningf("Can't find kube-controller-manager pod. Grabbing metrics from kube-controller-manager is disabled.")
|
||||||
}
|
}
|
||||||
|
if snapshotControllerManager == "" {
|
||||||
|
snapshotController = false
|
||||||
|
klog.Warningf("Can't find snapshot-controller pod. Grabbing metrics from snapshot-controller is disabled.")
|
||||||
|
}
|
||||||
if ec == nil {
|
if ec == nil {
|
||||||
klog.Warningf("Did not receive an external client interface. Grabbing metrics from ClusterAutoscaler is disabled.")
|
klog.Warningf("Did not receive an external client interface. Grabbing metrics from ClusterAutoscaler is disabled.")
|
||||||
}
|
}
|
||||||
@ -111,8 +126,10 @@ func NewMetricsGrabber(c clientset.Interface, ec clientset.Interface, kubelets b
|
|||||||
grabFromKubelets: kubelets,
|
grabFromKubelets: kubelets,
|
||||||
grabFromScheduler: scheduler,
|
grabFromScheduler: scheduler,
|
||||||
grabFromClusterAutoscaler: clusterAutoscaler,
|
grabFromClusterAutoscaler: clusterAutoscaler,
|
||||||
|
grabFromSnapshotController: snapshotController,
|
||||||
kubeScheduler: kubeScheduler,
|
kubeScheduler: kubeScheduler,
|
||||||
kubeControllerManager: kubeControllerManager,
|
kubeControllerManager: kubeControllerManager,
|
||||||
|
snapshotController: snapshotControllerManager,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,6 +237,48 @@ func (g *Grabber) GrabFromControllerManager() (ControllerManagerMetrics, error)
|
|||||||
return parseControllerManagerMetrics(output)
|
return parseControllerManagerMetrics(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GrabFromSnapshotController returns metrics from controller manager
|
||||||
|
func (g *Grabber) GrabFromSnapshotController(podName string, port int) (SnapshotControllerMetrics, error) {
|
||||||
|
if g.snapshotController == "" {
|
||||||
|
return SnapshotControllerMetrics{}, fmt.Errorf("SnapshotController pod is not registered. Skipping SnapshotController's metrics gathering")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use overrides if provided via test config flags.
|
||||||
|
// Otherwise, use the default snapshot controller pod name and port.
|
||||||
|
if podName == "" {
|
||||||
|
podName = g.snapshotController
|
||||||
|
}
|
||||||
|
if port == 0 {
|
||||||
|
port = snapshotControllerPort
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
g.waitForSnapshotControllerReadyOnce.Do(func() {
|
||||||
|
if readyErr := e2epod.WaitForPodsReady(g.client, metav1.NamespaceSystem, podName, 0); readyErr != nil {
|
||||||
|
err = fmt.Errorf("error waiting for snapshot controller pod to be ready: %w", readyErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastMetricsFetchErr error
|
||||||
|
if metricsWaitErr := wait.PollImmediate(time.Second, time.Minute, func() (bool, error) {
|
||||||
|
_, lastMetricsFetchErr = g.getMetricsFromPod(g.client, podName, metav1.NamespaceSystem, port)
|
||||||
|
return lastMetricsFetchErr == nil, nil
|
||||||
|
}); metricsWaitErr != nil {
|
||||||
|
err = fmt.Errorf("error waiting for snapshot controller pod to expose metrics: %v; %v", metricsWaitErr, lastMetricsFetchErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return SnapshotControllerMetrics{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := g.getMetricsFromPod(g.client, podName, metav1.NamespaceSystem, port)
|
||||||
|
if err != nil {
|
||||||
|
return SnapshotControllerMetrics{}, err
|
||||||
|
}
|
||||||
|
return parseSnapshotControllerMetrics(output)
|
||||||
|
}
|
||||||
|
|
||||||
// GrabFromAPIServer returns metrics from API server
|
// GrabFromAPIServer returns metrics from API server
|
||||||
func (g *Grabber) GrabFromAPIServer() (APIServerMetrics, error) {
|
func (g *Grabber) GrabFromAPIServer() (APIServerMetrics, error) {
|
||||||
output, err := g.getMetricsFromAPIServer()
|
output, err := g.getMetricsFromAPIServer()
|
||||||
@ -257,6 +316,14 @@ func (g *Grabber) Grab() (Collection, error) {
|
|||||||
result.ControllerManagerMetrics = metrics
|
result.ControllerManagerMetrics = metrics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if g.grabFromSnapshotController {
|
||||||
|
metrics, err := g.GrabFromSnapshotController(g.snapshotController, snapshotControllerPort)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
} else {
|
||||||
|
result.SnapshotControllerMetrics = metrics
|
||||||
|
}
|
||||||
|
}
|
||||||
if g.grabFromClusterAutoscaler {
|
if g.grabFromClusterAutoscaler {
|
||||||
metrics, err := g.GrabFromClusterAutoscaler()
|
metrics, err := g.GrabFromClusterAutoscaler()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
40
test/e2e/framework/metrics/snapshot_controller_metrics.go
Normal file
40
test/e2e/framework/metrics/snapshot_controller_metrics.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 metrics
|
||||||
|
|
||||||
|
import "k8s.io/component-base/metrics/testutil"
|
||||||
|
|
||||||
|
// SnapshotControllerMetrics is metrics for controller manager
|
||||||
|
type SnapshotControllerMetrics testutil.Metrics
|
||||||
|
|
||||||
|
// Equal returns true if all metrics are the same as the arguments.
|
||||||
|
func (m *SnapshotControllerMetrics) Equal(o SnapshotControllerMetrics) bool {
|
||||||
|
return (*testutil.Metrics)(m).Equal(testutil.Metrics(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSnapshotControllerMetrics() SnapshotControllerMetrics {
|
||||||
|
result := testutil.NewMetrics()
|
||||||
|
return SnapshotControllerMetrics(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSnapshotControllerMetrics(data string) (SnapshotControllerMetrics, error) {
|
||||||
|
result := newSnapshotControllerMetrics()
|
||||||
|
if err := testutil.ParseMetrics(data, (*testutil.Metrics)(&result)); err != nil {
|
||||||
|
return SnapshotControllerMetrics{}, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
@ -182,6 +182,12 @@ type TestContextType struct {
|
|||||||
|
|
||||||
// DockerConfigFile is a file that contains credentials which can be used to pull images from certain private registries, needed for a test.
|
// DockerConfigFile is a file that contains credentials which can be used to pull images from certain private registries, needed for a test.
|
||||||
DockerConfigFile string
|
DockerConfigFile string
|
||||||
|
|
||||||
|
// SnapshotControllerPodName is the name used for identifying the snapshot controller pod.
|
||||||
|
SnapshotControllerPodName string
|
||||||
|
|
||||||
|
// SnapshotControllerHTTPPort the port used for communicating with the snapshot controller HTTP endpoint.
|
||||||
|
SnapshotControllerHTTPPort int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeKillerConfig describes configuration of NodeKiller -- a utility to
|
// NodeKillerConfig describes configuration of NodeKiller -- a utility to
|
||||||
@ -315,6 +321,9 @@ func RegisterCommonFlags(flags *flag.FlagSet) {
|
|||||||
flags.StringVar(&TestContext.ProgressReportURL, "progress-report-url", "", "The URL to POST progress updates to as the suite runs to assist in aiding integrations. If empty, no messages sent.")
|
flags.StringVar(&TestContext.ProgressReportURL, "progress-report-url", "", "The URL to POST progress updates to as the suite runs to assist in aiding integrations. If empty, no messages sent.")
|
||||||
flags.StringVar(&TestContext.SpecSummaryOutput, "spec-dump", "", "The file to dump all ginkgo.SpecSummary to after tests run. If empty, no objects are saved/printed.")
|
flags.StringVar(&TestContext.SpecSummaryOutput, "spec-dump", "", "The file to dump all ginkgo.SpecSummary to after tests run. If empty, no objects are saved/printed.")
|
||||||
flags.StringVar(&TestContext.DockerConfigFile, "docker-config-file", "", "A file that contains credentials which can be used to pull images from certain private registries, needed for a test.")
|
flags.StringVar(&TestContext.DockerConfigFile, "docker-config-file", "", "A file that contains credentials which can be used to pull images from certain private registries, needed for a test.")
|
||||||
|
|
||||||
|
flags.StringVar(&TestContext.SnapshotControllerPodName, "snapshot-controller-pod-name", "", "The pod name to use for identifying the snapshot controller in the kube-system namespace.")
|
||||||
|
flags.IntVar(&TestContext.SnapshotControllerHTTPPort, "snapshot-controller-http-port", 0, "The port to use for snapshot controller HTTP communication.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterClusterFlags registers flags specific to the cluster e2e test suite.
|
// RegisterClusterFlags registers flags specific to the cluster e2e test suite.
|
||||||
|
@ -33,6 +33,7 @@ const (
|
|||||||
pvDeleteSlowTimeout = 20 * time.Minute
|
pvDeleteSlowTimeout = 20 * time.Minute
|
||||||
snapshotCreateTimeout = 5 * time.Minute
|
snapshotCreateTimeout = 5 * time.Minute
|
||||||
snapshotDeleteTimeout = 5 * time.Minute
|
snapshotDeleteTimeout = 5 * time.Minute
|
||||||
|
snapshotControllerMetricsTimeout = 5 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
// TimeoutContext contains timeout settings for several actions.
|
// TimeoutContext contains timeout settings for several actions.
|
||||||
@ -77,6 +78,9 @@ type TimeoutContext struct {
|
|||||||
|
|
||||||
// SnapshotDelete is how long for snapshot to delete snapshotContent.
|
// SnapshotDelete is how long for snapshot to delete snapshotContent.
|
||||||
SnapshotDelete time.Duration
|
SnapshotDelete time.Duration
|
||||||
|
|
||||||
|
// SnapshotControllerMetrics is how long to wait for snapshot controller metrics.
|
||||||
|
SnapshotControllerMetrics time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTimeoutContextWithDefaults returns a TimeoutContext with default values.
|
// NewTimeoutContextWithDefaults returns a TimeoutContext with default values.
|
||||||
@ -95,5 +99,6 @@ func NewTimeoutContextWithDefaults() *TimeoutContext {
|
|||||||
PVDeleteSlow: pvDeleteSlowTimeout,
|
PVDeleteSlow: pvDeleteSlowTimeout,
|
||||||
SnapshotCreate: snapshotCreateTimeout,
|
SnapshotCreate: snapshotCreateTimeout,
|
||||||
SnapshotDelete: snapshotDeleteTimeout,
|
SnapshotDelete: snapshotDeleteTimeout,
|
||||||
|
SnapshotControllerMetrics: snapshotControllerMetricsTimeout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ var _ = instrumentation.SIGDescribe("MetricsGrabber", func() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
gomega.Eventually(func() error {
|
gomega.Eventually(func() error {
|
||||||
grabber, err = e2emetrics.NewMetricsGrabber(c, ec, true, true, true, true, true)
|
grabber, err = e2emetrics.NewMetricsGrabber(c, ec, true, true, true, true, true, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create metrics grabber: %v", err)
|
return fmt.Errorf("failed to create metrics grabber: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/kubelet/events"
|
"k8s.io/kubernetes/pkg/kubelet/events"
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
e2eevents "k8s.io/kubernetes/test/e2e/framework/events"
|
e2eevents "k8s.io/kubernetes/test/e2e/framework/events"
|
||||||
|
e2emetrics "k8s.io/kubernetes/test/e2e/framework/metrics"
|
||||||
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
|
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
|
||||||
e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
|
e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
|
||||||
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
|
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
|
||||||
@ -1621,6 +1622,107 @@ var _ = utils.SIGDescribe("CSI mock volume", func() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ginkgo.Context("CSI Snapshot Controller metrics [Feature:VolumeSnapshotDataSource]", func() {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
pattern storageframework.TestPattern
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "snapshot controller should emit dynamic CreateSnapshot, CreateSnapshotAndReady, and DeleteSnapshot metrics",
|
||||||
|
pattern: storageframework.DynamicSnapshotDelete,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "snapshot controller should emit pre-provisioned CreateSnapshot, CreateSnapshotAndReady, and DeleteSnapshot metrics",
|
||||||
|
pattern: storageframework.PreprovisionedSnapshotDelete,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
ginkgo.It(test.name, func() {
|
||||||
|
init(testParameters{
|
||||||
|
disableAttach: true,
|
||||||
|
registerDriver: true,
|
||||||
|
enableSnapshot: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
sDriver, ok := m.driver.(storageframework.SnapshottableTestDriver)
|
||||||
|
if !ok {
|
||||||
|
e2eskipper.Skipf("mock driver does not support snapshots -- skipping")
|
||||||
|
}
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
metricsGrabber, err := e2emetrics.NewMetricsGrabber(m.config.Framework.ClientSet, nil, false, false, false, false, false, true)
|
||||||
|
if err != nil {
|
||||||
|
framework.Failf("Error creating metrics grabber : %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab initial metrics - if this fails, snapshot controller metrics are not setup. Skip in this case.
|
||||||
|
_, err = metricsGrabber.GrabFromSnapshotController(framework.TestContext.SnapshotControllerPodName, framework.TestContext.SnapshotControllerHTTPPort)
|
||||||
|
if err != nil {
|
||||||
|
e2eskipper.Skipf("Snapshot controller metrics not found -- skipping")
|
||||||
|
}
|
||||||
|
|
||||||
|
ginkgo.By("getting all initial metric values")
|
||||||
|
metricsTestConfig := newSnapshotMetricsTestConfig("snapshot_controller_operation_total_seconds_count",
|
||||||
|
"count",
|
||||||
|
m.config.GetUniqueDriverName(),
|
||||||
|
"CreateSnapshot",
|
||||||
|
"success",
|
||||||
|
"",
|
||||||
|
test.pattern)
|
||||||
|
createSnapshotMetrics := newSnapshotControllerMetrics(metricsTestConfig, metricsGrabber)
|
||||||
|
originalCreateSnapshotCount, _ := createSnapshotMetrics.getSnapshotControllerMetricValue()
|
||||||
|
metricsTestConfig.operationName = "CreateSnapshotAndReady"
|
||||||
|
createSnapshotAndReadyMetrics := newSnapshotControllerMetrics(metricsTestConfig, metricsGrabber)
|
||||||
|
originalCreateSnapshotAndReadyCount, _ := createSnapshotAndReadyMetrics.getSnapshotControllerMetricValue()
|
||||||
|
|
||||||
|
metricsTestConfig.operationName = "DeleteSnapshot"
|
||||||
|
deleteSnapshotMetrics := newSnapshotControllerMetrics(metricsTestConfig, metricsGrabber)
|
||||||
|
originalDeleteSnapshotCount, _ := deleteSnapshotMetrics.getSnapshotControllerMetricValue()
|
||||||
|
|
||||||
|
ginkgo.By("Creating storage class")
|
||||||
|
var sc *storagev1.StorageClass
|
||||||
|
if dDriver, ok := m.driver.(storageframework.DynamicPVTestDriver); ok {
|
||||||
|
sc = dDriver.GetDynamicProvisionStorageClass(m.config, "")
|
||||||
|
}
|
||||||
|
class, err := m.cs.StorageV1().StorageClasses().Create(context.TODO(), sc, metav1.CreateOptions{})
|
||||||
|
framework.ExpectNoError(err, "Failed to create storage class: %v", err)
|
||||||
|
m.sc[class.Name] = class
|
||||||
|
pvc := e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
|
||||||
|
Name: "snapshot-test-pvc",
|
||||||
|
StorageClassName: &(class.Name),
|
||||||
|
}, f.Namespace.Name)
|
||||||
|
|
||||||
|
ginkgo.By(fmt.Sprintf("Creating PVC %s/%s", pvc.Namespace, pvc.Name))
|
||||||
|
pvc, err = m.cs.CoreV1().PersistentVolumeClaims(f.Namespace.Name).Create(context.TODO(), pvc, metav1.CreateOptions{})
|
||||||
|
framework.ExpectNoError(err, "Failed to create claim: %v", err)
|
||||||
|
|
||||||
|
ginkgo.By("Wait for PVC to be Bound")
|
||||||
|
_, err = e2epv.WaitForPVClaimBoundPhase(m.cs, []*v1.PersistentVolumeClaim{pvc}, 1*time.Minute)
|
||||||
|
framework.ExpectNoError(err, "Failed to create claim: %v", err)
|
||||||
|
|
||||||
|
ginkgo.By("Creating snapshot")
|
||||||
|
parameters := map[string]string{}
|
||||||
|
sr := storageframework.CreateSnapshotResource(sDriver, m.config, test.pattern, pvc.Name, pvc.Namespace, f.Timeouts, parameters)
|
||||||
|
framework.ExpectNoError(err, "failed to create snapshot")
|
||||||
|
|
||||||
|
ginkgo.By("Checking for CreateSnapshot metrics")
|
||||||
|
createSnapshotMetrics.waitForSnapshotControllerMetric(originalCreateSnapshotCount+1.0, f.Timeouts.SnapshotControllerMetrics)
|
||||||
|
|
||||||
|
ginkgo.By("Checking for CreateSnapshotAndReady metrics")
|
||||||
|
err = utils.WaitForSnapshotReady(m.config.Framework.DynamicClient, pvc.Namespace, sr.Vs.GetName(), framework.Poll, f.Timeouts.SnapshotCreate)
|
||||||
|
framework.ExpectNoError(err, "failed to wait for snapshot ready")
|
||||||
|
createSnapshotAndReadyMetrics.waitForSnapshotControllerMetric(originalCreateSnapshotAndReadyCount+1.0, f.Timeouts.SnapshotControllerMetrics)
|
||||||
|
|
||||||
|
// delete the snapshot and check if the snapshot is deleted
|
||||||
|
deleteSnapshot(m.cs, m.config, sr.Vs)
|
||||||
|
|
||||||
|
ginkgo.By("check for delete metrics")
|
||||||
|
metricsTestConfig.operationName = "DeleteSnapshot"
|
||||||
|
deleteSnapshotMetrics.waitForSnapshotControllerMetric(originalDeleteSnapshotCount+1.0, f.Timeouts.SnapshotControllerMetrics)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
func deleteSnapshot(cs clientset.Interface, config *storageframework.PerTestConfig, snapshot *unstructured.Unstructured) {
|
func deleteSnapshot(cs clientset.Interface, config *storageframework.PerTestConfig, snapshot *unstructured.Unstructured) {
|
||||||
@ -2136,3 +2238,183 @@ func createPreHook(method string, callback func(counter int64) error) *drivers.H
|
|||||||
}(),
|
}(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type snapshotMetricsTestConfig struct {
|
||||||
|
// expected values
|
||||||
|
metricName string
|
||||||
|
metricType string
|
||||||
|
driverName string
|
||||||
|
operationName string
|
||||||
|
operationStatus string
|
||||||
|
snapshotType string
|
||||||
|
le string
|
||||||
|
}
|
||||||
|
|
||||||
|
type snapshotControllerMetrics struct {
|
||||||
|
// configuration for metric
|
||||||
|
cfg snapshotMetricsTestConfig
|
||||||
|
metricsGrabber *e2emetrics.Grabber
|
||||||
|
|
||||||
|
// results
|
||||||
|
countMetrics map[string]float64
|
||||||
|
sumMetrics map[string]float64
|
||||||
|
bucketMetrics map[string]float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSnapshotMetricsTestConfig(metricName, metricType, driverName, operationName, operationStatus, le string, pattern storageframework.TestPattern) snapshotMetricsTestConfig {
|
||||||
|
var snapshotType string
|
||||||
|
switch pattern.SnapshotType {
|
||||||
|
case storageframework.DynamicCreatedSnapshot:
|
||||||
|
snapshotType = "dynamic"
|
||||||
|
|
||||||
|
case storageframework.PreprovisionedCreatedSnapshot:
|
||||||
|
snapshotType = "pre-provisioned"
|
||||||
|
|
||||||
|
default:
|
||||||
|
framework.Failf("invalid snapshotType: %v", pattern.SnapshotType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return snapshotMetricsTestConfig{
|
||||||
|
metricName: metricName,
|
||||||
|
metricType: metricType,
|
||||||
|
driverName: driverName,
|
||||||
|
operationName: operationName,
|
||||||
|
operationStatus: operationStatus,
|
||||||
|
snapshotType: snapshotType,
|
||||||
|
le: le,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSnapshotControllerMetrics(cfg snapshotMetricsTestConfig, metricsGrabber *e2emetrics.Grabber) *snapshotControllerMetrics {
|
||||||
|
return &snapshotControllerMetrics{
|
||||||
|
cfg: cfg,
|
||||||
|
metricsGrabber: metricsGrabber,
|
||||||
|
|
||||||
|
countMetrics: make(map[string]float64),
|
||||||
|
sumMetrics: make(map[string]float64),
|
||||||
|
bucketMetrics: make(map[string]float64),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (scm *snapshotControllerMetrics) waitForSnapshotControllerMetric(expectedValue float64, timeout time.Duration) {
|
||||||
|
metricKey := scm.getMetricKey()
|
||||||
|
if successful := utils.WaitUntil(10*time.Second, timeout, func() bool {
|
||||||
|
// get metric value
|
||||||
|
actualValue, err := scm.getSnapshotControllerMetricValue()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Another operation could have finished from a previous test,
|
||||||
|
// so we check if we have at least the expected value.
|
||||||
|
if actualValue < expectedValue {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}); successful {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
scm.showMetricsFailure(metricKey)
|
||||||
|
framework.Failf("Unable to get valid snapshot controller metrics after %v", timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (scm *snapshotControllerMetrics) getSnapshotControllerMetricValue() (float64, error) {
|
||||||
|
metricKey := scm.getMetricKey()
|
||||||
|
|
||||||
|
// grab and parse into readable format
|
||||||
|
err := scm.grabSnapshotControllerMetrics()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
metrics := scm.getMetricsTable()
|
||||||
|
actual, ok := metrics[metricKey]
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("did not find metric for key %s", metricKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
return actual, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (scm *snapshotControllerMetrics) getMetricsTable() map[string]float64 {
|
||||||
|
var metrics map[string]float64
|
||||||
|
switch scm.cfg.metricType {
|
||||||
|
case "count":
|
||||||
|
metrics = scm.countMetrics
|
||||||
|
|
||||||
|
case "sum":
|
||||||
|
metrics = scm.sumMetrics
|
||||||
|
|
||||||
|
case "bucket":
|
||||||
|
metrics = scm.bucketMetrics
|
||||||
|
}
|
||||||
|
|
||||||
|
return metrics
|
||||||
|
}
|
||||||
|
|
||||||
|
func (scm *snapshotControllerMetrics) showMetricsFailure(metricKey string) {
|
||||||
|
framework.Logf("failed to find metric key %s inside of the following metrics:", metricKey)
|
||||||
|
|
||||||
|
metrics := scm.getMetricsTable()
|
||||||
|
for k, v := range metrics {
|
||||||
|
framework.Logf("%s: %v", k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (scm *snapshotControllerMetrics) grabSnapshotControllerMetrics() error {
|
||||||
|
// pull all metrics
|
||||||
|
metrics, err := scm.metricsGrabber.GrabFromSnapshotController(framework.TestContext.SnapshotControllerPodName, framework.TestContext.SnapshotControllerHTTPPort)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for method, samples := range metrics {
|
||||||
|
|
||||||
|
for _, sample := range samples {
|
||||||
|
operationName := string(sample.Metric["operation_name"])
|
||||||
|
driverName := string(sample.Metric["driver_name"])
|
||||||
|
operationStatus := string(sample.Metric["operation_status"])
|
||||||
|
snapshotType := string(sample.Metric["snapshot_type"])
|
||||||
|
le := string(sample.Metric["le"])
|
||||||
|
key := snapshotMetricKey(scm.cfg.metricName, driverName, operationName, operationStatus, snapshotType, le)
|
||||||
|
|
||||||
|
switch method {
|
||||||
|
case "snapshot_controller_operation_total_seconds_count":
|
||||||
|
for _, sample := range samples {
|
||||||
|
scm.countMetrics[key] = float64(sample.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "snapshot_controller_operation_total_seconds_sum":
|
||||||
|
for _, sample := range samples {
|
||||||
|
scm.sumMetrics[key] = float64(sample.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "snapshot_controller_operation_total_seconds_bucket":
|
||||||
|
for _, sample := range samples {
|
||||||
|
scm.bucketMetrics[key] = float64(sample.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (scm *snapshotControllerMetrics) getMetricKey() string {
|
||||||
|
return snapshotMetricKey(scm.cfg.metricName, scm.cfg.driverName, scm.cfg.operationName, scm.cfg.operationStatus, scm.cfg.snapshotType, scm.cfg.le)
|
||||||
|
}
|
||||||
|
|
||||||
|
func snapshotMetricKey(metricName, driverName, operationName, operationStatus, snapshotType, le string) string {
|
||||||
|
key := driverName
|
||||||
|
|
||||||
|
// build key for shorthand metrics storage
|
||||||
|
for _, s := range []string{metricName, operationName, operationStatus, snapshotType, le} {
|
||||||
|
if s != "" {
|
||||||
|
key = fmt.Sprintf("%s_%s", key, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
@ -107,7 +107,7 @@ func getVolumeOpCounts(c clientset.Interface, pluginName string) opCounts {
|
|||||||
|
|
||||||
nodeLimit := 25
|
nodeLimit := 25
|
||||||
|
|
||||||
metricsGrabber, err := e2emetrics.NewMetricsGrabber(c, nil, true, false, true, false, false)
|
metricsGrabber, err := e2emetrics.NewMetricsGrabber(c, nil, true, false, true, false, false, false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
framework.ExpectNoError(err, "Error creating metrics grabber: %v", err)
|
framework.ExpectNoError(err, "Error creating metrics grabber: %v", err)
|
||||||
|
@ -72,7 +72,7 @@ var _ = utils.SIGDescribe("[Serial] Volume metrics", func() {
|
|||||||
VolumeMode: &test.VolumeMode,
|
VolumeMode: &test.VolumeMode,
|
||||||
}, ns)
|
}, ns)
|
||||||
|
|
||||||
metricsGrabber, err = e2emetrics.NewMetricsGrabber(c, nil, true, false, true, false, false)
|
metricsGrabber, err = e2emetrics.NewMetricsGrabber(c, nil, true, false, true, false, false, false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
framework.Failf("Error creating metrics grabber : %v", err)
|
framework.Failf("Error creating metrics grabber : %v", err)
|
||||||
|
@ -61,7 +61,7 @@ func gatherTestSuiteMetrics() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Grab metrics for apiserver, scheduler, controller-manager, kubelet (for non-kubemark case) and cluster autoscaler (optionally).
|
// Grab metrics for apiserver, scheduler, controller-manager, kubelet (for non-kubemark case) and cluster autoscaler (optionally).
|
||||||
grabber, err := e2emetrics.NewMetricsGrabber(c, nil, !framework.ProviderIs("kubemark"), true, true, true, framework.TestContext.IncludeClusterAutoscalerMetrics)
|
grabber, err := e2emetrics.NewMetricsGrabber(c, nil, !framework.ProviderIs("kubemark"), true, true, true, framework.TestContext.IncludeClusterAutoscalerMetrics, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create MetricsGrabber: %v", err)
|
return fmt.Errorf("failed to create MetricsGrabber: %v", err)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user