Skip to content

Commit

Permalink
UPSTREAM: 3606: feat: Migrate to autoscalingv2 (kedacore#3606)
Browse files Browse the repository at this point in the history
* feat: migrate to autoscalingv2

Signed-off-by: Jorge Turrado Ferrero <Jorge_turrado@hotmail.es>
Co-authored-by: Tom Kerkhove <kerkhove.tom@gmail.com>
  • Loading branch information
2 people authored and joelsmith committed Feb 9, 2023
1 parent 671fd10 commit 6f48778
Show file tree
Hide file tree
Showing 72 changed files with 522 additions and 536 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ To learn more about our roadmap, we recommend reading [this document](ROADMAP.md

### Breaking Changes

- TODO ([#XXX](https://github.com/kedacore/keda/issue/XXX))
- **General:** Change API version of HPA from `autoscaling/v2beta2` to `autoscaling/v2` ([#2462](https://github.com/kedacore/keda/issues/2462))

### Other

Expand Down
10 changes: 5 additions & 5 deletions CREATE-NEW-SCALER.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@ For example:

>**Note:** There is a naming helper function `GenerateMetricNameWithIndex(scalerIndex int, metricName string)`, that receives the current index and the original metric name (without the prefix) and returns the concatenated string using the convention (please use this function).<br>Next lines are an example about how to use it:
>```golang
>func (s *artemisScaler) GetMetricSpecForScaling() []v2beta2.MetricSpec {
> externalMetric := &v2beta2.ExternalMetricSource{
> Metric: v2beta2.MetricIdentifier{
>func (s *artemisScaler) GetMetricSpecForScaling() []v2.MetricSpec {
> externalMetric := &v2.ExternalMetricSource{
> Metric: v2.MetricIdentifier{
> Name: GenerateMetricNameWithIndex(s.metadata.scalerIndex, kedautil.NormalizeString(fmt.Sprintf("%s-%s-%s", "artemis", s.metadata.brokerName, s.metadata.queueName))),
> },
> Target: GetMetricTarget(s.metricType, s.metadata.queueLength),
> }
> metricSpec := v2beta2.MetricSpec{External: externalMetric, Type: artemisMetricType}
> return []v2beta2.MetricSpec{metricSpec}
> metricSpec := v2.MetricSpec{External: externalMetric, Type: artemisMetricType}
> return []v2.MetricSpec{metricSpec}
>}
>```
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ GO_LDFLAGS="-X=github.com/kedacore/keda/v2/version.GitCommit=$(GIT_COMMIT) -X=gi
COSIGN_FLAGS ?= -a GIT_HASH=${GIT_COMMIT} -a GIT_VERSION=${VERSION} -a BUILD_DATE=${DATE}

# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.22
ENVTEST_K8S_VERSION = 1.23

# Setting SHELL to bash allows bash commands to be executed by recipes.
# This is a requirement for 'setup-envtest.sh' in the test target.
Expand Down
6 changes: 3 additions & 3 deletions apis/keda/v1alpha1/scaledobject_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ limitations under the License.
package v1alpha1

import (
autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2"
autoscalingv2 "k8s.io/api/autoscaling/v2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand Down Expand Up @@ -103,7 +103,7 @@ type AdvancedConfig struct {
// HorizontalPodAutoscalerConfig specifies horizontal scale config
type HorizontalPodAutoscalerConfig struct {
// +optional
Behavior *autoscalingv2beta2.HorizontalPodAutoscalerBehavior `json:"behavior,omitempty"`
Behavior *autoscalingv2.HorizontalPodAutoscalerBehavior `json:"behavior,omitempty"`
// +optional
Name string `json:"name,omitempty"`
}
Expand All @@ -128,7 +128,7 @@ type ScaleTriggers struct {
// +optional
AuthenticationRef *ScaledObjectAuthRef `json:"authenticationRef,omitempty"`
// +optional
MetricType autoscalingv2beta2.MetricTargetType `json:"metricType,omitempty"`
MetricType autoscalingv2.MetricTargetType `json:"metricType,omitempty"`
}

// +k8s:openapi-gen=true
Expand Down
4 changes: 2 additions & 2 deletions apis/keda/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 12 additions & 23 deletions controllers/keda/hpa.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
"unicode"

"github.com/go-logr/logr"
autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2"
autoscalingv2 "k8s.io/api/autoscaling/v2"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
Expand Down Expand Up @@ -70,14 +70,14 @@ func (r *ScaledObjectReconciler) createAndDeployNewHPA(ctx context.Context, logg
}

// newHPAForScaledObject returns HPA as it is specified in ScaledObject
func (r *ScaledObjectReconciler) newHPAForScaledObject(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject, gvkr *kedav1alpha1.GroupVersionKindResource) (*autoscalingv2beta2.HorizontalPodAutoscaler, error) {
func (r *ScaledObjectReconciler) newHPAForScaledObject(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject, gvkr *kedav1alpha1.GroupVersionKindResource) (*autoscalingv2.HorizontalPodAutoscaler, error) {
scaledObjectMetricSpecs, err := r.getScaledObjectMetricSpecs(ctx, logger, scaledObject)
if err != nil {
return nil, err
}

var behavior *autoscalingv2beta2.HorizontalPodAutoscalerBehavior
if r.kubeVersion.MinorVersion >= 18 && scaledObject.Spec.Advanced != nil && scaledObject.Spec.Advanced.HorizontalPodAutoscalerConfig != nil {
var behavior *autoscalingv2.HorizontalPodAutoscalerBehavior
if scaledObject.Spec.Advanced != nil && scaledObject.Spec.Advanced.HorizontalPodAutoscalerConfig != nil {
behavior = scaledObject.Spec.Advanced.HorizontalPodAutoscalerConfig.Behavior
} else {
behavior = nil
Expand Down Expand Up @@ -117,13 +117,13 @@ func (r *ScaledObjectReconciler) newHPAForScaledObject(ctx context.Context, logg
maxReplicas = *pausedCount
}

hpa := &autoscalingv2beta2.HorizontalPodAutoscaler{
Spec: autoscalingv2beta2.HorizontalPodAutoscalerSpec{
hpa := &autoscalingv2.HorizontalPodAutoscaler{
Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
MinReplicas: minReplicas,
MaxReplicas: maxReplicas,
Metrics: scaledObjectMetricSpecs,
Behavior: behavior,
ScaleTargetRef: autoscalingv2beta2.CrossVersionObjectReference{
ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
Name: scaledObject.Spec.ScaleTargetRef.Name,
Kind: gvkr.Kind,
APIVersion: gvkr.GroupVersion().String(),
Expand All @@ -135,7 +135,7 @@ func (r *ScaledObjectReconciler) newHPAForScaledObject(ctx context.Context, logg
Annotations: scaledObject.Annotations,
},
TypeMeta: metav1.TypeMeta{
APIVersion: "v2beta2",
APIVersion: "v2",
},
}

Expand All @@ -148,7 +148,7 @@ func (r *ScaledObjectReconciler) newHPAForScaledObject(ctx context.Context, logg
}

// updateHPAIfNeeded checks whether update of HPA is needed
func (r *ScaledObjectReconciler) updateHPAIfNeeded(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject, foundHpa *autoscalingv2beta2.HorizontalPodAutoscaler, gvkr *kedav1alpha1.GroupVersionKindResource) error {
func (r *ScaledObjectReconciler) updateHPAIfNeeded(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject, foundHpa *autoscalingv2.HorizontalPodAutoscaler, gvkr *kedav1alpha1.GroupVersionKindResource) error {
hpa, err := r.newHPAForScaledObject(ctx, logger, scaledObject, gvkr)
if err != nil {
logger.Error(err, "Failed to create new HPA resource", "HPA.Namespace", scaledObject.Namespace, "HPA.Name", getHPAName(scaledObject))
Expand All @@ -163,8 +163,6 @@ func (r *ScaledObjectReconciler) updateHPAIfNeeded(ctx context.Context, logger l
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
// check if scaledObject.spec.behavior was defined, because it is supported only on k8s >= 1.18
r.checkMinK8sVersionforHPABehavior(logger, scaledObject)

logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
Expand All @@ -183,7 +181,7 @@ func (r *ScaledObjectReconciler) updateHPAIfNeeded(ctx context.Context, logger l
}

// deleteAndCreateHpa delete old HPA and create new one
func (r *ScaledObjectReconciler) renameHPA(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject, foundHpa *autoscalingv2beta2.HorizontalPodAutoscaler, gvkr *kedav1alpha1.GroupVersionKindResource) error {
func (r *ScaledObjectReconciler) renameHPA(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject, foundHpa *autoscalingv2.HorizontalPodAutoscaler, gvkr *kedav1alpha1.GroupVersionKindResource) error {
logger.Info("Deleting old HPA", "HPA.Namespace", scaledObject.Namespace, "HPA.Name", foundHpa.Name)
if err := r.Client.Delete(ctx, foundHpa); err != nil {
logger.Error(err, "Failed to delete old HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
Expand All @@ -194,8 +192,8 @@ func (r *ScaledObjectReconciler) renameHPA(ctx context.Context, logger logr.Logg
}

// getScaledObjectMetricSpecs returns MetricSpec for HPA, generater from Triggers defitinion in ScaledObject
func (r *ScaledObjectReconciler) getScaledObjectMetricSpecs(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject) ([]autoscalingv2beta2.MetricSpec, error) {
var scaledObjectMetricSpecs []autoscalingv2beta2.MetricSpec
func (r *ScaledObjectReconciler) getScaledObjectMetricSpecs(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject) ([]autoscalingv2.MetricSpec, error) {
var scaledObjectMetricSpecs []autoscalingv2.MetricSpec
var externalMetricNames []string
var resourceMetricNames []string

Expand Down Expand Up @@ -260,15 +258,6 @@ func updateHealthStatus(scaledObject *kedav1alpha1.ScaledObject, externalMetricN
status.Health = newHealth
}

// checkMinK8sVersionforHPABehavior min version (k8s v1.18) for HPA Behavior
func (r *ScaledObjectReconciler) checkMinK8sVersionforHPABehavior(logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject) {
if r.kubeVersion.MinorVersion < 18 {
if scaledObject.Spec.Advanced != nil && scaledObject.Spec.Advanced.HorizontalPodAutoscalerConfig != nil && scaledObject.Spec.Advanced.HorizontalPodAutoscalerConfig.Behavior != nil {
logger.Info("Warning: Ignoring scaledObject.spec.behavior, it is only supported on kubernetes version >= 1.18", "kubernetes.version", r.kubeVersion.PrettyVersion)
}
}
}

// getHPAName returns generated HPA name for ScaledObject specified in the parameter
func getHPAName(scaledObject *kedav1alpha1.ScaledObject) string {
if scaledObject.Spec.Advanced != nil && scaledObject.Spec.Advanced.HorizontalPodAutoscalerConfig != nil && scaledObject.Spec.Advanced.HorizontalPodAutoscalerConfig.Name != "" {
Expand Down
10 changes: 5 additions & 5 deletions controllers/keda/hpa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/api/autoscaling/v2beta2"
v2 "k8s.io/api/autoscaling/v2"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/kedacore/keda/v2/apis/keda/v1alpha1"
Expand Down Expand Up @@ -140,14 +140,14 @@ func setupTest(health map[string]v1alpha1.HealthStatus, scaler *mock_scalers.Moc
Logger: logr.Discard(),
Recorder: nil,
}
metricSpec := v2beta2.MetricSpec{
External: &v2beta2.ExternalMetricSource{
Metric: v2beta2.MetricIdentifier{
metricSpec := v2.MetricSpec{
External: &v2.ExternalMetricSource{
Metric: v2.MetricIdentifier{
Name: "some metric name",
},
},
}
metricSpecs := []v2beta2.MetricSpec{metricSpec}
metricSpecs := []v2.MetricSpec{metricSpec}
ctx := context.Background()
scaler.EXPECT().GetMetricSpecForScaling(ctx).Return(metricSpecs)
scaleHandler.EXPECT().GetScalersCache(context.Background(), gomock.Eq(scaledObject)).Return(&scalersCache, nil)
Expand Down
11 changes: 4 additions & 7 deletions controllers/keda/scaledobject_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (

"github.com/go-logr/logr"
autoscalingv1 "k8s.io/api/autoscaling/v1"
autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2"
autoscalingv2 "k8s.io/api/autoscaling/v2"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
Expand Down Expand Up @@ -129,7 +129,7 @@ func (r *ScaledObjectReconciler) SetupWithManager(mgr ctrl.Manager, options cont
predicate.GenerationChangedPredicate{},
),
)).
Owns(&autoscalingv2beta2.HorizontalPodAutoscaler{}).
Owns(&autoscalingv2.HorizontalPodAutoscaler{}).
Complete(r)
}

Expand Down Expand Up @@ -370,7 +370,7 @@ func (r *ScaledObjectReconciler) ensureHPAForScaledObjectExists(ctx context.Cont
} else {
hpaName = getHPAName(scaledObject)
}
foundHpa := &autoscalingv2beta2.HorizontalPodAutoscaler{}
foundHpa := &autoscalingv2.HorizontalPodAutoscaler{}
// Check if HPA for this ScaledObject already exists
err := r.Client.Get(ctx, types.NamespacedName{Name: hpaName, Namespace: scaledObject.Namespace}, foundHpa)
if err != nil && errors.IsNotFound(err) {
Expand All @@ -380,9 +380,6 @@ func (r *ScaledObjectReconciler) ensureHPAForScaledObjectExists(ctx context.Cont
return false, err
}

// check if scaledObject.spec.behavior was defined, because it is supported only on k8s >= 1.18
r.checkMinK8sVersionforHPABehavior(logger, scaledObject)

// new HPA created successfully -> notify Reconcile function so it could fire a new ScaleLoop
return true, nil
} else if err != nil {
Expand Down Expand Up @@ -410,7 +407,7 @@ func (r *ScaledObjectReconciler) ensureHPAForScaledObjectExists(ctx context.Cont
return false, nil
}

func isHpaRenamed(scaledObject *kedav1alpha1.ScaledObject, foundHpa *autoscalingv2beta2.HorizontalPodAutoscaler) bool {
func isHpaRenamed(scaledObject *kedav1alpha1.ScaledObject, foundHpa *autoscalingv2.HorizontalPodAutoscaler) bool {
// if HPA name defined in SO -> check if equals to the found HPA
if scaledObject.Spec.Advanced != nil && scaledObject.Spec.Advanced.HorizontalPodAutoscalerConfig != nil && scaledObject.Spec.Advanced.HorizontalPodAutoscalerConfig.Name != "" {
return scaledObject.Spec.Advanced.HorizontalPodAutoscalerConfig.Name != foundHpa.Name
Expand Down
32 changes: 16 additions & 16 deletions controllers/keda/scaledobject_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
appsv1 "k8s.io/api/apps/v1"
autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2"
autoscalingv2 "k8s.io/api/autoscaling/v2"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
Expand Down Expand Up @@ -269,7 +269,7 @@ var _ = Describe("ScaledObjectController", func() {
Expect(err).ToNot(HaveOccurred())

// Get and confirm the HPA.
hpa := &autoscalingv2beta2.HorizontalPodAutoscaler{}
hpa := &autoscalingv2.HorizontalPodAutoscaler{}
Eventually(func() error {
return k8sClient.Get(context.Background(), types.NamespacedName{Name: "keda-hpa-clean-up-test", Namespace: "default"}, hpa)
}).ShouldNot(HaveOccurred())
Expand Down Expand Up @@ -329,7 +329,7 @@ var _ = Describe("ScaledObjectController", func() {
Expect(err).ToNot(HaveOccurred())

// Get and confirm the HPA.
hpa := &autoscalingv2beta2.HorizontalPodAutoscaler{}
hpa := &autoscalingv2.HorizontalPodAutoscaler{}
Eventually(func() error {
return k8sClient.Get(context.Background(), types.NamespacedName{Name: fmt.Sprintf("keda-hpa-%s", soName), Namespace: "default"}, hpa)
}).ShouldNot(HaveOccurred())
Expand Down Expand Up @@ -384,7 +384,7 @@ var _ = Describe("ScaledObjectController", func() {
Expect(err).ToNot(HaveOccurred())

// Get and confirm the HPA.
hpa := &autoscalingv2beta2.HorizontalPodAutoscaler{}
hpa := &autoscalingv2.HorizontalPodAutoscaler{}
Eventually(func() error {
return k8sClient.Get(context.Background(), types.NamespacedName{Name: "keda-hpa-cache-regenerate", Namespace: "default"}, hpa)
}).ShouldNot(HaveOccurred())
Expand Down Expand Up @@ -421,7 +421,7 @@ var _ = Describe("ScaledObjectController", func() {
time.Sleep(30 * time.Second)

// Get and confirm the HPA.
hpa2 := &autoscalingv2beta2.HorizontalPodAutoscaler{}
hpa2 := &autoscalingv2.HorizontalPodAutoscaler{}
Eventually(func() error {
return k8sClient.Get(context.Background(), types.NamespacedName{Name: "keda-hpa-cache-regenerate", Namespace: "default"}, hpa2)
}).ShouldNot(HaveOccurred())
Expand Down Expand Up @@ -469,7 +469,7 @@ var _ = Describe("ScaledObjectController", func() {
Ω(err).ToNot(HaveOccurred())

// Get and confirm the HPA
hpa := &autoscalingv2beta2.HorizontalPodAutoscaler{}
hpa := &autoscalingv2.HorizontalPodAutoscaler{}
Eventually(func() error {
return k8sClient.Get(context.Background(), types.NamespacedName{Name: "keda-hpa-" + soName, Namespace: "default"}, hpa)
}).ShouldNot(HaveOccurred())
Expand Down Expand Up @@ -523,7 +523,7 @@ var _ = Describe("ScaledObjectController", func() {
Ω(err).ToNot(HaveOccurred())

// Get and confirm the HPA
hpa := &autoscalingv2beta2.HorizontalPodAutoscaler{}
hpa := &autoscalingv2.HorizontalPodAutoscaler{}
Eventually(func() error {
return k8sClient.Get(context.Background(), types.NamespacedName{Name: "keda-hpa-" + soName, Namespace: "default"}, hpa)
}).ShouldNot(HaveOccurred())
Expand Down Expand Up @@ -686,14 +686,14 @@ var _ = Describe("ScaledObjectController", func() {
Triggers: []kedav1alpha1.ScaleTriggers{
{
Type: "cpu",
MetricType: autoscalingv2beta2.UtilizationMetricType,
MetricType: autoscalingv2.UtilizationMetricType,
Metadata: map[string]string{
"value": "50",
},
},
{
Type: "external-mock",
MetricType: autoscalingv2beta2.AverageValueMetricType,
MetricType: autoscalingv2.AverageValueMetricType,
Metadata: map[string]string{},
},
},
Expand All @@ -710,7 +710,7 @@ var _ = Describe("ScaledObjectController", func() {
}, 5*time.Second).Should(Equal(metav1.ConditionTrue))

// check hpa
hpa := &autoscalingv2beta2.HorizontalPodAutoscaler{}
hpa := &autoscalingv2.HorizontalPodAutoscaler{}
Eventually(func() int {
err = k8sClient.Get(context.Background(), types.NamespacedName{Name: getHPAName(so), Namespace: "default"}, hpa)
Expect(err).ToNot(HaveOccurred())
Expand All @@ -730,12 +730,12 @@ var _ = Describe("ScaledObjectController", func() {
// mock kube-controller-manager request v1beta1.custom.metrics.k8s.io api GetMetrics
err = k8sClient.Get(context.Background(), types.NamespacedName{Name: getHPAName(so), Namespace: "default"}, hpa)
Expect(err).ToNot(HaveOccurred())
hpa.Status.CurrentMetrics = []autoscalingv2beta2.MetricStatus{
hpa.Status.CurrentMetrics = []autoscalingv2.MetricStatus{
{
Type: autoscalingv2beta2.ResourceMetricSourceType,
Resource: &autoscalingv2beta2.ResourceMetricStatus{
Type: autoscalingv2.ResourceMetricSourceType,
Resource: &autoscalingv2.ResourceMetricStatus{
Name: corev1.ResourceCPU,
Current: autoscalingv2beta2.MetricValueStatus{
Current: autoscalingv2.MetricValueStatus{
Value: resource.NewQuantity(int64(100), resource.DecimalSI),
},
},
Expand All @@ -746,7 +746,7 @@ var _ = Describe("ScaledObjectController", func() {

// hpa metrics will only left CPU metric
Eventually(func() int {
hpa := &autoscalingv2beta2.HorizontalPodAutoscaler{}
hpa := &autoscalingv2.HorizontalPodAutoscaler{}
err = k8sClient.Get(context.Background(), types.NamespacedName{Name: getHPAName(so), Namespace: "default"}, hpa)
Expect(err).ToNot(HaveOccurred())
return len(hpa.Spec.Metrics)
Expand All @@ -764,7 +764,7 @@ var _ = Describe("ScaledObjectController", func() {

// hpa will recover
Eventually(func() int {
hpa := &autoscalingv2beta2.HorizontalPodAutoscaler{}
hpa := &autoscalingv2.HorizontalPodAutoscaler{}
err = k8sClient.Get(context.Background(), types.NamespacedName{Name: getHPAName(so), Namespace: "default"}, hpa)
Expect(err).ToNot(HaveOccurred())
return len(hpa.Spec.Metrics)
Expand Down
Loading

0 comments on commit 6f48778

Please sign in to comment.