Skip to content

Commit

Permalink
Merge pull request #14 from prometheus-operator/chore/adding-alertman…
Browse files Browse the repository at this point in the history
…ager-builder

[CHORE] adding alertmanager builder
  • Loading branch information
nicolastakashi committed Jul 1, 2024
2 parents 5abddde + 8d00f7d commit 1bf11ab
Show file tree
Hide file tree
Showing 4 changed files with 346 additions and 2 deletions.
139 changes: 139 additions & 0 deletions cmd/servicemonitor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Copyright 2024 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"context"
"errors"
"fmt"

"github.com/prometheus-operator/poctl/internal/k8sutil"
"github.com/prometheus-operator/poctl/internal/log"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/client/applyconfiguration/monitoring/v1"
monitoringclient "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
applyConfigMetav1 "k8s.io/client-go/applyconfigurations/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/utils/ptr"
)

// servicemonitorCmd represents the servicemonitor command

Check failure on line 33 in cmd/servicemonitor.go

View workflow job for this annotation

GitHub Actions / Golang linter

Comment should end in a period (godot)
var (
serviceName string
namespace string
port string
servicemonitorCmd = &cobra.Command{
Use: "servicemonitor",
Short: "Create a service monitor object",
Long: `Create a service monitor object based on user input parameters or taking as source of truth a kubernetes service`,
RunE: runServiceMonitor,
}
)

func runServiceMonitor(_ *cobra.Command, args []string) error {

Check failure on line 46 in cmd/servicemonitor.go

View workflow job for this annotation

GitHub Actions / Golang linter

unused-parameter: parameter 'args' seems to be unused, consider removing or renaming it as _ (revive)
logger, err := log.NewLogger()
if err != nil {
fmt.Println(err)
return err
}

//TODO(nicolastakashi): Replace it when the PR #6623 is merged
restConfig, err := k8sutil.GetRestConfig(logger, kubeconfig)
if err != nil {
logger.With("error", err.Error()).Error("error while getting kubeconfig")
return err
}

kclient, err := kubernetes.NewForConfig(restConfig)
if err != nil {
logger.With("error", err.Error()).Error("error while creating k8s client")
return err
}

mclient, err := monitoringclient.NewForConfig(restConfig)
if err != nil {
logger.With("error", err.Error()).Error("error while creating Prometheus Operator client")
return err
}

if serviceName == "" {
return errors.New("service name is required")
}

err = createFromService(context.Background(), kclient, mclient, namespace, serviceName, port)
if err != nil {
logger.With("error", err.Error()).Error("error while creating service monitor")
return err
}

return nil
}

func createFromService(
ctx context.Context,
k8sClient *kubernetes.Clientset,
mClient *monitoringclient.Clientset,
namespace string,
serviceName string,
port string) error {

service, err := k8sClient.CoreV1().Services(namespace).Get(ctx, serviceName, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("error while getting service %s: %v", serviceName, err)
}

svcMonitor := &monitoringv1.ServiceMonitorApplyConfiguration{
TypeMetaApplyConfiguration: applyConfigMetav1.TypeMetaApplyConfiguration{
Kind: ptr.To("ServiceMonitor"),
APIVersion: ptr.To("monitoring.coreos.com/v1"),
},
ObjectMetaApplyConfiguration: &applyConfigMetav1.ObjectMetaApplyConfiguration{
Name: ptr.To(serviceName),
Namespace: ptr.To(namespace),
Labels: service.Labels,
},
Spec: &monitoringv1.ServiceMonitorSpecApplyConfiguration{
Selector: &metav1.LabelSelector{
MatchLabels: service.Spec.Selector,
},
},
}

for _, p := range service.Spec.Ports {
if port != "" && p.Name != port {
continue
}

svcMonitor.Spec.Endpoints = append(svcMonitor.Spec.Endpoints, monitoringv1.EndpointApplyConfiguration{
HonorLabels: ptr.To(true),
Port: ptr.To(p.Name),
})
}

_, err = mClient.MonitoringV1().ServiceMonitors(namespace).Apply(ctx, svcMonitor, k8sutil.ApplyOption)
if err != nil {
return fmt.Errorf("error while creating service monitor %s: %v", serviceName, err)
}

return nil
}

