Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(metrics-operator): flush status when analysis is finished #2122

Merged
merged 8 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions metrics-operator/controllers/analysis/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ func (a *AnalysisReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c

err = a.evaluateObjectives(ctx, res, analysisDef, analysis)

// if evaluation was successful remove the stored values
if err == nil {
analysis.Status.StoredValues = nil
err = a.updateStatus(ctx, analysis)
}

return ctrl.Result{}, err
}

Expand Down
55 changes: 55 additions & 0 deletions metrics-operator/controllers/analysis/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,61 @@ func TestAnalysisReconciler_Reconcile_BasicControlLoop(t *testing.T) {
}
}

func TestAnalysisReconciler_ExistingAnalysisStatusIsFlushedWhenEvaluationFinishes(t *testing.T) {
analysis, analysisDef, template, _ := getTestCRDs()

analysis.Status = metricsapi.AnalysisStatus{
StoredValues: map[string]metricsapi.ProviderResult{
"default": {
Objective: metricsapi.ObjectReference{
Name: "my-analysis-def",
Namespace: "default",
},
Value: "1",
},
},
}

mockFactory := func(ctx context.Context, analysisMoqParam *metricsapi.Analysis, obj []metricsapi.Objective, numWorkers int, c client.Client, log logr.Logger, namespace string) (context.Context, IAnalysisPool) {
mymock := fake.IAnalysisPoolMock{
DispatchAndCollectFunc: func(ctx context.Context) (map[string]metricsapi.ProviderResult, error) {
return map[string]metricsapi.ProviderResult{}, nil
},
}
return ctx, &mymock
}

fclient := fake2.NewClient(&analysis, &analysisDef, &template)
a := &AnalysisReconciler{
Client: fclient,
Scheme: fclient.Scheme(),
Log: testr.New(t),
MaxWorkers: 2,
NewWorkersPoolFactory: mockFactory,
IAnalysisEvaluator: &fakeEvaluator.IAnalysisEvaluatorMock{
EvaluateFunc: func(values map[string]metricsapi.ProviderResult, ad *metricsapi.AnalysisDefinition) metricstypes.AnalysisResult {
return metricstypes.AnalysisResult{Pass: true}
}},
}

req := controllerruntime.Request{
NamespacedName: types.NamespacedName{Namespace: "default", Name: "my-analysis"},
}

status := &metricsapi.AnalysisStatus{Raw: "{\"objectiveResults\":null,\"totalScore\":0,\"maximumScore\":0,\"pass\":true,\"warning\":false}", Pass: true}

got, err := a.Reconcile(context.TODO(), req)

require.Nil(t, err)
require.Equal(t, controllerruntime.Result{}, got)
resAnalysis := metricsapi.Analysis{}
err = fclient.Get(context.TODO(), req.NamespacedName, &resAnalysis)
require.Nil(t, err)
require.Nil(t, resAnalysis.Status.StoredValues)
require.Equal(t, *status, resAnalysis.Status)

}

