Skip to content

Commit

Permalink
OCPBUGS-17113: Lazy updates for Prometheus
Browse files Browse the repository at this point in the history
Incorporate openshift/library-go#1575
downstream.

Signed-off-by: Pranshu Srivastava <rexagod@gmail.com>
  • Loading branch information
rexagod committed Jun 26, 2024
1 parent 5d1fd1b commit 29846c5
Show file tree
Hide file tree
Showing 10 changed files with 583 additions and 143 deletions.
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/ghodss/yaml v1.0.0
github.com/go-kit/log v0.2.1
github.com/go-openapi/strfmt v0.22.0
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.6.0
github.com/imdario/mergo v0.3.16
github.com/openshift/api v0.0.0-20240618205917-987b8890c273
Expand Down Expand Up @@ -81,7 +82,6 @@ require (
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/cel-go v0.17.8 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
Expand Down Expand Up @@ -148,3 +148,5 @@ require (
sigs.k8s.io/kube-storage-version-migrator v0.0.6-0.20230721195810-5c8923c5ff96 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
)

replace github.com/openshift/library-go => github.com/rexagod/library-go v0.0.0-20240625152537-56a2f7ff3336
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -396,8 +396,6 @@ github.com/openshift/api v0.0.0-20240618205917-987b8890c273 h1:a2B5ocKga0ckZlb4f
github.com/openshift/api v0.0.0-20240618205917-987b8890c273/go.mod h1:OOh6Qopf21pSzqNVCB5gomomBXb8o5sGKZxG2KNpaXM=
github.com/openshift/client-go v0.0.0-20240528061634-b054aa794d87 h1:JtLhaGpSEconE+1IKmIgCOof/Len5ceG6H1pk43yv5U=
github.com/openshift/client-go v0.0.0-20240528061634-b054aa794d87/go.mod h1:3IPD4U0qyovZS4EFady2kqY32m8lGcbs/Wx+yprg9z8=
github.com/openshift/library-go v0.0.0-20240619120114-0c65da30ad30 h1:c9PNqAVBbnsR4Ro+P2e2Ih3aacnq5l1IfGX5985Rd7c=
github.com/openshift/library-go v0.0.0-20240619120114-0c65da30ad30/go.mod h1:PdASVamWinll2BPxiUpXajTwZxV8A1pQbWEsCN1od7I=
github.com/ovh/go-ovh v1.4.3 h1:Gs3V823zwTFpzgGLZNI6ILS4rmxZgJwJCz54Er9LwD0=
github.com/ovh/go-ovh v1.4.3/go.mod h1:AkPXVtgwB6xlKblMjRKJJmjRp+ogrE7fz2lVgcQY8SY=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
Expand Down Expand Up @@ -443,6 +441,8 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/prometheus/prometheus v0.48.1 h1:CTszphSNTXkuCG6O0IfpKdHcJkvvnAAE1GbELKS+NFk=
github.com/prometheus/prometheus v0.48.1/go.mod h1:SRw624aMAxTfryAcP8rOjg4S/sHHaetx2lyJJ2nM83g=
github.com/rexagod/library-go v0.0.0-20240625152537-56a2f7ff3336 h1:weB3nceKGmmC3JREE/OelCSILuUATFHDgjhVVjcYgoI=
github.com/rexagod/library-go v0.0.0-20240625152537-56a2f7ff3336/go.mod h1:/wmao3qtqOQ484HDka9cWP7SIvOQOdzpmhyXkF2YdzE=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
Expand Down
130 changes: 111 additions & 19 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import (
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand All @@ -58,6 +59,7 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/metadata"
"k8s.io/client-go/rest"
Expand All @@ -81,6 +83,7 @@ type Client struct {
userWorkloadNamespace string

kclient kubernetes.Interface
dclient dynamic.Interface
mdataclient metadata.Interface
osmclient openshiftmonitoringclientset.Interface
oscclient openshiftconfigclientset.Interface
Expand All @@ -107,6 +110,14 @@ func NewForConfig(cfg *rest.Config, version string, namespace, userWorkloadNames
client.kclient = kclient
}

if client.dclient == nil {
dclient, err := dynamic.NewForConfig(cfg)
if err != nil {
return nil, fmt.Errorf("creating dynamic clientset client: %w", err)
}
client.dclient = dclient
}

if client.eclient == nil {
eclient, err := apiextensionsclient.NewForConfig(cfg)
if err != nil {
Expand Down Expand Up @@ -203,6 +214,12 @@ func KubernetesClient(kclient kubernetes.Interface) Option {
}
}

func DynamicClient(dclient *dynamic.DynamicClient) Option {
return func(c *Client) {
c.dclient = dclient
}
}

func OpenshiftMonitoringClient(osmclient openshiftmonitoringclientset.Interface) Option {
return func(c *Client) {
c.osmclient = osmclient
Expand Down Expand Up @@ -632,29 +649,96 @@ func (c *Client) GetAlertingRule(ctx context.Context, namespace, name string) (*
return c.osmclient.MonitoringV1().AlertingRules(namespace).Get(ctx, name, metav1.GetOptions{})
}

func (c *Client) CreateOrUpdatePrometheus(ctx context.Context, p *monv1.Prometheus) error {
pclient := c.mclient.MonitoringV1().Prometheuses(p.GetNamespace())
existing, err := pclient.Get(ctx, p.GetName(), metav1.GetOptions{})
func (c *Client) CreateOrUpdatePrometheus(ctx context.Context, structuredRequiredPrometheus *monv1.Prometheus) (*bool, error) {
unstructuredRequiredPrometheusObject, err := runtime.DefaultUnstructuredConverter.ToUnstructured(structuredRequiredPrometheus)
if err != nil {
return nil, fmt.Errorf("converting Prometheus object to unstructured failed: %w", err)
}
unstructuredRequiredPrometheus := &unstructured.Unstructured{}
unstructuredRequiredPrometheus.SetUnstructuredContent(unstructuredRequiredPrometheusObject)

// Preserve the original behavior: always merge the metadata, never replace.
// Refer: https://github.com/openshift/cluster-monitoring-operator/pull/942.
unstructuredExistingPrometheus, err := c.dclient.Resource(structuredRequiredPrometheus.GroupVersionKind().GroupVersion().WithResource("prometheuses")).Namespace(structuredRequiredPrometheus.GetNamespace()).Get(ctx, structuredRequiredPrometheus.GetName(), metav1.GetOptions{})
if apierrors.IsNotFound(err) {
_, err := pclient.Create(ctx, p, metav1.CreateOptions{})
_, err := c.dclient.Resource(structuredRequiredPrometheus.GroupVersionKind().GroupVersion().WithResource("prometheuses")).Namespace(structuredRequiredPrometheus.GetNamespace()).Create(ctx, unstructuredRequiredPrometheus, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("creating Prometheus object failed: %w", err)
return nil, fmt.Errorf("creating Prometheus object failed: %w", err)
}
return nil
return ptr.To(true), nil
}
if err != nil {
return fmt.Errorf("retrieving Prometheus object failed: %w", err)
return nil, fmt.Errorf("retrieving Prometheus object failed: %w", err)
}
unstructuredRequiredPrometheusMetadataLabels := unstructuredRequiredPrometheus.GetLabels()
unstructuredRequiredPrometheusMetadataAnnotations := unstructuredRequiredPrometheus.GetAnnotations()
mergeMetadataLabels(unstructuredRequiredPrometheusMetadataLabels, unstructuredExistingPrometheus.GetLabels())
mergeMetadataAnnotations(unstructuredRequiredPrometheusMetadataAnnotations, unstructuredExistingPrometheus.GetAnnotations())
unstructuredRequiredPrometheus.SetLabels(unstructuredRequiredPrometheusMetadataLabels)
unstructuredRequiredPrometheus.SetAnnotations(unstructuredRequiredPrometheusMetadataAnnotations)
unstructuredRequiredPrometheus.SetResourceVersion(unstructuredExistingPrometheus.GetResourceVersion())

required := p.DeepCopy()
mergeMetadata(&required.ObjectMeta, existing.ObjectMeta)
_, didUpdate, err := resourceapply.ApplyUnstructuredResourceImproved(
ctx,
c.dclient,
c.eventRecorder,
unstructuredRequiredPrometheus,
c.resourceCache,
structuredRequiredPrometheus.GroupVersionKind().GroupVersion().WithResource("prometheuses"),
prometheusDefaultingFunc,
nil,
)
if err != nil {
return &didUpdate, fmt.Errorf("updating Prometheus object failed: %w", err)
}

required.ResourceVersion = existing.ResourceVersion
_, err = pclient.Update(ctx, required, metav1.UpdateOptions{})
return &didUpdate, nil
}

func prometheusDefaultingFunc(unstructuredPrometheus *unstructured.Unstructured) {
// Cast to the corresponding structured representation.
structuredPrometheus := &monv1.Prometheus{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredPrometheus.UnstructuredContent(), structuredPrometheus); err != nil {
klog.ErrorS(err, "failed to convert unstructured to structured Prometheus spec")
return
}

// Set defaults.
if structuredPrometheus.Spec.CommonPrometheusFields.ScrapeInterval == "" {
structuredPrometheus.Spec.CommonPrometheusFields.ScrapeInterval = "30s"
}
if len(structuredPrometheus.Spec.CommonPrometheusFields.ExternalLabels) == 0 {
structuredPrometheus.Spec.CommonPrometheusFields.ExternalLabels = nil
}
if len(structuredPrometheus.Spec.CommonPrometheusFields.EnableFeatures) == 0 {
structuredPrometheus.Spec.CommonPrometheusFields.EnableFeatures = nil
}
for i, container := range structuredPrometheus.Spec.CommonPrometheusFields.Containers {
for j, port := range container.Ports {
if port.Protocol == "" {
structuredPrometheus.Spec.CommonPrometheusFields.Containers[i].Ports[j].Protocol = "TCP"
}
}
}
if structuredPrometheus.Spec.CommonPrometheusFields.PortName == "" {
structuredPrometheus.Spec.CommonPrometheusFields.PortName = "web"
}
if structuredPrometheus.Spec.Thanos == nil {
structuredPrometheus.Spec.Thanos = &monv1.ThanosSpec{}
}
if structuredPrometheus.Spec.Thanos.BlockDuration == "" {
structuredPrometheus.Spec.Thanos.BlockDuration = "2h"
}
if structuredPrometheus.Spec.EvaluationInterval == "" {
structuredPrometheus.Spec.EvaluationInterval = "30s"
}

// Convert back to the corresponding unstructured representation and inject.
var err error
unstructuredPrometheus.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(structuredPrometheus)
if err != nil {
return fmt.Errorf("updating Prometheus object failed: %w", err)
klog.ErrorS(err, "failed to convert structured to unstructured Prometheus")
}
return nil
}

func (c *Client) CreateOrUpdatePrometheusRule(ctx context.Context, p *monv1.PrometheusRule) error {
Expand Down Expand Up @@ -1788,20 +1872,28 @@ func (c *Client) VPACustomResourceDefinitionPresent(ctx context.Context, lastKno
// where keys starting from string defined in `metadataPrefix` are deleted. This prevents issues with preserving stale
// metadata defined by the operator
func mergeMetadata(required *metav1.ObjectMeta, existing metav1.ObjectMeta) {
for k := range existing.Annotations {
mergeMetadataLabels(required.Labels, existing.Labels)
mergeMetadataAnnotations(required.Annotations, existing.Annotations)
}

func mergeMetadataLabels(requiredLabels map[string]string, existingLabels map[string]string) {
for k := range existingLabels {
if strings.HasPrefix(k, metadataPrefix) {
delete(existing.Annotations, k)
delete(existingLabels, k)
}
}

for k := range existing.Labels {
_ = mergo.Merge(&requiredLabels, existingLabels)
}

func mergeMetadataAnnotations(requiredAnnotations map[string]string, existingAnnotations map[string]string) {
for k := range existingAnnotations {
if strings.HasPrefix(k, metadataPrefix) {
delete(existing.Labels, k)
delete(existingAnnotations, k)
}
}

_ = mergo.Merge(&required.Annotations, existing.Annotations)
_ = mergo.Merge(&required.Labels, existing.Labels)
_ = mergo.Merge(&requiredAnnotations, existingAnnotations)
}

type jsonPatch struct {
Expand Down
Loading

0 comments on commit 29846c5

Please sign in to comment.