Skip to content

Commit

Permalink
[ACM-10706] Add support for custom alertmanager url (stolostron#1419)
Browse files Browse the repository at this point in the history
* Add support for custom alertmanager hub URL

Signed-off-by: Douglas Camata <[email protected]>

* Add some missing contexts args

Signed-off-by: Douglas Camata <[email protected]>

* Add tests for `GetAlertmanagerEndpoint`

Signed-off-by: Douglas Camata <[email protected]>

---------

Signed-off-by: Douglas Camata <[email protected]>
  • Loading branch information
douglascamata authored May 2, 2024
1 parent afc17b4 commit a9fbe5f
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ type AdvancedConfig struct {
// For the metrics-collector that runs in the hub this setting has no effect.
// +optional
CustomObservabilityHubURL observabilityshared.URL `json:"customObservabilityHubURL,omitempty"`
// CustomAlertmanagerHubURL overrides the alertmanager URL to send alerts from the spoke
// to the hub server.
// For the alertmanager that runs in the hub this setting has no effect.
// +optional
CustomAlertmanagerHubURL observabilityshared.URL `json:"customAlertmanagerHubURL,omitempty"`
// The spec of the data retention configurations
// +optional
RetentionConfig *RetentionConfig `json:"retentionConfig,omitempty"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,11 @@ spec:
description: Annotations is an unstructured key value map stored with a service account
type: object
type: object
customAlertmanagerHubURL:
description: CustomAlertmanagerHubURL overrides the alertmanager URL to send alerts from the spoke to the hub server. For the alertmanager that runs in the hub this setting has no effect.
maxLength: 2083
pattern: ^https?:\/\/
type: string
customObservabilityHubURL:
description: CustomObservabilityHubURL overrides the endpoint used by the metrics-collector to send metrics to the hub server. For the metrics-collector that runs in the hub this setting has no effect.
maxLength: 2083
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1761,6 +1761,13 @@ spec:
stored with a service account
type: object
type: object
customAlertmanagerHubURL:
description: CustomAlertmanagerHubURL overrides the alertmanager
URL to send alerts from the spoke to the hub server. For the
alertmanager that runs in the hub this setting has no effect.
maxLength: 2083
pattern: ^https?:\/\/
type: string
customObservabilityHubURL:
description: CustomObservabilityHubURL overrides the endpoint
used by the metrics-collector to send metrics to the hub server.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package placementrule

import (
"context"
"net/url"

"gopkg.in/yaml.v2"
Expand All @@ -27,15 +28,15 @@ func generateHubInfoSecret(client client.Client, obsNamespace string,

if ingressCtlCrdExists {
var err error
obsApiRouteHost, err = config.GetObsAPIHost(client, obsNamespace)
obsApiRouteHost, err = config.GetObsAPIHost(context.TODO(), client, obsNamespace)
if err != nil {
log.Error(err, "Failed to get the host for observatorium API route")
return nil, err
}

// if alerting is disabled, do not set alertmanagerEndpoint
if !config.IsAlertingDisabled() {
alertmanagerEndpoint, err = config.GetAlertmanagerEndpoint(client, obsNamespace)
alertmanagerEndpoint, err = config.GetAlertmanagerEndpoint(context.TODO(), client, obsNamespace)
if err != nil {
log.Error(err, "Failed to get alertmanager endpoint")
return nil, err
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ import (
"net"
"time"

operatorconfig "github.com/stolostron/multicluster-observability-operator/operators/pkg/config"
certificatesv1 "k8s.io/api/certificates/v1"

operatorconfig "github.com/stolostron/multicluster-observability-operator/operators/pkg/config"

"golang.org/x/exp/slices"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
Expand Down Expand Up @@ -460,7 +461,7 @@ func pemEncode(cert []byte, key []byte) (*bytes.Buffer, *bytes.Buffer) {
func getHosts(c client.Client, ingressCtlCrdExists bool) ([]string, error) {
hosts := []string{config.GetObsAPISvc(config.GetOperandName(config.Observatorium))}
if ingressCtlCrdExists {
url, err := config.GetObsAPIHost(c, config.GetDefaultNamespace())
url, err := config.GetObsAPIHost(context.TODO(), c, config.GetDefaultNamespace())
if err != nil {
log.Error(err, "Failed to get api route address")
return nil, err
Expand Down Expand Up @@ -515,7 +516,7 @@ func CreateUpdateMtlsCertSecretForHubCollector(c client.Client, updateMtlsCert b
log.Error(nil, "failed to sign CSR")
return errors.NewBadRequest("failed to sign CSR")
}
//Create a secret
// Create a secret
HubMtlsSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: operatorconfig.HubMetricsCollectorMtlsCert,
Expand Down
26 changes: 21 additions & 5 deletions operators/multiclusterobservability/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,9 +481,9 @@ func GetDefaultTenantName() string {
}

// GetObsAPIHost is used to get the URL for observartium api gateway.
func GetObsAPIHost(client client.Client, namespace string) (string, error) {
func GetObsAPIHost(ctx context.Context, client client.Client, namespace string) (string, error) {
mco := &observabilityv1beta2.MultiClusterObservability{}
err := client.Get(context.TODO(),
err := client.Get(ctx,
types.NamespacedName{
Name: GetMonitoringCRName(),
}, mco)
Expand Down Expand Up @@ -532,10 +532,26 @@ func GetMCONamespace() string {
}

// GetAlertmanagerEndpoint is used to get the URL for alertmanager.
func GetAlertmanagerEndpoint(client client.Client, namespace string) (string, error) {
found := &routev1.Route{}
func GetAlertmanagerEndpoint(ctx context.Context, client client.Client, namespace string) (string, error) {
mco := &observabilityv1beta2.MultiClusterObservability{}
err := client.Get(ctx,
types.NamespacedName{
Name: GetMonitoringCRName(),
}, mco)
if err != nil && !errors.IsNotFound(err) {
return "", err
}
advancedConfig := mco.Spec.AdvancedConfig
if advancedConfig != nil && advancedConfig.CustomAlertmanagerHubURL != "" {
err := advancedConfig.CustomAlertmanagerHubURL.Validate()
if err != nil {
return "", err
}
return string(advancedConfig.CustomAlertmanagerHubURL), nil
}

err := client.Get(context.TODO(), types.NamespacedName{Name: AlertmanagerRouteName, Namespace: namespace}, found)
found := &routev1.Route{}
err = client.Get(ctx, types.NamespacedName{Name: AlertmanagerRouteName, Namespace: namespace}, found)
if err != nil && errors.IsNotFound(err) {
// if the alertmanager router is not created yet, fallback to get host from the domain of ingresscontroller
domain, err := getDomainForIngressController(
Expand Down
59 changes: 55 additions & 4 deletions operators/multiclusterobservability/pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package config

import (
"context"
"fmt"
"os"
"reflect"
Expand Down Expand Up @@ -266,12 +267,12 @@ func TestGetObsAPIHost(t *testing.T) {
scheme.AddKnownTypes(mcov1beta2.GroupVersion, &mcov1beta2.MultiClusterObservability{})
client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(route).Build()

host, _ := GetObsAPIHost(client, "default")
host, _ := GetObsAPIHost(context.TODO(), client, "default")
if host == apiServerURL {
t.Errorf("Should not get route host in default namespace")
}

host, _ = GetObsAPIHost(client, "test")
host, _ = GetObsAPIHost(context.TODO(), client, "test")
if host != apiServerURL {
t.Errorf("Observatorium api (%v) is not the expected (%v)", host, apiServerURL)
}
Expand All @@ -288,18 +289,68 @@ func TestGetObsAPIHost(t *testing.T) {
},
}
client = fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(route, mco).Build()
host, _ = GetObsAPIHost(client, "test")
host, _ = GetObsAPIHost(context.TODO(), client, "test")
if host != customBaseURL {
t.Errorf("Observatorium api (%v) is not the expected (%v)", host, customBaseURL)
}

mco.Spec.AdvancedConfig.CustomObservabilityHubURL = "httpa://foob ar.c"
client = fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(route, mco).Build()
_, err := GetObsAPIHost(client, "test")
_, err := GetObsAPIHost(context.TODO(), client, "test")
if err == nil {
t.Errorf("expected error when parsing URL '%v', but got none", mco.Spec.AdvancedConfig.CustomObservabilityHubURL)
}
}

func TestGetAlertmanagerEndpoint(t *testing.T) {
routeURL := "http://route.example.com"
route := &routev1.Route{
ObjectMeta: metav1.ObjectMeta{
Name: AlertmanagerRouteName,
Namespace: "test",
},
Spec: routev1.RouteSpec{
Host: routeURL,
},
}
scheme := runtime.NewScheme()
scheme.AddKnownTypes(routev1.GroupVersion, route)
scheme.AddKnownTypes(mcov1beta2.GroupVersion, &mcov1beta2.MultiClusterObservability{})
client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(route).Build()

host, _ := GetAlertmanagerEndpoint(context.TODO(), client, "default")
if host == routeURL {
t.Errorf("Should not get route host in default namespace")
}

host, _ = GetAlertmanagerEndpoint(context.TODO(), client, "test")
if host != routeURL {
t.Errorf("Alertmanager URL (%v) is not the expected (%v)", host, routeURL)
}

customBaseURL := "https://custom.base/url"
mco := &mcov1beta2.MultiClusterObservability{
ObjectMeta: metav1.ObjectMeta{
Name: GetMonitoringCRName(),
},
Spec: mcov1beta2.MultiClusterObservabilitySpec{
AdvancedConfig: &mcov1beta2.AdvancedConfig{
CustomAlertmanagerHubURL: mcoshared.URL(customBaseURL),
},
},
}
client = fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(route, mco).Build()
host, _ = GetAlertmanagerEndpoint(context.TODO(), client, "test")
if host != customBaseURL {
t.Errorf("Alertmanager URL (%v) is not the expected (%v)", host, customBaseURL)
}

mco.Spec.AdvancedConfig.CustomAlertmanagerHubURL = "httpa://foob ar.c"
client = fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(route, mco).Build()
_, err := GetAlertmanagerEndpoint(context.TODO(), client, "test")
if err == nil {
t.Errorf("expected error when parsing URL '%v', but got none", mco.Spec.AdvancedConfig.CustomObservabilityHubURL)
}
}

func TestIsPaused(t *testing.T) {
Expand Down

0 comments on commit a9fbe5f

Please sign in to comment.