func getTestCRDs() (metricsapi.Analysis, metricsapi.AnalysisDefinition, metricsapi.AnalysisValueTemplate, metricsapi.KeptnMetricsProvider) {
analysis := metricsapi.Analysis{
ObjectMeta: metav1.ObjectMeta{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: metrics.keptn.sh/v1alpha3
kind: Analysis
metadata:
name: analysis-sample
namespace: testy
odubajDT marked this conversation as resolved.
Show resolved Hide resolved
spec:
analysisDefinition:
name: ed-my-proj-dev-svc1
status:
storedValues:
ready-testy:
objectiveReference:
name: ready
namespace: testy
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
apiVersion: v1
kind: Namespace
metadata:
name: testy
---
apiVersion: metrics.keptn.sh/v1alpha3
kind: AnalysisValueTemplate
metadata:
name: ready
namespace: testy
spec:
provider:
name: my-mocked-provider
namespace: testy
query: 'sum(kube_pod_container_status_ready{namespace="{{.ns}}"})'
---
apiVersion: metrics.keptn.sh/v1alpha3
kind: AnalysisDefinition
metadata:
name: ed-my-proj-dev-svc1
namespace: testy
spec:
objectives:
- analysisValueTemplateRef:
name: ready
namespace: testy
target:
failure:
lessThan:
fixedValue: 2
warning:
lessThan:
fixedValue: 3
weight: 1
keyObjective: false
totalScore:
passPercentage: 90
warningPercentage: 75
---
apiVersion: metrics.keptn.sh/v1alpha3
kind: Analysis
metadata:
name: analysis-sample
namespace: testy
spec:
timeframe:
from: 2023-09-14T07:33:19Z
to: 2023-09-14T07:33:19Z
args:
"ns": "keptn-lifecycle-toolkit-system"
analysisDefinition:
name: ed-my-proj-dev-svc1
namespace: testy
status:
storedValues:
my-provider-query-1:
objectiveReference:
name: objective-template-1
value: 1
errMsg: ""

---
apiVersion: metrics.keptn.sh/v1alpha3
kind: KeptnMetricsProvider
metadata:
name: my-mocked-provider
namespace: testy
spec:
type: prometheus
targetServer: "http://mockserver.testy.svc.cluster.local:1080"
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: metrics.keptn.sh/v1alpha3
kind: Analysis
metadata:
name: analysis-sample
namespace: testy
spec:
analysisDefinition:
name: ed-my-proj-dev-svc1
status:
pass: true
# yamllint disable-line rule:line-length
raw: '{"objectiveResults":[{"result":{"failResult":{"operator":{"lessThan":{"fixedValue":"2"}},"fulfilled":false},"warnResult":{"operator":{"lessThan":{"fixedValue":"3"}},"fulfilled":false},"warning":false,"pass":true},"value":4,"score":1}],"totalScore":1,"maximumScore":1,"pass":true,"warning":false}'
143 changes: 143 additions & 0 deletions test/integration/analysis-controller-existing-status/01-install.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
apiVersion: v1
kind: Service
metadata:
name: mockserver
namespace: testy
spec:
ports:
- name: serviceport
port: 1080
protocol: TCP
targetPort: serviceport
selector:
app: mockserver
sessionAffinity: None
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: mockserver
name: mockserver
namespace: testy
spec:
replicas: 1
selector:
matchLabels:
app: mockserver
template:
metadata:
labels:
app: mockserver
name: mockserver
spec:
containers:
- env:
- name: MOCKSERVER_LOG_LEVEL
value: INFO
- name: SERVER_PORT
value: "1080"
image: mockserver/mockserver:mockserver-5.13.0
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 10
initialDelaySeconds: 10
periodSeconds: 5
successThreshold: 1
tcpSocket:
port: serviceport
timeoutSeconds: 1
name: mockserver
ports:
- containerPort: 1080
name: serviceport
protocol: TCP
readinessProbe:
failureThreshold: 10
initialDelaySeconds: 2
periodSeconds: 2
successThreshold: 1
tcpSocket:
port: serviceport
timeoutSeconds: 1
volumeMounts:
- mountPath: /config
name: config-volume
- mountPath: /libs
name: libs-volume
terminationGracePeriodSeconds: 30
volumes:
- configMap:
defaultMode: 420
name: mockserver-config
optional: true
name: config-volume
- configMap:
defaultMode: 420
name: mockserver-config
optional: true
name: libs-volume
---
kind: ConfigMap
apiVersion: v1
metadata:
name: mockserver-config
namespace: testy
data:
initializerJson.json: |-
[
{
"httpRequest": {
"path": "/api/v1/query_range",
"method": "POST"
},
"httpResponse": {
"body": {
"status": "success",
"data": {
"resultType": "matrix",
"result": [
{
"metric": {
"__name__": "metric-name",
"job": "",
"instance": ""
},
"values": [[1669714193.275, "4"]]
}
]
}
},
"statusCode": 200
}
}
]
mockserver.properties: |-
###############################
# MockServer & Proxy Settings #
###############################
# Socket & Port Settings
# socket timeout in milliseconds (default 120000)
mockserver.maxSocketTimeout=120000
# Certificate Generation
# dynamically generated CA key pair (if they don't already exist in
specified directory)
mockserver.dynamicallyCreateCertificateAuthorityCertificate=true
# save dynamically generated CA key pair in working directory
mockserver.directoryToSaveDynamicSSLCertificate=.
# certificate domain name (default "localhost")
mockserver.sslCertificateDomainName=localhost
# comma separated list of ip addresses for Subject Alternative Name domain
names (default empty list)
mockserver.sslSubjectAlternativeNameDomains=www.example.com,www.another.com
# comma separated list of ip addresses for Subject Alternative Name ips
(default empty list)
mockserver.sslSubjectAlternativeNameIps=127.0.0.1
# CORS
# enable CORS for MockServer REST API
mockserver.enableCORSForAPI=true
# enable CORS for all responses
mockserver.enableCORSForAllResponses=true
# Json Initialization
mockserver.initializationJsonPath=/config/initializerJson.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
commands:
- script: kubectl delete ns testy