Skip to content

Commit 37abec5

Browse files
committed
OCPBUGS-17113: Lazy updates for Prometheus
Incorporate openshift/library-go#1575 downstream. Signed-off-by: Pranshu Srivastava <[email protected]>
1 parent ec07655 commit 37abec5

File tree

7 files changed

+412
-43
lines changed

7 files changed

+412
-43
lines changed

Diff for: Makefile

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ GOPROXY?=http://proxy.golang.org
1212
export GO111MODULE
1313
export GOPROXY
1414

15-
# go pakages for unit tests, excluding e2e tests
15+
# go packages for unit tests, excluding e2e tests
1616
PKGS=$(shell go list ./... | grep -v /test/e2e)
1717
GOLANG_FILES:=$(shell find . -name \*.go -print)
1818
# NOTE: grep -v %.yaml is needed because "%s-policy.yaml" is used
@@ -249,6 +249,10 @@ check-runbooks:
249249
# Testing #
250250
###########
251251

252+
.PHONY: bench
253+
bench:
254+
go test -run NONE -bench=. -benchmem $(PKGS)
255+
252256
.PHONY: test
253257
test: test-unit test-rules test-e2e
254258

Diff for: go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ require (
1212
github.com/ghodss/yaml v1.0.0
1313
github.com/go-kit/log v0.2.1
1414
github.com/go-openapi/strfmt v0.23.0
15+
github.com/google/go-cmp v0.6.0
1516
github.com/google/uuid v1.6.0
1617
github.com/imdario/mergo v0.3.16
1718
github.com/mattn/go-shellwords v1.0.12
@@ -82,7 +83,6 @@ require (
8283
github.com/golang/protobuf v1.5.4 // indirect
8384
github.com/google/cel-go v0.20.1 // indirect
8485
github.com/google/gnostic-models v0.6.8 // indirect
85-
github.com/google/go-cmp v0.6.0 // indirect
8686
github.com/google/gofuzz v1.2.0 // indirect
8787
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
8888
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect

Diff for: pkg/client/client.go

+91-20
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import (
5050
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
5151
apierrors "k8s.io/apimachinery/pkg/api/errors"
5252
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
53+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
5354
"k8s.io/apimachinery/pkg/fields"
5455
"k8s.io/apimachinery/pkg/runtime"
5556
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -58,6 +59,7 @@ import (
5859
"k8s.io/apimachinery/pkg/util/intstr"
5960
"k8s.io/apimachinery/pkg/util/wait"
6061
"k8s.io/apimachinery/pkg/watch"
62+
"k8s.io/client-go/dynamic"
6163
"k8s.io/client-go/kubernetes"
6264
"k8s.io/client-go/metadata"
6365
"k8s.io/client-go/rest"
@@ -81,6 +83,7 @@ type Client struct {
8183
userWorkloadNamespace string
8284

8385
kclient kubernetes.Interface
86+
dclient dynamic.Interface
8487
mdataclient metadata.Interface
8588
osmclient openshiftmonitoringclientset.Interface
8689
oscclient openshiftconfigclientset.Interface
@@ -107,6 +110,14 @@ func NewForConfig(cfg *rest.Config, version string, namespace, userWorkloadNames
107110
client.kclient = kclient
108111
}
109112

113+
if client.dclient == nil {
114+
dclient, err := dynamic.NewForConfig(cfg)
115+
if err != nil {
116+
return nil, fmt.Errorf("creating dynamic clientset client: %w", err)
117+
}
118+
client.dclient = dclient
119+
}
120+
110121
if client.eclient == nil {
111122
eclient, err := apiextensionsclient.NewForConfig(cfg)
112123
if err != nil {
@@ -203,6 +214,12 @@ func KubernetesClient(kclient kubernetes.Interface) Option {
203214
}
204215
}
205216

217+
func DynamicClient(dclient *dynamic.DynamicClient) Option {
218+
return func(c *Client) {
219+
c.dclient = dclient
220+
}
221+
}
222+
206223
func OpenshiftMonitoringClient(osmclient openshiftmonitoringclientset.Interface) Option {
207224
return func(c *Client) {
208225
c.osmclient = osmclient
@@ -632,29 +649,75 @@ func (c *Client) GetAlertingRule(ctx context.Context, namespace, name string) (*
632649
return c.osmclient.MonitoringV1().AlertingRules(namespace).Get(ctx, name, metav1.GetOptions{})
633650
}
634651

635-
func (c *Client) CreateOrUpdatePrometheus(ctx context.Context, p *monv1.Prometheus) error {
636-
pclient := c.mclient.MonitoringV1().Prometheuses(p.GetNamespace())
637-
existing, err := pclient.Get(ctx, p.GetName(), metav1.GetOptions{})
652+
// CreateOrUpdatePrometheus does not use a defaulting function, and resorts only to the caching mechanism, since,
653+
// * defaults for Prometheus should be introduced from its operator level, and,
654+
// * the defaulting approach generally requires hardcoding the default values, which adds on to the maintenance overhead.
655+
// NOTE: The return values establish the following matrix (w.r.t. the create or update verbs):
656+
// * true, nil : verb action needed; operation successful,
657+
// * false, nil : verb action not needed; operation skipped,
658+
// * true, error : verb action needed, operation unsuccessful.
659+
// * false, error: verb action may or may not be needed; operation unsuccessful,
660+
func (c *Client) CreateOrUpdatePrometheus(ctx context.Context, structuredRequiredPrometheus *monv1.Prometheus) (bool, error) {
661+
unstructuredRequiredPrometheusObject, err := runtime.DefaultUnstructuredConverter.ToUnstructured(structuredRequiredPrometheus)
662+
if err != nil {
663+
return false, fmt.Errorf("converting Prometheus object to unstructured failed: %w", err)
664+
}
665+
unstructuredRequiredPrometheus := &unstructured.Unstructured{}
666+
unstructuredRequiredPrometheus.SetUnstructuredContent(unstructuredRequiredPrometheusObject)
667+
668+
// Set the GVK for the unstructured object, since these are not inferred automatically, unlike their structured counterparts.
669+
unstructuredRequiredPrometheus.SetGroupVersionKind(monv1.SchemeGroupVersion.WithKind(monv1.PrometheusesKind))
670+
671+
// Preserve the original behavior: always merge the metadata, never replace.
672+
// Refer: https://github.com/openshift/cluster-monitoring-operator/pull/942.
673+
prometheusGVR := unstructuredRequiredPrometheus.GroupVersionKind().GroupVersion().WithResource(monv1.PrometheusName)
674+
unstructuredExistingPrometheus, err := c.dclient.
675+
Resource(prometheusGVR).
676+
Namespace(structuredRequiredPrometheus.GetNamespace()).
677+
Get(ctx, structuredRequiredPrometheus.GetName(), metav1.GetOptions{})
638678
if apierrors.IsNotFound(err) {
639-
_, err := pclient.Create(ctx, p, metav1.CreateOptions{})
679+
_, didUpdate, err := resourceapply.ApplyUnstructuredResourceImproved(
680+
ctx,
681+
c.dclient,
682+
c.eventRecorder,
683+
unstructuredRequiredPrometheus,
684+
c.resourceCache,
685+
prometheusGVR,
686+
nil,
687+
nil,
688+
)
640689
if err != nil {
641-
return fmt.Errorf("creating Prometheus object failed: %w", err)
690+
return didUpdate, fmt.Errorf("creating Prometheus object failed: %w", err)
642691
}
643-
return nil
692+
693+
return true, nil
644694
}
645695
if err != nil {
646-
return fmt.Errorf("retrieving Prometheus object failed: %w", err)
696+
return false, fmt.Errorf("retrieving Prometheus object failed: %w", err)
647697
}
698+
unstructuredRequiredPrometheusMetadataLabels := unstructuredRequiredPrometheus.GetLabels()
699+
unstructuredRequiredPrometheusMetadataAnnotations := unstructuredRequiredPrometheus.GetAnnotations()
700+
mergeMetadataLabels(unstructuredRequiredPrometheusMetadataLabels, unstructuredExistingPrometheus.GetLabels())
701+
mergeMetadataAnnotations(unstructuredRequiredPrometheusMetadataAnnotations, unstructuredExistingPrometheus.GetAnnotations())
702+
unstructuredRequiredPrometheus.SetLabels(unstructuredRequiredPrometheusMetadataLabels)
703+
unstructuredRequiredPrometheus.SetAnnotations(unstructuredRequiredPrometheusMetadataAnnotations)
704+
unstructuredRequiredPrometheus.SetResourceVersion(unstructuredExistingPrometheus.GetResourceVersion())
648705

649-
required := p.DeepCopy()
650-
mergeMetadata(&required.ObjectMeta, existing.ObjectMeta)
651-
652-
required.ResourceVersion = existing.ResourceVersion
653-
_, err = pclient.Update(ctx, required, metav1.UpdateOptions{})
706+
_, didUpdate, err := resourceapply.ApplyUnstructuredResourceImproved(
707+
ctx,
708+
c.dclient,
709+
c.eventRecorder,
710+
unstructuredRequiredPrometheus,
711+
c.resourceCache,
712+
prometheusGVR,
713+
nil,
714+
nil,
715+
)
654716
if err != nil {
655-
return fmt.Errorf("updating Prometheus object failed: %w", err)
717+
return didUpdate, fmt.Errorf("updating Prometheus object failed: %w", err)
656718
}
657-
return nil
719+
720+
return didUpdate, nil
658721
}
659722

660723
func (c *Client) CreateOrUpdatePrometheusRule(ctx context.Context, p *monv1.PrometheusRule) error {
@@ -1878,20 +1941,28 @@ func (c *Client) VPACustomResourceDefinitionPresent(ctx context.Context, lastKno
18781941
// where keys starting from string defined in `metadataPrefix` are deleted. This prevents issues with preserving stale
18791942
// metadata defined by the operator
18801943
func mergeMetadata(required *metav1.ObjectMeta, existing metav1.ObjectMeta) {
1881-
for k := range existing.Annotations {
1944+
mergeMetadataLabels(required.Labels, existing.Labels)
1945+
mergeMetadataAnnotations(required.Annotations, existing.Annotations)
1946+
}
1947+
1948+
func mergeMetadataLabels(requiredLabels map[string]string, existingLabels map[string]string) {
1949+
for k := range existingLabels {
18821950
if strings.HasPrefix(k, metadataPrefix) {
1883-
delete(existing.Annotations, k)
1951+
delete(existingLabels, k)
18841952
}
18851953
}
18861954

1887-
for k := range existing.Labels {
1955+
_ = mergo.Merge(&requiredLabels, existingLabels)
1956+
}
1957+
1958+
func mergeMetadataAnnotations(requiredAnnotations map[string]string, existingAnnotations map[string]string) {
1959+
for k := range existingAnnotations {
18881960
if strings.HasPrefix(k, metadataPrefix) {
1889-
delete(existing.Labels, k)
1961+
delete(existingAnnotations, k)
18901962
}
18911963
}
18921964

1893-
_ = mergo.Merge(&required.Annotations, existing.Annotations)
1894-
_ = mergo.Merge(&required.Labels, existing.Labels)
1965+
_ = mergo.Merge(&requiredAnnotations, existingAnnotations)
18951966
}
18961967

18971968
type jsonPatch struct {

0 commit comments

Comments
 (0)