Skip to content

Commit 453cb2b

Browse files
committed
New conditional gatherer - basic structure
1 parent 25b06fa commit 453cb2b

File tree

6 files changed

+166
-15
lines changed

6 files changed

+166
-15
lines changed

manifests/03-clusterrole.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,14 @@ rules:
212212
- get
213213
- list
214214
- watch
215+
- apiGroups:
216+
- apiserver.openshift.io
217+
resources:
218+
- apirequestcounts
219+
verbs:
220+
- get
221+
- list
222+
- watch
215223
---
216224
apiVersion: rbac.authorization.k8s.io/v1
217225
kind: ClusterRoleBinding

pkg/gatherers/conditional/conditional_gatherer.go

+38-8
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
var gatheringFunctionBuilders = map[GatheringFunctionName]GathererFunctionBuilderPtr{
2828
GatherLogsOfNamespace: (*Gatherer).BuildGatherLogsOfNamespace,
2929
GatherImageStreamsOfNamespace: (*Gatherer).BuildGatherImageStreamsOfNamespace,
30+
GatherApiRequestCounts: (*Gatherer).BuildGatherApiRequestCounts,
3031
}
3132

3233
// gatheringRules contains all the rules used to run conditional gatherings.
@@ -77,6 +78,21 @@ var defaultGatheringRules = []GatheringRule{
7778
},
7879
},
7980
},
81+
{
82+
Conditions: []ConditionWithParams{
83+
{
84+
Type: AlertIsFiring,
85+
Params: AlertIsFiringConditionParams{
86+
Name: "APIRemovedInNextReleaseInUse",
87+
},
88+
},
89+
},
90+
GatheringFunctions: GatheringFunctions{
91+
GatherApiRequestCounts: GatherApiRequestCountsParams{
92+
AlertName: "APIRemovedInNextReleaseInUse",
93+
},
94+
},
95+
},
8096
}
8197

8298
const canConditionalGathererFail = false
@@ -86,8 +102,9 @@ type Gatherer struct {
86102
gatherProtoKubeConfig *rest.Config
87103
metricsGatherKubeConfig *rest.Config
88104
imageKubeConfig *rest.Config
89-
firingAlerts map[string]bool // golang doesn't have sets :(
90-
gatheringRules []GatheringRule
105+
// there can be multiple instances of the same alert
106+
firingAlerts map[string][]Alert // golang doesn't have sets :(
107+
gatheringRules []GatheringRule
91108
}
92109