func init() {
createCmd.AddCommand(servicemonitorCmd)
servicemonitorCmd.Flags().StringVarP(&serviceName, "service", "s", "", "Service name to create the service monitor from")
servicemonitorCmd.Flags().StringVarP(&namespace, "namespace", "n", "default", "Namespace of the service")
servicemonitorCmd.Flags().StringVarP(&port, "port", "p", "", "Port of the service")
}
50 changes: 48 additions & 2 deletions cmd/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
Expand All @@ -39,7 +40,7 @@ var (
Use: "stack",
Short: "create a stack of Prometheus Operator resources.",
Long: `create a stack of Prometheus Operator resources.`,
Run: run,
Run: runStack,
}

crds = []string{
Expand Down Expand Up @@ -70,7 +71,7 @@ func init() {
// stackCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

func run(cmd *cobra.Command, _ []string) {
func runStack(cmd *cobra.Command, _ []string) {
logger, err := log.NewLogger()
if err != nil {
fmt.Println(err)
Expand Down Expand Up @@ -127,6 +128,11 @@ func run(cmd *cobra.Command, _ []string) {
os.Exit(1)
}

if err := createAlertManager(cmd.Context(), logger, kclient, mclient, metav1.NamespaceDefault); err != nil {
logger.With("error", err.Error()).Error("error while creating AlertManager")
os.Exit(1)
}

logger.Info("Prometheus Operator stack created successfully.")
}

Expand Down Expand Up @@ -288,3 +294,43 @@ func createPrometheus(

return nil
}

func createAlertManager(
ctx context.Context,
logger *slog.Logger,
k8sClient *kubernetes.Clientset,
poClient *monitoringclient.Clientset,
namespace string) error {
manifests := builder.NewAlertManager(namespace).
WithServiceAccount().
WithAlertManager().
WithService().
WithServiceMonitor().
Build()

_, err := k8sClient.CoreV1().ServiceAccounts(namespace).Apply(ctx, manifests.ServiceAccount, k8sutil.ApplyOption)
if err != nil {
logger.ErrorContext(ctx, "error while creating ServiceAccount", "error", err.Error())
return err
}

_, err = poClient.MonitoringV1().Alertmanagers(namespace).Apply(ctx, manifests.AlertManager, k8sutil.ApplyOption)
if err != nil {
logger.ErrorContext(ctx, "error while creating AlertManager", "error", err.Error())
return err
}

_, err = k8sClient.CoreV1().Services(namespace).Apply(ctx, manifests.Service, k8sutil.ApplyOption)
if err != nil {
logger.ErrorContext(ctx, "error while creating Service", "error", err.Error())
return err
}

_, err = poClient.MonitoringV1().ServiceMonitors(namespace).Apply(ctx, manifests.ServiceMonitor, k8sutil.ApplyOption)
if err != nil {
logger.ErrorContext(ctx, "error while creating ServiceMonitor", "error", err.Error())
return err
}

return nil
}
150 changes: 150 additions & 0 deletions internal/builder/alertmanager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Copyright 2024 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package builder

import (
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/client/applyconfiguration/monitoring/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
applyConfigCorev1 "k8s.io/client-go/applyconfigurations/core/v1"
applyConfigMetav1 "k8s.io/client-go/applyconfigurations/meta/v1"
"k8s.io/utils/ptr"
)

type AlertManagerBuilder struct {
labels map[string]string
labelSelectors map[string]string
namespace string
manifets AlertManagerManifests
}

type AlertManagerManifests struct {
ServiceAccount *applyConfigCorev1.ServiceAccountApplyConfiguration
AlertManager *monitoringv1.AlertmanagerApplyConfiguration
Service *applyConfigCorev1.ServiceApplyConfiguration
ServiceMonitor *monitoringv1.ServiceMonitorApplyConfiguration
}

const AlertManagerName = "alertmanager"

func NewAlertManager(namespace string) *AlertManagerBuilder {
return &AlertManagerBuilder{
labels: map[string]string{
"alertmanager": AlertManagerName,
},
labelSelectors: map[string]string{
"alertmanager": AlertManagerName,
},
namespace: namespace,
}
}

func (a *AlertManagerBuilder) WithServiceAccount() *AlertManagerBuilder {
a.manifets.ServiceAccount = &applyConfigCorev1.ServiceAccountApplyConfiguration{
TypeMetaApplyConfiguration: applyConfigMetav1.TypeMetaApplyConfiguration{
Kind: ptr.To("ServiceAccount"),
APIVersion: ptr.To("v1"),
},
ObjectMetaApplyConfiguration: &applyConfigMetav1.ObjectMetaApplyConfiguration{
Name: ptr.To(AlertManagerName),
Labels: a.labels,
Namespace: ptr.To(a.namespace),
},
}
return a
}

func (a *AlertManagerBuilder) WithAlertManager() *AlertManagerBuilder {
a.manifets.AlertManager = &monitoringv1.AlertmanagerApplyConfiguration{
TypeMetaApplyConfiguration: applyConfigMetav1.TypeMetaApplyConfiguration{
Kind: ptr.To("Alertmanager"),
APIVersion: ptr.To("monitoring.coreos.com/v1"),
},
ObjectMetaApplyConfiguration: &applyConfigMetav1.ObjectMetaApplyConfiguration{
Name: ptr.To(AlertManagerName),
Labels: a.labels,
Namespace: ptr.To(a.namespace),
},
Spec: &monitoringv1.AlertmanagerSpecApplyConfiguration{
ServiceAccountName: a.manifets.ServiceAccount.Name,
Replicas: ptr.To(int32(1)),
AlertmanagerConfigSelector: &metav1.LabelSelector{},
AlertmanagerConfigNamespaceSelector: &metav1.LabelSelector{},
},
}
return a
}

func (a *AlertManagerBuilder) WithService() *AlertManagerBuilder {
a.manifets.Service = &applyConfigCorev1.ServiceApplyConfiguration{
TypeMetaApplyConfiguration: applyConfigMetav1.TypeMetaApplyConfiguration{
Kind: ptr.To("Service"),
APIVersion: ptr.To("v1"),
},
ObjectMetaApplyConfiguration: &applyConfigMetav1.ObjectMetaApplyConfiguration{
Name: ptr.To(AlertManagerName),
Labels: a.labels,
Namespace: ptr.To(a.namespace),
},
Spec: &applyConfigCorev1.ServiceSpecApplyConfiguration{
Ports: []applyConfigCorev1.ServicePortApplyConfiguration{
{
Name: ptr.To("http-web"),
Port: ptr.To(int32(9093)),
TargetPort: ptr.To(intstr.FromInt32(9093)),
Protocol: ptr.To(corev1.ProtocolTCP),
},
{
Name: ptr.To("reloader-web"),
Port: ptr.To(int32(8080)),
TargetPort: ptr.To(intstr.FromString("reloader-web")),
},
},
Selector: a.labelSelectors,
},
}
return a
}

func (a *AlertManagerBuilder) WithServiceMonitor() *AlertManagerBuilder {
a.manifets.ServiceMonitor = &monitoringv1.ServiceMonitorApplyConfiguration{
TypeMetaApplyConfiguration: applyConfigMetav1.TypeMetaApplyConfiguration{
Kind: ptr.To("ServiceMonitor"),
APIVersion: ptr.To("monitoring.coreos.com/v1"),
},
ObjectMetaApplyConfiguration: &applyConfigMetav1.ObjectMetaApplyConfiguration{
Name: ptr.To(AlertManagerName),
Labels: a.labels,
Namespace: ptr.To(a.namespace),
},
Spec: &monitoringv1.ServiceMonitorSpecApplyConfiguration{
Selector: &metav1.LabelSelector{
MatchLabels: a.labelSelectors,
},
Endpoints: []monitoringv1.EndpointApplyConfiguration{
{
HonorLabels: ptr.To(true),
Port: ptr.To("http-web"),
},
},
},
}
return a
}

func (a *AlertManagerBuilder) Build() AlertManagerManifests {
return a.manifets
}
9 changes: 9 additions & 0 deletions internal/builder/prometheus.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,15 @@ func (p *PrometheusBuilder) WithPrometheus() *PrometheusBuilder {
},
RuleSelector: &metav1.LabelSelector{},
RuleNamespaceSelector: &metav1.LabelSelector{},
Alerting: &monitoringv1.AlertingSpecApplyConfiguration{
Alertmanagers: []monitoringv1.AlertmanagerEndpointsApplyConfiguration{
{
Namespace: ptr.To(p.namespace),
Name: ptr.To(AlertManagerName),
Port: ptr.To(intstr.FromString("http-web")),
},
},
},
},
}
return p
Expand Down

0 comments on commit 1bf11ab

Please sign in to comment.