Skip to content

Commit

Permalink
feat: add support for timeframe in KeptnMetric (#1471)
Browse files Browse the repository at this point in the history
Signed-off-by: Rakshit Gondwal <rakshitgondwal3@gmail.com>
Signed-off-by: Rakshit Gondwal <98955085+rakshitgondwal@users.noreply.github.com>
Co-authored-by: Florian Bacher <florian.bacher@dynatrace.com>
  • Loading branch information
rakshitgondwal and bacherfl committed Jun 22, 2023
1 parent b37aed9 commit 4d9ceb7
Show file tree
Hide file tree
Showing 20 changed files with 353 additions and 11 deletions.
12 changes: 6 additions & 6 deletions helm/chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ certificateOperator:
labelSelectorValue: "true"
image:
repository: ghcr.io/keptn/certificate-operator
tag: v0.7.1
tag: v0.8.0
imagePullPolicy: Always
livenessProbe:
httpGet:
Expand Down Expand Up @@ -67,7 +67,7 @@ lifecycleOperator:
seccompProfile:
type: RuntimeDefault
env:
functionRunnerImage: ghcr.io/keptn/functions-runtime:v0.7.1
functionRunnerImage: ghcr.io/keptn/functions-runtime:v0.8.0
keptnAppControllerLogLevel: "0"
keptnAppCreationRequestControllerLogLevel: "0"
keptnAppVersionControllerLogLevel: "0"
Expand All @@ -78,10 +78,10 @@ lifecycleOperator:
keptnWorkloadInstanceControllerLogLevel: "0"
optionsControllerLogLevel: "0"
otelCollectorUrl: otel-collector:4317
pythonRunnerImage: ghcr.io/keptn/python-runtime:v0.0.0
pythonRunnerImage: ghcr.io/keptn/python-runtime:v0.8.0
image:
repository: ghcr.io/keptn/lifecycle-operator
tag: v0.7.1
tag: v0.8.0
imagePullPolicy: Always
livenessProbe:
httpGet:
Expand Down Expand Up @@ -148,7 +148,7 @@ metricsOperator:
metricsControllerLogLevel: "0"
image:
repository: ghcr.io/keptn/metrics-operator
tag: v0.7.1
tag: v0.8.0
livenessProbe:
httpGet:
path: /healthz
Expand Down Expand Up @@ -211,7 +211,7 @@ scheduler:
otelCollectorUrl: otel-collector:4317
image:
repository: ghcr.io/keptn/scheduler
tag: v0.7.1
tag: v0.8.0
imagePullPolicy: Always
livenessProbe:
httpGet:
Expand Down
3 changes: 3 additions & 0 deletions metrics-operator/PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ resources:
kind: KeptnMetric
path: github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha3
version: v1alpha3
webhooks:
validation: true
webhookVersion: v1
- api:
crdVersion: v1
namespaced: true
Expand Down
9 changes: 9 additions & 0 deletions metrics-operator/api/v1alpha3/keptnmetric_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ type KeptnMetricSpec struct {
Query string `json:"query"`
// FetchIntervalSeconds represents the update frequency in seconds that is used to update the metric
FetchIntervalSeconds uint `json:"fetchIntervalSeconds"`
// Range represents the time range for which data is to be queried
Range *RangeSpec `json:"range,omitempty"`
}

// KeptnMetricStatus defines the observed state of KeptnMetric
Expand All @@ -51,6 +53,13 @@ type ProviderRef struct {
Name string `json:"name"`
}

// RangeSpec defines the time range for which data is to be queried
type RangeSpec struct {
// Interval specifies the duration of the time interval for the data query
// +kubebuilder:default:="5m"
Interval string `json:"interval,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Provider",type=string,JSONPath=`.spec.provider.name`
Expand Down
68 changes: 68 additions & 0 deletions metrics-operator/api/v1alpha3/keptnmetric_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,79 @@ limitations under the License.
package v1alpha3

import (
"time"

"github.com/pkg/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)

// log is for logging in this package.
var keptnmetriclog = logf.Log.WithName("keptnmetric-resource")

func (r *KeptnMetric) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
}

//+kubebuilder:webhook:path=/validate-metrics-keptn-sh-v1alpha3-keptnmetric,mutating=false,failurePolicy=fail,sideEffects=None,groups=metrics.keptn.sh,resources=keptnmetrics,verbs=create;update,versions=v1alpha3,name=vkeptnmetric.kb.io,admissionReviewVersions=v1

var _ webhook.Validator = &KeptnMetric{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *KeptnMetric) ValidateCreate() error {
keptnmetriclog.Info("validate create", "name", r.Name)

return r.validateKeptnMetric()
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *KeptnMetric) ValidateUpdate(old runtime.Object) error {
keptnmetriclog.Info("validate update", "name", r.Name)

return r.validateKeptnMetric()
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *KeptnMetric) ValidateDelete() error {
keptnmetriclog.Info("validate delete", "name", r.Name)

return nil
}

func (s *KeptnMetric) validateKeptnMetric() error {
var allErrs field.ErrorList //defined as a list to allow returning multiple validation errors
var err *field.Error
if err = s.validateRangeInterval(); err != nil {
allErrs = append(allErrs, err)
}
if len(allErrs) == 0 {
return nil
}

return apierrors.NewInvalid(
schema.GroupKind{Group: "metrics.keptn.sh", Kind: "KeptnMetric"},
s.Name,
allErrs)
}

func (s *KeptnMetric) validateRangeInterval() *field.Error {
if s.Spec.Range == nil {
return nil
}
_, err := time.ParseDuration(s.Spec.Range.Interval)
if err != nil {
return field.Invalid(
field.NewPath("spec").Child("range").Child("interval"),
s.Spec.Range.Interval,
errors.New("Forbidden! The time interval cannot be parsed. Please check for suitable conventions").Error(),
)
}
return nil
}
143 changes: 143 additions & 0 deletions metrics-operator/api/v1alpha3/keptnmetric_webhook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package v1alpha3

import (
"testing"

"github.com/stretchr/testify/require"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
)

func TestKeptnMetric_validateRangeInterval(t *testing.T) {

tests := []struct {
name string
verb string
Spec KeptnMetricSpec
want error
oldSpec runtime.Object
}{
{
name: "create-with-nil-range",
verb: "create",
Spec: KeptnMetricSpec{
Range: nil,
},
},
{
name: "create-with-wrong-interval",
verb: "create",
Spec: KeptnMetricSpec{
Range: &RangeSpec{Interval: "5mins"},
},
want: apierrors.NewInvalid(
schema.GroupKind{Group: "metrics.keptn.sh", Kind: "KeptnMetric"},
"create-with-wrong-interval",
field.ErrorList{
field.Invalid(
field.NewPath("spec").Child("range").Child("interval"),
"5mins",
"Forbidden! The time interval cannot be parsed. Please check for suitable conventions",
),
},
),
},
{
name: "create-with-empty-interval",
verb: "create",
Spec: KeptnMetricSpec{
Range: &RangeSpec{Interval: ""},
},
want: apierrors.NewInvalid(
schema.GroupKind{Group: "metrics.keptn.sh", Kind: "KeptnMetric"},
"create-with-empty-interval",
field.ErrorList{
field.Invalid(
field.NewPath("spec").Child("range").Child("interval"),
"",
"Forbidden! The time interval cannot be parsed. Please check for suitable conventions",
),
},
),
},
{
name: "create-with-right-interval",
verb: "create",
Spec: KeptnMetricSpec{
Range: &RangeSpec{Interval: "5m"},
},
},
{
name: "update-with-right-interval",
verb: "update",
Spec: KeptnMetricSpec{
Range: &RangeSpec{Interval: "5m"},
},
oldSpec: &KeptnMetric{
Spec: KeptnMetricSpec{
Range: &RangeSpec{Interval: "5mins"},
},
},
},
{
name: "update-with-wrong-interval",
verb: "update",
Spec: KeptnMetricSpec{
Range: &RangeSpec{Interval: "5mins"},
},
want: apierrors.NewInvalid(
schema.GroupKind{Group: "metrics.keptn.sh", Kind: "KeptnMetric"},
"update-with-wrong-interval",
field.ErrorList{
field.Invalid(
field.NewPath("spec").Child("range").Child("interval"),
"5mins",
"Forbidden! The time interval cannot be parsed. Please check for suitable conventions",
),
},
),
oldSpec: &KeptnMetric{
Spec: KeptnMetricSpec{
Range: &RangeSpec{Interval: "5m"},
},
},
},
{
name: "delete",
verb: "delete",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var s *KeptnMetric
if tt.Spec.Range == nil {
s = &KeptnMetric{
ObjectMeta: metav1.ObjectMeta{Name: tt.name},
Spec: KeptnMetricSpec{Range: tt.Spec.Range},
}
} else {
s = &KeptnMetric{
ObjectMeta: metav1.ObjectMeta{Name: tt.name},
Spec: KeptnMetricSpec{Range: &RangeSpec{Interval: tt.Spec.Range.Interval}},
}
}
var err error
switch tt.verb {
case "create":
err = s.ValidateCreate()
case "update":
err = s.ValidateUpdate(tt.oldSpec)
case "delete":
err = s.ValidateDelete()
}
if tt.want == nil {
require.Nil(t, err)
} else {
require.Equal(t, tt.want, err)
}
})
}
}
24 changes: 22 additions & 2 deletions metrics-operator/api/v1alpha3/zz_generated.deepcopy.go

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

Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,16 @@ spec:
query:
description: Query represents the query to be run
type: string
range:
description: Range represents the time range for which data is to
be queried
properties:
interval:
default: 5m
description: Interval specifies the duration of the time interval
for the data query
type: string
type: object
required:
- fetchIntervalSeconds
- provider
Expand Down
23 changes: 23 additions & 0 deletions metrics-operator/config/default/manager_webhook_patch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: system
spec:
template:
spec:
containers:
- name: manager
ports:
- containerPort: 9443
name: webhook-server
protocol: TCP
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
volumes:
- name: cert
secret:
defaultMode: 420
secretName: webhook-server-cert
Loading

0 comments on commit 4d9ceb7

Please sign in to comment.