93110
// New creates a new instance of conditional gatherer with the appropriate configs
@@ -138,7 +155,6 @@ func (g *Gatherer) GetGatheringFunctions(ctx context.Context) (map[string]gather
138155
if err != nil {
139156
return nil, err
140157
}
141-
142158
if allConditionsAreSatisfied {
143159
functions, errs := g.createGatheringClosures(conditionalGathering.GatheringFunctions)
144160
if len(errs) > 0 {
@@ -206,7 +222,7 @@ func (g *Gatherer) updateAlertsCache(ctx context.Context) error {
206222
func (g *Gatherer) updateAlertsCacheFromClient(ctx context.Context, metricsClient rest.Interface) error {
207223
const logPrefix = "conditional gatherer: "
208224

209-
g.firingAlerts = make(map[string]bool)
225+
g.firingAlerts = make(map[string][]Alert)
210226

211227
data, err := metricsClient.Get().AbsPath("federate").
212228
Param("match[]", `ALERTS{alertstate="firing"}`).
@@ -237,17 +253,31 @@ func (g *Gatherer) updateAlertsCacheFromClient(ctx context.Context, metricsClien
237253
klog.Info(logPrefix + "metric is nil")
238254
continue
239255
}
240-
256+
var alert Alert
241257
for _, label := range metric.GetLabel() {
242258
if label == nil {
243259
klog.Info(logPrefix + "label is nil")
244260
continue
245261
}
246-
247-
if label.GetName() == "alertname" {
248-
g.firingAlerts[label.GetValue()] = true
262+
switch label.GetName() {
263+
case "alertname":
264+
alert.Name = label.GetValue()
265+
case "version":
266+
alert.Version = label.GetValue()
267+
case "resource":
268+
alert.Resource = label.GetValue()
269+
case "group":
270+
alert.Group = label.GetValue()
249271
}
250272
}
273+
alerts, ok := g.firingAlerts[alert.Name]
274+
if ok {
275+
alerts = append(alerts, alert)
276+
} else {
277+
alerts = []Alert{alert}
278+
}
279+
280+
g.firingAlerts[alert.Name] = alerts
251281
}
252282

253283
return nil
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package conditional
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/openshift/insights-operator/pkg/gatherers"
8+
"github.com/openshift/insights-operator/pkg/record"
9+
"k8s.io/apimachinery/pkg/api/errors"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
12+
"k8s.io/apimachinery/pkg/runtime/schema"
13+
"k8s.io/client-go/dynamic"
14+
)
15+
16+
type ApiRequestCount struct {
17+
ResourceName string
18+
TotalRequestCount int64
19+
LastDayRequestCount int64
20+
}
21+
22+
func (g *Gatherer) BuildGatherApiRequestCounts(paramsInterface interface{}) (gatherers.GatheringClosure, error) {
23+
params, ok := paramsInterface.(GatherApiRequestCountsParams)
24+
if !ok {
25+
return gatherers.GatheringClosure{}, fmt.Errorf(
26+
"unexpected type in paramsInterface, expected %T, got %T",
27+
GatherApiRequestCountsParams{}, paramsInterface,
28+
)
29+
}
30+
31+
return gatherers.GatheringClosure{
32+
Run: func(ctx context.Context) ([]record.Record, []error) {
33+
records, err := g.gatherApiRequestCounts(ctx, params.AlertName)
34+
if err != nil {
35+
return records, []error{err}
36+
}
37+
return records, nil
38+
},
39+
CanFail: canConditionalGathererFail,
40+
}, nil
41+
}
42+
43+
func (g *Gatherer) gatherApiRequestCounts(ctx context.Context, alertName string) ([]record.Record, error) {
44+
45+
var resources []string
46+
for _, a := range g.firingAlerts[alertName] {
47+
resourceName := fmt.Sprintf("%s.%s.%s", a.Resource, a.Version, a.Group)
48+
resources = append(resources, resourceName)
49+
}
50+
51+
dynamicClient, err := dynamic.NewForConfig(g.gatherProtoKubeConfig)
52+
if err != nil {
53+
return nil, err
54+
}
55+
gvr := schema.GroupVersionResource{Group: "apiserver.openshift.io", Version: "v1", Resource: "apirequestcounts"}
56+
apiReqCounts, err := dynamicClient.Resource(gvr).List(ctx, metav1.ListOptions{})
57+
if errors.IsNotFound(err) {
58+
return nil, nil
59+
}
60+
if err != nil {
61+
return nil, err
62+
}
63+
var records []record.Record
64+
for i := range apiReqCounts.Items {
65+
it := apiReqCounts.Items[i]
66+
67+
// TODO check name is in array of resources
68+
if it.GetName() == resources[0] {
69+
// TODO - handle errors
70+
totalReqCount, _, _ := unstructured.NestedInt64(it.Object, "status", "requestCount")
71+
lastDayReqCount, _, _ := unstructured.NestedInt64(it.Object, "status", "currentHour", "requestCount")
72+
apiReqCount := ApiRequestCount{
73+
TotalRequestCount: totalReqCount,
74+
LastDayRequestCount: lastDayReqCount,
75+
ResourceName: "TBD",
76+
}
77+
records = append(records, record.Record{Name: "TBD", Item: record.JSONMarshaller{Object: apiReqCount}})
78+
}
79+
}
80+
return records, nil
81+
}

pkg/gatherers/conditional/gathering_functions.go

+18-7
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,19 @@ type GatheringFunctions = map[GatheringFunctionName]interface{}
1313
// GatheringFunctionName defines functions of conditional gatherer
1414
type GatheringFunctionName string
1515

16-
// GatherLogsOfNamespace is a function collecting logs of the provided namespace.
17-
// See file gather_logs_of_namespace.go
18-
const GatherLogsOfNamespace GatheringFunctionName = "logs_of_namespace"
19-
20-
// GatherImageStreamsOfNamespace is a function collecting image streams of the provided namespace.
21-
// See file gather_image_streams_of_namespace.go
22-
const GatherImageStreamsOfNamespace GatheringFunctionName = "image_streams_of_namespace"
16+
const (
17+
// GatherLogsOfNamespace is a function collecting logs of the provided namespace.
18+
// See file gather_logs_of_namespace.go
19+
GatherLogsOfNamespace GatheringFunctionName = "logs_of_namespace"
20+
21+
// GatherImageStreamsOfNamespace is a function collecting image streams of the provided namespace.
22+
// See file gather_image_streams_of_namespace.go
23+
GatherImageStreamsOfNamespace GatheringFunctionName = "image_streams_of_namespace"
24+
25+
// GatherApiRequestCounts is a function collecting api request counts for the resources read
26+
// from the corresponding alert
27+
GatherApiRequestCounts GatheringFunctionName = "api_request_counts"
28+
)
2329

2430
func (name GatheringFunctionName) NewParams(jsonParams []byte) (interface{}, error) {
2531
switch name {
@@ -50,3 +56,8 @@ type GatherImageStreamsOfNamespaceParams struct {
5056
// Namespace from which to collect image streams
5157
Namespace string `json:"namespace"`
5258
}
59+
60+
// GatherImageStreamsOfNamespaceParams defines parameters for api_request_counts gatherer
61+
type GatherApiRequestCountsParams struct {
62+
AlertName string `json:"alert_name"`
63+
}

pkg/gatherers/conditional/gathering_rule.schema.json

+13
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,19 @@
120120
"pattern": "^openshift-[a-zA-Z0-9_.-]{1,128}$"
121121
}
122122
}
123+
},
124+
"^api_request_counts$": {
125+
"type": "object",
126+
"title": "GatherApiRequestCountsParams",
127+
"required": [
128+
"alert_name"
129+
],
130+
"properties": {
131+
"alert_name": {
132+
"type": "string",
133+
"title": "AlertName"
134+
}
135+
}
123136
}
124137
}
125138
}

pkg/gatherers/conditional/types.go

+8
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,11 @@ type GatheringRule struct {
1515

1616
// GathererFunctionBuilderPtr defines a pointer to a gatherer function builder
1717
type GathererFunctionBuilderPtr = func(*Gatherer, interface{}) (gatherers.GatheringClosure, error)
18+
19+
// Alert defines basic alert attributes (basically alert labels)
20+
type Alert struct {
21+
Name string
22+
Resource string
23+
Version string
24+
Group string
25+
}

0 commit comments

Comments
 (0)