Skip to content

Commit c1259ab

Browse files
committed
WIP
1 parent 865262a commit c1259ab

16 files changed

+453
-167
lines changed

api/v1alpha1/ruleraction_types.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ type RulerActionCredentials struct {
2727

2828
// WebHook TODO
2929
type Webhook struct {
30-
Url string `json:"url"`
31-
Verb string `json:"verb"`
32-
Headers map[string]string `json:"headers,omitempty"`
33-
Validator string `json:"validator,omitempty"`
34-
Credentials RulerActionCredentials `json:"credentials,omitempty"`
30+
Url string `json:"url"`
31+
Verb string `json:"verb"`
32+
Headers map[string]string `json:"headers,omitempty"`
33+
TlsSkipVerify bool `json:"tlsSkipVerify,omitempty"`
34+
Validator string `json:"validator,omitempty"`
35+
Credentials RulerActionCredentials `json:"credentials,omitempty"`
3536
}
3637

3738
// RulerActionSpec defines the desired state of RulerAction.

config/crd/bases/searchruler.prosimcorp.com_ruleractions.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ spec:
6666
additionalProperties:
6767
type: string
6868
type: object
69+
tlsSkipVerify:
70+
type: boolean
6971
url:
7072
type: string
7173
validator:

config/samples/searchruler_v1alpha1_ruleraction.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ spec:
1717
# HTTP method to send the webhook message
1818
verb: POST
1919

20+
# Skip certificate verification if the connection is HTTPS
21+
tlsSkipVerify: false
22+
2023
# Additional headers if needed for the connection
2124
headers: {}
2225

internal/controller/commons.go

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,34 @@ const (
1111
defaultSyncInterval = "1m"
1212

1313
// Error messages
14-
resourceNotFoundError = "%s '%s' resource not found. Ignoring since object must be deleted."
15-
resourceRetrievalError = "Error getting the %s '%s' from the cluster: %s"
16-
resourceTargetsDeleteError = "Failed to delete targets of %s '%s': %s"
17-
resourceFinalizersUpdateError = "Failed to update finalizer of %s '%s': %s"
18-
resourceConditionUpdateError = "Failed to update the condition on %s '%s': %s"
19-
resourceSyncTimeRetrievalError = "can not get synchronization time from the %s '%s': %s"
20-
syncTargetError = "can not sync the target for the %s '%s': %s"
21-
ValidatorNotFoundErrorMessage = "validator %s not found"
22-
ValidationFailedErrorMessage = "validation failed: %s"
23-
HttpRequestCreationErrorMessage = "error creating http request: %s"
24-
HttpRequestSendingErrorMessage = "error sending http request: %s"
25-
AlertFiringInfoMessage = "alert firing for searchRule with namespaced name %s/%s"
26-
SecretNotFoundErrorMessage = "error fetching secret %s: %v"
27-
MissingCredentialsMessage = "missing credentials in secret %s"
28-
GetRulerActionErrorMessage = "error getting RulerAction from event: %v"
29-
EvaluateTemplateErrorMessage = "error evaluating template message: %v"
14+
resourceNotFoundError = "%s '%s' resource not found. Ignoring since object must be deleted."
15+
resourceRetrievalError = "Error getting the %s '%s' from the cluster: %s"
16+
resourceTargetsDeleteError = "Failed to delete targets of %s '%s': %s"
17+
resourceFinalizersUpdateError = "Failed to update finalizer of %s '%s': %s"
18+
resourceConditionUpdateError = "Failed to update the condition on %s '%s': %s"
19+
resourceSyncTimeRetrievalError = "can not get synchronization time from the %s '%s': %s"
20+
syncTargetError = "can not sync the target for the %s '%s': %s"
21+
ValidatorNotFoundErrorMessage = "validator %s not found"
22+
ValidationFailedErrorMessage = "validation failed: %s"
23+
HttpRequestCreationErrorMessage = "error creating http request: %s"
24+
HttpRequestSendingErrorMessage = "error sending http request: %s"
25+
AlertFiringInfoMessage = "alert firing for searchRule with namespaced name %s/%s. Description: %s"
26+
SecretNotFoundErrorMessage = "error fetching secret %s: %v"
27+
MissingCredentialsMessage = "missing credentials in secret %s"
28+
GetRulerActionErrorMessage = "error getting RulerAction from event: %v"
29+
EvaluateTemplateErrorMessage = "error evaluating template message: %v"
30+
AlertsPoolErrorMessage = "error getting alerts pool: %v"
31+
QueryConnectorNotFoundMessage = "queryConnector %s not found in the resource namespace %s"
32+
QueryNotDefinedErrorMessage = "query not defined in resource %s"
33+
QueryDefinedInBothErrorMessage = "both query and queryJSON are defined in resource %s. Only one of them must be defined"
34+
JSONMarshalErrorMessage = "error marshaling json: %v"
35+
ElasticsearchQueryErrorMessage = "error executing elasticsearch request %s: %v"
36+
ResponseBodyReadErrorMessage = "error reading response body: %v"
37+
ElasticsearchQueryResponseErrorMessage = "error response from Elasticsearch executing request %s: %s"
38+
ConditionFieldNotFoundMessage = "conditionField %s not found in the response: %s"
39+
EvaluatingConditionErrorMessage = "error evaluating condition: %v"
40+
ForValueParseErrorMessage = "error parsing `for` time: %v"
41+
KubeEventCreationErrorMessage = "error creating kube event: %v"
3042

3143
// Finalizer
3244
resourceFinalizer = "searchruler.prosimcorp.com/finalizer"

internal/controller/queryconnector_controller.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"time"
2424

2525
"k8s.io/apimachinery/pkg/runtime"
26+
"k8s.io/apimachinery/pkg/watch"
2627
ctrl "sigs.k8s.io/controller-runtime"
2728
"sigs.k8s.io/controller-runtime/pkg/client"
2829
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
@@ -77,16 +78,17 @@ func (r *QueryConnectorReconciler) Reconcile(ctx context.Context, req ctrl.Reque
7778
// 3. Check if the SearchRule instance is marked to be deleted: indicated by the deletion timestamp being set
7879
if !QueryConnectorResource.DeletionTimestamp.IsZero() {
7980
if controllerutil.ContainsFinalizer(QueryConnectorResource, resourceFinalizer) {
81+
82+
// 3.1 Delete the resources associated with the QueryConnector
83+
err = r.Sync(ctx, watch.Deleted, QueryConnectorResource)
84+
8085
// Remove the finalizers on Patch CR
8186
controllerutil.RemoveFinalizer(QueryConnectorResource, resourceFinalizer)
8287
err = r.Update(ctx, QueryConnectorResource)
8388
if err != nil {
8489
logger.Info(fmt.Sprintf(resourceFinalizersUpdateError, QueryConnectorResourceType, req.NamespacedName, err.Error()))
8590
}
8691
}
87-
// Delete credentials from pool
88-
credentialsKey := fmt.Sprintf("%s/%s", QueryConnectorResource.Namespace, QueryConnectorResource.Name)
89-
r.CredentialsPool.Delete(credentialsKey)
9092

9193
result = ctrl.Result{}
9294
err = nil
@@ -125,7 +127,7 @@ func (r *QueryConnectorReconciler) Reconcile(ctx context.Context, req ctrl.Reque
125127

126128
// 7. Sync credentials if defined
127129
if !reflect.ValueOf(QueryConnectorResource.Spec.Credentials).IsZero() {
128-
err = r.Sync(ctx, QueryConnectorResource)
130+
err = r.Sync(ctx, watch.Modified, QueryConnectorResource)
129131
if err != nil {
130132
r.UpdateConditionKubernetesApiCallFailure(QueryConnectorResource)
131133
logger.Info(fmt.Sprintf(syncTargetError, QueryConnectorResourceType, req.NamespacedName, err.Error()))

internal/controller/queryconnector_sync.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,22 @@ import (
2222

2323
v1 "k8s.io/api/core/v1"
2424
"k8s.io/apimachinery/pkg/types"
25+
"k8s.io/apimachinery/pkg/watch"
2526
"prosimcorp.com/SearchRuler/api/v1alpha1"
2627
"prosimcorp.com/SearchRuler/internal/pools"
2728
)
2829

2930
// Sync function is used to synchronize the QueryConnector resource with the credentials. Adds the credentials to the
3031
// credentials pool to be used in SearchRule resources. Just executed when the resource has a secretRef defined.
31-
func (r *QueryConnectorReconciler) Sync(ctx context.Context, resource *v1alpha1.QueryConnector) (err error) {
32+
func (r *QueryConnectorReconciler) Sync(ctx context.Context, eventType watch.EventType, resource *v1alpha1.QueryConnector) (err error) {
33+
34+
// If the eventType is Deleted, remove the credentials from the pool
35+
// In other cases get the credentials from the secret and add them to the pool
36+
if eventType == watch.Deleted {
37+
credentialsKey := fmt.Sprintf("%s/%s", resource.Namespace, resource.Name)
38+
r.CredentialsPool.Delete(credentialsKey)
39+
return nil
40+
}
3241

3342
// Get credentials for the queryConnector in the secret associated
3443
// First get secret with the credentials. The secret must be in the same

internal/controller/ruleraction_sync.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package controller
1919
import (
2020
"bytes"
2121
"context"
22+
"crypto/tls"
2223
"fmt"
2324
"io"
2425
"net/http"
@@ -27,6 +28,7 @@ import (
2728
corev1 "k8s.io/api/core/v1"
2829
"k8s.io/apimachinery/pkg/types"
2930
"prosimcorp.com/SearchRuler/api/v1alpha1"
31+
"prosimcorp.com/SearchRuler/internal/pools"
3032
"prosimcorp.com/SearchRuler/internal/template"
3133
"prosimcorp.com/SearchRuler/internal/validators"
3234
"sigs.k8s.io/controller-runtime/pkg/log"
@@ -72,12 +74,21 @@ func (r *RulerActionReconciler) Sync(ctx context.Context, resource *v1alpha1.Rul
7274

7375
// Check alert pool for alerts related to this rulerAction
7476
// Alerts key pattern: namespace/rulerActionName/searchRuleName
75-
alerts := r.AlertsPool.GetByRegex(fmt.Sprintf("%s/%s/*", resource.Namespace, resource.Name))
77+
alerts, err := r.getRulerActionAssociatedAlerts(resource)
78+
if err != nil {
79+
return fmt.Errorf(AlertsPoolErrorMessage, err)
80+
}
7681

7782
// If there are alerts for the rulerAction, initialice the HTTP client
7883
if len(alerts) > 0 {
7984
// Create the HTTP client
80-
httpClient := &http.Client{}
85+
httpClient := &http.Client{
86+
Transport: &http.Transport{
87+
TLSClientConfig: &tls.Config{
88+
InsecureSkipVerify: resource.Spec.Webhook.TlsSkipVerify,
89+
},
90+
},
91+
}
8192

8293
// Create the request with the configured verb and URL
8394
httpRequest, err := http.NewRequest(resource.Spec.Webhook.Verb, resource.Spec.Webhook.Url, nil)
@@ -104,6 +115,7 @@ func (r *RulerActionReconciler) Sync(ctx context.Context, resource *v1alpha1.Rul
104115
logger.Info(fmt.Sprintf(
105116
AlertFiringInfoMessage,
106117
alert.SearchRule.Namespace,
118+
alert.SearchRule.Name,
107119
alert.SearchRule.Spec.Description,
108120
))
109121

@@ -214,3 +226,19 @@ func (r *RulerActionReconciler) GetEventRuleAction(ctx context.Context, namespac
214226

215227
return ruleAction, nil
216228
}
229+
230+
// getRulerActionAssociatedAlerts returns all alerts associated with the RulerAction
231+
func (r *RulerActionReconciler) getRulerActionAssociatedAlerts(resource *v1alpha1.RulerAction) (alerts []*pools.Alert, err error) {
232+
233+
// Get all alerts from the AlertsPool
234+
alertsPool := r.AlertsPool.GetAll()
235+
236+
// Iterate over the alerts in the pool and check if the alert is associated with the RulerAction
237+
for _, alert := range alertsPool {
238+
if alert.RulerActionName == resource.Name {
239+
alerts = append(alerts, alert)
240+
}
241+
}
242+
243+
return alerts, nil
244+
}

internal/controller/searchrule_controller.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"time"
2323

2424
"k8s.io/apimachinery/pkg/runtime"
25+
"k8s.io/apimachinery/pkg/watch"
2526
ctrl "sigs.k8s.io/controller-runtime"
2627
"sigs.k8s.io/controller-runtime/pkg/client"
2728
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
@@ -78,15 +79,17 @@ func (r *SearchRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request)
7879
// 3. Check if the SearchRule instance is marked to be deleted: indicated by the deletion timestamp being set
7980
if !searchRuleResource.DeletionTimestamp.IsZero() {
8081
if controllerutil.ContainsFinalizer(searchRuleResource, resourceFinalizer) {
82+
83+
// 3.1 Delete the resources associated with the SearchRule
84+
err = r.Sync(ctx, watch.Deleted, searchRuleResource)
85+
8186
// Remove the finalizers on Patch CR
8287
controllerutil.RemoveFinalizer(searchRuleResource, resourceFinalizer)
8388
err = r.Update(ctx, searchRuleResource)
8489
if err != nil {
8590
logger.Info(fmt.Sprintf(resourceFinalizersUpdateError, SearchRuleResourceType, req.NamespacedName, err.Error()))
8691
}
8792
}
88-
// Delete credentials from pool
89-
r.RulesPool.Delete(fmt.Sprintf("%s/%s", searchRuleResource.Namespace, searchRuleResource.Name))
9093

9194
result = ctrl.Result{}
9295
err = nil
@@ -121,7 +124,7 @@ func (r *SearchRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request)
121124
}
122125

123126
// 7. Check the rule
124-
err = r.Sync(ctx, searchRuleResource)
127+
err = r.Sync(ctx, watch.Modified, searchRuleResource)
125128
if err != nil {
126129
r.UpdateConditionKubernetesApiCallFailure(searchRuleResource)
127130
logger.Info(fmt.Sprintf(syncTargetError, SearchRuleResourceType, req.NamespacedName, err.Error()))
Lines changed: 90 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/*
2+
Copyright 2024.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
117
package controller
218

319
import (
@@ -7,38 +23,101 @@ import (
723
v1alpha1 "prosimcorp.com/SearchRuler/api/v1alpha1"
824
)
925

10-
func (r *SearchRuleReconciler) UpdateConditionSuccess(searchRule *v1alpha1.SearchRule) {
26+
// UpdateConditionSuccess updates the status of the SearchRule resource with a success condition
27+
func (r *SearchRuleReconciler) UpdateConditionSuccess(SearchRule *v1alpha1.SearchRule) {
1128

12-
//
29+
// Create the new condition with the success status
1330
condition := globals.NewCondition(globals.ConditionTypeResourceSynced, metav1.ConditionTrue,
1431
globals.ConditionReasonTargetSynced, globals.ConditionReasonTargetSyncedMessage)
1532

16-
globals.UpdateCondition(&searchRule.Status.Conditions, condition)
33+
// Update the status of the SearchRule resource
34+
globals.UpdateCondition(&SearchRule.Status.Conditions, condition)
1735
}
1836

19-
func (r *SearchRuleReconciler) UpdateConditionKubernetesApiCallFailure(searchRule *v1alpha1.SearchRule) {
37+
// UpdateConditionKubernetesApiCallFailure updates the status of the SearchRule resource with a failure condition
38+
func (r *SearchRuleReconciler) UpdateConditionKubernetesApiCallFailure(SearchRule *v1alpha1.SearchRule) {
2039

21-
//
40+
// Create the new condition with the failure status
2241
condition := globals.NewCondition(globals.ConditionTypeResourceSynced, metav1.ConditionTrue,
2342
globals.ConditionReasonKubernetesApiCallErrorType, globals.ConditionReasonKubernetesApiCallErrorMessage)
2443

44+
// Update the status of the SearchRule resource
45+
globals.UpdateCondition(&SearchRule.Status.Conditions, condition)
46+
}
47+
48+
// Note: No state status means no alert fired never for this rule
49+
// UpdateConditionNoCredsFound updates the status of the SearchRule resource with alert firing condition
50+
func (r *SearchRuleReconciler) UpdateConditionAlertFiring(searchRule *v1alpha1.SearchRule) {
51+
52+
// Create the new condition with the alert firing status
53+
condition := globals.NewCondition(globals.ConditionTypeState, metav1.ConditionTrue,
54+
globals.ConditionReasonAlertFiring, globals.ConditionReasonAlertFiringMessage)
55+
56+
// Update the status of the SearchRule resource
2557
globals.UpdateCondition(&searchRule.Status.Conditions, condition)
2658
}
2759

28-
func (r *SearchRuleReconciler) UpdateConditionAlertFiring(searchRule *v1alpha1.SearchRule, conditionReasonAlertFiringMessage string) {
60+
// UpdateConditionNoCredsFound updates the status of the SearchRule resource with alert resolved condition
61+
func (r *SearchRuleReconciler) UpdateConditionAlertResolved(searchRule *v1alpha1.SearchRule) {
2962

30-
//
63+
// Create the new condition with the alert resolved status
3164
condition := globals.NewCondition(globals.ConditionTypeState, metav1.ConditionTrue,
32-
globals.ConditionReasonAlertFiring, conditionReasonAlertFiringMessage)
65+
globals.ConditionReasonAlertResolved, globals.ConditionReasonAlertResolvedMessage)
3366

67+
// Update the status of the SearchRule resource
3468
globals.UpdateCondition(&searchRule.Status.Conditions, condition)
3569
}
3670

37-
func (r *SearchRuleReconciler) UpdateConditionAlertResolved(searchRule *v1alpha1.SearchRule, conditionReasonAlertResolvedMessage string) {
71+
// UpdateConditionConnectionError updates the status of the SearchRule resource with a QueryConnector not found condition
72+
func (r *SearchRuleReconciler) UpdateConditionQueryConnectorNotFound(searchRule *v1alpha1.SearchRule) {
3873

39-
//
74+
// Create the new condition with the alert firing status
4075
condition := globals.NewCondition(globals.ConditionTypeState, metav1.ConditionTrue,
41-
globals.ConditionReasonAlertResolved, conditionReasonAlertResolvedMessage)
76+
globals.ConditionReasonQueryConnectorNotFoundType, globals.ConditionReasonQueryConnectorNotFoundMessage)
4277

78+
// Update the status of the SearchRule resource
4379
globals.UpdateCondition(&searchRule.Status.Conditions, condition)
4480
}
81+
82+
// UpdateConditionNoCredsFound updates the status of the SearchRule resource with a NoCreds condition
83+
func (r *SearchRuleReconciler) UpdateConditionNoCredsFound(SearchRule *v1alpha1.SearchRule) {
84+
85+
// Create the new condition with the success status
86+
condition := globals.NewCondition(globals.ConditionTypeState, metav1.ConditionTrue,
87+
globals.ConditionReasonNoCredsFoundType, globals.ConditionReasonNoCredsFoundMessage)
88+
89+
// Update the status of the SearchRule resource
90+
globals.UpdateCondition(&SearchRule.Status.Conditions, condition)
91+
}
92+
93+
func (r *SearchRuleReconciler) UpdateConditionNoQueryFound(SearchRule *v1alpha1.SearchRule) {
94+
95+
// Create the new condition with the success status
96+
condition := globals.NewCondition(globals.ConditionTypeState, metav1.ConditionTrue,
97+
globals.ConditionReasonNoQueryFoundType, globals.ConditionReasonNoQueryFoundMessage)
98+
99+
// Update the status of the SearchRule resource
100+
globals.UpdateCondition(&SearchRule.Status.Conditions, condition)
101+
}
102+
103+
// UpdateConditionConnectionError updates the status of the SearchRule resource with a ConnectionError condition
104+
func (r *SearchRuleReconciler) UpdateConditionConnectionError(SearchRule *v1alpha1.SearchRule) {
105+
106+
// Create the new condition with the failure status
107+
condition := globals.NewCondition(globals.ConditionTypeState, metav1.ConditionTrue,
108+
globals.ConditionReasonConnectionErrorType, globals.ConditionReasonConnectionErrorMessage)
109+
110+
// Update the status of the SearchRule resource
111+
globals.UpdateCondition(&SearchRule.Status.Conditions, condition)
112+
}
113+
114+
// UpdateConditionEvaluateTemplateError updates the status of the SearchRule resource with a QueryError condition
115+
func (r *SearchRuleReconciler) UpdateConditionQueryError(SearchRule *v1alpha1.SearchRule) {
116+
117+
// Create the new condition with the failure status
118+
condition := globals.NewCondition(globals.ConditionTypeState, metav1.ConditionTrue,
119+
globals.ConditionReasonQueryErrorType, globals.ConditionReasonQueryErrorMessage)
120+
121+
// Update the status of the SearchRule resource
122+
globals.UpdateCondition(&SearchRule.Status.Conditions, condition)
123+
}

0 commit comments

Comments
 (0)