diff --git a/CHANGELOG.md b/CHANGELOG.md index f6d2fc1a0..cdcc0f3a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Delete per cluster heartbeats when Mimir is enabled. +- Delete per cluster heartbeat alertmanager wiring when Mimir is enabled. ## [4.75.1] - 2024-05-23 diff --git a/service/controller/clusterapi/resource.go b/service/controller/clusterapi/resource.go index 25ec9eb4b..ae850f832 100644 --- a/service/controller/clusterapi/resource.go +++ b/service/controller/clusterapi/resource.go @@ -146,6 +146,8 @@ func New(config Config) ([]resource.Interface, error) { Installation: config.Installation, Proxy: config.Proxy, + + MimirEnabled: config.MimirEnabled, } heartbeatWebhookConfigResource, err = heartbeatwebhookconfig.New(c) diff --git a/service/controller/managementcluster/resource.go b/service/controller/managementcluster/resource.go index 63ac67056..a02363495 100644 --- a/service/controller/managementcluster/resource.go +++ b/service/controller/managementcluster/resource.go @@ -191,6 +191,8 @@ func newResources(config resourcesConfig) ([]resource.Interface, error) { Installation: config.Installation, Proxy: config.Proxy, + + MimirEnabled: config.MimirEnabled, } heartbeatWebhookConfigResource, err = heartbeatwebhookconfig.New(c) diff --git a/service/controller/resource/alerting/heartbeatwebhookconfig/client.go b/service/controller/resource/alerting/heartbeatwebhookconfig/client.go deleted file mode 100644 index ba2ed1ce8..000000000 --- a/service/controller/resource/alerting/heartbeatwebhookconfig/client.go +++ /dev/null @@ -1,26 +0,0 @@ -package heartbeatwebhookconfig - -import ( - "golang.org/x/net/context" - - monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1" - monitoringv1alpha1client "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/typed/monitoring/v1alpha1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type wrappedClient struct { - client monitoringv1alpha1client.AlertmanagerConfigInterface -} - -func (c wrappedClient) Create(ctx context.Context, object metav1.Object, options metav1.CreateOptions) (metav1.Object, error) { - return c.client.Create(ctx, object.(*monitoringv1alpha1.AlertmanagerConfig), options) -} -func (c wrappedClient) Update(ctx context.Context, object metav1.Object, options metav1.UpdateOptions) (metav1.Object, error) { - return c.client.Update(ctx, object.(*monitoringv1alpha1.AlertmanagerConfig), options) -} -func (c wrappedClient) Get(ctx context.Context, name string, options metav1.GetOptions) (metav1.Object, error) { - return c.client.Get(ctx, name, options) -} -func (c wrappedClient) Delete(ctx context.Context, name string, options *metav1.DeleteOptions) error { - return c.client.Delete(ctx, name, *options) -} diff --git a/service/controller/resource/alerting/heartbeatwebhookconfig/create.go b/service/controller/resource/alerting/heartbeatwebhookconfig/create.go new file mode 100644 index 000000000..f2cbcdc56 --- /dev/null +++ b/service/controller/resource/alerting/heartbeatwebhookconfig/create.go @@ -0,0 +1,63 @@ +package heartbeatwebhookconfig + +import ( + "context" + + "github.com/giantswarm/microerror" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (r *Resource) EnsureCreated(ctx context.Context, obj interface{}) error { + if r.mimirEnabled { + return r.EnsureDeleted(ctx, obj) + } + + desired, err := r.toAlertmanagerConfig(obj) + if err != nil { + return microerror.Mask(err) + } + + r.logger.Debugf(ctx, "creating") + current, err := r.client.MonitoringV1alpha1().AlertmanagerConfigs(desired.GetNamespace()).Get(ctx, desired.GetName(), metav1.GetOptions{}) + if apierrors.IsNotFound(err) { + current, err = r.client.MonitoringV1alpha1().AlertmanagerConfigs(desired.GetNamespace()).Create(ctx, desired, metav1.CreateOptions{}) + } + + if err != nil { + return microerror.Mask(err) + } + + if r.hasChanged(current, desired) { + updateMeta(current, desired) + _, err = r.client.MonitoringV1alpha1().AlertmanagerConfigs(desired.GetNamespace()).Update(ctx, desired, metav1.UpdateOptions{}) + if err != nil { + return microerror.Mask(err) + } + } + r.logger.Debugf(ctx, "created") + + return nil +} + +func updateMeta(c, d metav1.Object) { + d.SetGenerateName(c.GetGenerateName()) + d.SetUID(c.GetUID()) + d.SetResourceVersion(c.GetResourceVersion()) + d.SetGeneration(c.GetGeneration()) + d.SetSelfLink(c.GetSelfLink()) + d.SetCreationTimestamp(c.GetCreationTimestamp()) + d.SetDeletionTimestamp(c.GetDeletionTimestamp()) + d.SetDeletionGracePeriodSeconds(c.GetDeletionGracePeriodSeconds()) + // without this, it's impossible to change labels on resources + if len(d.GetLabels()) == 0 { + d.SetLabels(c.GetLabels()) + } + // without this, it's impossible to change annotations on resources + if len(d.GetAnnotations()) == 0 { + d.SetAnnotations(c.GetAnnotations()) + } + d.SetFinalizers(c.GetFinalizers()) + d.SetOwnerReferences(c.GetOwnerReferences()) + d.SetManagedFields(c.GetManagedFields()) +} diff --git a/service/controller/resource/alerting/heartbeatwebhookconfig/delete.go b/service/controller/resource/alerting/heartbeatwebhookconfig/delete.go new file mode 100644 index 000000000..5f7b59ec6 --- /dev/null +++ b/service/controller/resource/alerting/heartbeatwebhookconfig/delete.go @@ -0,0 +1,27 @@ +package heartbeatwebhookconfig + +import ( + "context" + + "github.com/giantswarm/microerror" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (r *Resource) EnsureDeleted(ctx context.Context, obj interface{}) error { + object, err := r.getObjectMeta(obj) + if err != nil { + return microerror.Mask(err) + } + + r.logger.Debugf(ctx, "deleting") + err = r.client.MonitoringV1alpha1().AlertmanagerConfigs(object.GetNamespace()).Delete(ctx, object.GetName(), metav1.DeleteOptions{}) + if apierrors.IsNotFound(err) { + // fall through + } else if err != nil { + return microerror.Mask(err) + } + r.logger.Debugf(ctx, "deleted") + + return nil +} diff --git a/service/controller/resource/alerting/heartbeatwebhookconfig/error.go b/service/controller/resource/alerting/heartbeatwebhookconfig/error.go index d16321632..2b2b0d045 100644 --- a/service/controller/resource/alerting/heartbeatwebhookconfig/error.go +++ b/service/controller/resource/alerting/heartbeatwebhookconfig/error.go @@ -12,12 +12,3 @@ var invalidConfigError = µerror.Error{ func IsInvalidConfig(err error) bool { return microerror.Cause(err) == invalidConfigError } - -var wrongTypeError = µerror.Error{ - Kind: "wrongTypeError", -} - -// IsWrongType asserts wrongTypeError. -func IsWrongType(err error) bool { - return microerror.Cause(err) == wrongTypeError -} diff --git a/service/controller/resource/alerting/heartbeatwebhookconfig/resource.go b/service/controller/resource/alerting/heartbeatwebhookconfig/resource.go index 4a4329430..ba497e031 100644 --- a/service/controller/resource/alerting/heartbeatwebhookconfig/resource.go +++ b/service/controller/resource/alerting/heartbeatwebhookconfig/resource.go @@ -9,11 +9,9 @@ import ( monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1" monitoringclient "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned" - "golang.org/x/net/context" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/giantswarm/prometheus-meta-operator/v2/service/controller/resource/generic" "github.com/giantswarm/prometheus-meta-operator/v2/service/key" ) @@ -26,35 +24,39 @@ type Config struct { Logger micrologger.Logger Installation string Proxy func(reqURL *url.URL) (*url.URL, error) + + MimirEnabled bool } -func New(config Config) (*generic.Resource, error) { - clientFunc := func(namespace string) generic.Interface { - c := config.Client.MonitoringV1alpha1().AlertmanagerConfigs(namespace) - return wrappedClient{client: c} - } +type Resource struct { + client monitoringclient.Interface + logger micrologger.Logger + installation string + proxy func(reqURL *url.URL) (*url.URL, error) - c := generic.Config{ - ClientFunc: clientFunc, - Logger: config.Logger, - Name: Name, - GetObjectMeta: func(ctx context.Context, v interface{}) (metav1.ObjectMeta, error) { - return getObjectMeta(v) - }, - GetDesiredObject: func(ctx context.Context, v interface{}) (metav1.Object, error) { - return toAlertmanagerConfig(v, config) - }, - HasChangedFunc: hasChanged, + mimirEnabled bool +} + +func New(config Config) (*Resource, error) { + if config.Logger == nil { + return nil, microerror.Maskf(invalidConfigError, "%T.Logger must not be empty", config) } - r, err := generic.New(c) - if err != nil { - return nil, microerror.Mask(err) + if config.Installation == "" { + return nil, microerror.Maskf(invalidConfigError, "%T.Installation must not be empty", config) + } + + r := &Resource{ + client: config.Client, + logger: config.Logger, + installation: config.Installation, + proxy: config.Proxy, + mimirEnabled: config.MimirEnabled, } return r, nil } -func getObjectMeta(v interface{}) (metav1.ObjectMeta, error) { +func (r Resource) getObjectMeta(v interface{}) (metav1.ObjectMeta, error) { cluster, err := key.ToCluster(v) if err != nil { return metav1.ObjectMeta{}, microerror.Mask(err) @@ -67,12 +69,12 @@ func getObjectMeta(v interface{}) (metav1.ObjectMeta, error) { }, nil } -func toAlertmanagerConfig(v interface{}, config Config) (metav1.Object, error) { +func (r Resource) toAlertmanagerConfig(v interface{}) (*monitoringv1alpha1.AlertmanagerConfig, error) { if v == nil { return nil, nil } - objectMeta, err := getObjectMeta(v) + objectMeta, err := r.getObjectMeta(v) if err != nil { return nil, microerror.Mask(err) } @@ -88,7 +90,7 @@ func toAlertmanagerConfig(v interface{}, config Config) (metav1.Object, error) { } sendResolved := false - urlAddress, err := url.Parse(key.HeartbeatAPI(cluster, config.Installation)) + urlAddress, err := url.Parse(key.HeartbeatAPI(cluster, r.installation)) if err != nil { return nil, microerror.Mask(err) } @@ -110,7 +112,7 @@ func toAlertmanagerConfig(v interface{}, config Config) (metav1.Object, error) { if err != nil { return nil, err } - proxyURL, err := config.Proxy(opsgenieUrl) + proxyURL, err := r.proxy(opsgenieUrl) if err != nil { return nil, err } @@ -119,7 +121,7 @@ func toAlertmanagerConfig(v interface{}, config Config) (metav1.Object, error) { } receiver := monitoringv1alpha1.Receiver{ - Name: key.HeartbeatReceiverName(cluster, config.Installation), + Name: key.HeartbeatReceiverName(cluster, r.installation), WebhookConfigs: []monitoringv1alpha1.WebhookConfig{ { URL: &address, @@ -133,10 +135,10 @@ func toAlertmanagerConfig(v interface{}, config Config) (metav1.Object, error) { ObjectMeta: objectMeta, Spec: monitoringv1alpha1.AlertmanagerConfigSpec{ Route: &monitoringv1alpha1.Route{ - Receiver: key.HeartbeatReceiverName(cluster, config.Installation), + Receiver: key.HeartbeatReceiverName(cluster, r.installation), Matchers: []monitoringv1alpha1.Matcher{ {Name: key.ClusterIDKey, Value: key.ClusterID(cluster)}, - {Name: key.InstallationKey, Value: config.Installation}, + {Name: key.InstallationKey, Value: r.installation}, {Name: key.TypeKey, Value: key.Heartbeat()}, }, Continue: false, @@ -154,9 +156,13 @@ func toAlertmanagerConfig(v interface{}, config Config) (metav1.Object, error) { return alertmanagerConfig, nil } -func hasChanged(current, desired metav1.Object) bool { +func (r Resource) hasChanged(current, desired metav1.Object) bool { c := current.(*monitoringv1alpha1.AlertmanagerConfig) d := desired.(*monitoringv1alpha1.AlertmanagerConfig) return !reflect.DeepEqual(c.Spec, d.Spec) } + +func (r *Resource) Name() string { + return Name +} diff --git a/service/controller/resource/alerting/heartbeatwebhookconfig/resource_test.go b/service/controller/resource/alerting/heartbeatwebhookconfig/resource_test.go index 08a3647e4..957b2273d 100644 --- a/service/controller/resource/alerting/heartbeatwebhookconfig/resource_test.go +++ b/service/controller/resource/alerting/heartbeatwebhookconfig/resource_test.go @@ -7,18 +7,36 @@ import ( "golang.org/x/net/http/httpproxy" + "github.com/giantswarm/micrologger" "github.com/giantswarm/prometheus-meta-operator/v2/pkg/unittest" ) var update = flag.Bool("update", false, "update the output file") -func TestAlertmanager(t *testing.T) { +func TestAlertmanagerConfig(t *testing.T) { + var err error + var logger micrologger.Logger + { + c := micrologger.Config{} + + logger, err = micrologger.New(c) + if err != nil { + t.Fatal(err) + } + } + proxyConfig := httpproxy.Config{} config := Config{ Proxy: proxyConfig.ProxyFunc(), + Logger: logger, Installation: "test-installation", } + resource, err := New(config) + if err != nil { + t.Fatal(err) + } + for _, flavor := range unittest.ProviderFlavors { outputDir, err := filepath.Abs("./test/" + flavor) if err != nil { @@ -29,7 +47,7 @@ func TestAlertmanager(t *testing.T) { Flavor: flavor, T: t, TestFunc: func(v interface{}) (interface{}, error) { - return toAlertmanagerConfig(v, config) + return resource.toAlertmanagerConfig(v) }, Update: *update, }