Skip to content

Commit

Permalink
feat: moved operatorMode and monitorType to project.yaml
Browse files Browse the repository at this point in the history
  • Loading branch information
ffforest committed Aug 30, 2023
1 parent 8c26fc5 commit d0e9a87
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 43 deletions.
41 changes: 26 additions & 15 deletions pkg/generator/appconfiguration/generator/monitoring_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package generator
import (
"fmt"

prometheusV1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
prometheusv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"kusionstack.io/kusion/pkg/generator/appconfiguration"
"kusionstack.io/kusion/pkg/models"
Expand Down Expand Up @@ -43,27 +43,38 @@ func (g *monitoringGenerator) Generate(spec *models.Spec) error {
spec.Resources = make(models.Resources, 0)
}

// If Prometheus runs as an operator, it relies on Custom Resources to
// manage the scrape configs. CRs (ServiceMonitors and PodMonitors) rely on
// corresponding resources (Services and Pods) to have labels that can be
// used as part of the label selector for the CR to determine which
// service/pods to scrape from.
// Here we choose the label name kusion_monitoring_appname for two reasons:
// 1. Unlike the label validation in Kubernetes, the label name accepted by
// Prometheus cannot contain non-alphanumeric characters except underscore:
// https://github.com/prometheus/common/blob/main/model/labels.go#L94
// 2. The name should be unique enough that is only created by Kusion and
// used to identify a certain application
monitoringLabels := map[string]string{
"kusion_monitoring_appname": g.appName,
}

if g.monitor != nil && g.monitor.OperatorMode {
if g.monitor.MonitorType == "service" {
serviceEndpoint := prometheusV1.Endpoint{
if g.project.ProjectConfiguration.Prometheus != nil && g.project.ProjectConfiguration.Prometheus.OperatorMode && g.monitor != nil {
if g.project.ProjectConfiguration.Prometheus.MonitorType == projectstack.ServiceMonitorType {
serviceEndpoint := prometheusv1.Endpoint{
Interval: g.monitor.Interval,
ScrapeTimeout: g.monitor.Timeout,
Port: g.monitor.Port,
Path: g.monitor.Path,
Scheme: g.monitor.Scheme,
}
serviceEndpointList := []prometheusV1.Endpoint{serviceEndpoint}
serviceMonitor := &prometheusV1.ServiceMonitor{
serviceEndpointList := []prometheusv1.Endpoint{serviceEndpoint}
serviceMonitor := &prometheusv1.ServiceMonitor{
TypeMeta: metav1.TypeMeta{
Kind: "ServiceMonitor",
APIVersion: prometheusV1.SchemeGroupVersion.String(),
APIVersion: prometheusv1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-service-monitor", g.appName), Namespace: g.project.Name},
Spec: prometheusV1.ServiceMonitorSpec{
Spec: prometheusv1.ServiceMonitorSpec{
Selector: metav1.LabelSelector{
MatchLabels: monitoringLabels,
},
Expand All @@ -79,23 +90,23 @@ func (g *monitoringGenerator) Generate(spec *models.Spec) error {
if err != nil {
return err
}
} else if g.monitor != nil && g.monitor.MonitorType == "pod" {
podMetricsEndpoint := prometheusV1.PodMetricsEndpoint{
} else if g.project.ProjectConfiguration.Prometheus.MonitorType == projectstack.PodMonitorType {
podMetricsEndpoint := prometheusv1.PodMetricsEndpoint{
Interval: g.monitor.Interval,
ScrapeTimeout: g.monitor.Timeout,
Port: g.monitor.Port,
Path: g.monitor.Path,
Scheme: g.monitor.Scheme,
}
podMetricsEndpointList := []prometheusV1.PodMetricsEndpoint{podMetricsEndpoint}
podMetricsEndpointList := []prometheusv1.PodMetricsEndpoint{podMetricsEndpoint}

podMonitor := &prometheusV1.PodMonitor{
podMonitor := &prometheusv1.PodMonitor{
TypeMeta: metav1.TypeMeta{
Kind: "PodMonitor",
APIVersion: prometheusV1.SchemeGroupVersion.String(),
APIVersion: prometheusv1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-pod-monitor", g.appName), Namespace: g.project.Name},
Spec: prometheusV1.PodMonitorSpec{
Spec: prometheusv1.PodMonitorSpec{
Selector: metav1.LabelSelector{
MatchLabels: monitoringLabels,
},
Expand All @@ -113,7 +124,7 @@ func (g *monitoringGenerator) Generate(spec *models.Spec) error {
return err
}
} else {
return fmt.Errorf("MonitorType should either be service or pod %s", g.monitor.MonitorType)
return fmt.Errorf("MonitorType should either be service or pod %s", g.project.ProjectConfiguration.Prometheus.MonitorType)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package generator

import (
"fmt"
"strings"
"testing"

Prometheusv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
Expand Down Expand Up @@ -33,29 +34,31 @@ type TestCase struct {
func BuildMonitoringTestCase(
projectName, appName string,
interval, timeout Prometheusv1.Duration,
path, port, scheme, monitorType string,
path, port, scheme string,
monitorType projectstack.MonitorType,
operatorMode bool,
) *TestCase {
var monitorKind, endpointType string
if monitorType == "service" {
var endpointType string
var monitorKind projectstack.MonitorType
if monitorType == "Service" {
monitorKind = "ServiceMonitor"
endpointType = "endpoints"
} else if monitorType == "pod" {
} else if monitorType == "Pod" {
monitorKind = "PodMonitor"
endpointType = "podMetricsEndpoints"
}
expectedResources := make([]models.Resource, 0)
if operatorMode {
expectedResources = []models.Resource{
{
ID: fmt.Sprintf("monitoring.coreos.com/v1:%s:%s:%s-%s-monitor", monitorKind, projectName, appName, monitorType),
ID: fmt.Sprintf("monitoring.coreos.com/v1:%s:%s:%s-%s-monitor", monitorKind, projectName, appName, strings.ToLower(string(monitorType))),
Type: "Kubernetes",
Attributes: map[string]interface{}{
"apiVersion": "monitoring.coreos.com/v1",
"kind": monitorKind,
"kind": string(monitorKind),
"metadata": map[string]interface{}{
"creationTimestamp": nil,
"name": fmt.Sprintf("%s-%s-monitor", appName, monitorType),
"name": fmt.Sprintf("%s-%s-monitor", appName, strings.ToLower(string(monitorType))),
"namespace": projectName,
},
"spec": map[string]interface{}{
Expand Down Expand Up @@ -90,17 +93,19 @@ func BuildMonitoringTestCase(
project: &projectstack.Project{
ProjectConfiguration: projectstack.ProjectConfiguration{
Name: projectName,
Prometheus: &projectstack.PrometheusConfig{
OperatorMode: operatorMode,
MonitorType: monitorType,
},
},
Path: "/test-project",
},
monitor: &monitoring.Monitor{
Interval: interval,
Timeout: timeout,
Path: path,
Port: port,
Scheme: scheme,
OperatorMode: operatorMode,
MonitorType: monitorType,
Interval: interval,
Timeout: timeout,
Path: path,
Port: port,
Scheme: scheme,
},
appName: appName,
},
Expand All @@ -115,12 +120,12 @@ func BuildMonitoringTestCase(
return testCase
}

func Test_monitoringGenerator_Generate(t *testing.T) {
func TestMonitoringGenerator_Generate(t *testing.T) {
tests := []TestCase{
*BuildMonitoringTestCase("test-project", "test-app", "15s", "5s", "/metrics", "web", "http", "service", true),
*BuildMonitoringTestCase("test-project", "test-app", "15s", "5s", "/metrics", "web", "http", "pod", true),
*BuildMonitoringTestCase("test-project", "test-app", "30s", "15s", "/metrics", "8080", "http", "service", false),
*BuildMonitoringTestCase("test-project", "test-app", "30s", "15s", "/metrics", "8080", "http", "pod", false),
*BuildMonitoringTestCase("test-project", "test-app", "15s", "5s", "/metrics", "web", "http", "Service", true),
*BuildMonitoringTestCase("test-project", "test-app", "15s", "5s", "/metrics", "web", "http", "Pod", true),
*BuildMonitoringTestCase("test-project", "test-app", "30s", "15s", "/metrics", "8080", "http", "Service", false),
*BuildMonitoringTestCase("test-project", "test-app", "30s", "15s", "/metrics", "8080", "http", "Pod", false),
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ func TestWorkloadGenerator_Generate(t *testing.T) {
expectedProject := &projectstack.Project{
ProjectConfiguration: projectstack.ProjectConfiguration{
Name: "test",
Prometheus: &projectstack.PrometheusConfig{
OperatorMode: false,
MonitorType: "Pod",
},
},
}
expectedStack := &projectstack.Stack{}
Expand Down
17 changes: 9 additions & 8 deletions pkg/models/appconfiguration/monitoring/monitoring.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package monitoring

import (
prometheusV1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
prometheusv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
)

type Monitor struct {
Interval prometheusV1.Duration `yaml:"interval,omitempty" json:"interval,omitempty"`
Timeout prometheusV1.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"`
Path string `yaml:"path,omitempty" json:"path,omitempty"`
Port string `yaml:"port,omitempty" json:"port,omitempty"`
Scheme string `yaml:"scheme,omitempty" json:"scheme,omitempty"`
OperatorMode bool `yaml:"operatorMode,omitempty" json:"operatorMode,omitempty"`
MonitorType string `yaml:"monitorType,omitempty" json:"monitorType,omitempty"`
Interval prometheusv1.Duration `yaml:"interval,omitempty" json:"interval,omitempty"`
Timeout prometheusv1.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"`
Path string `yaml:"path,omitempty" json:"path,omitempty"`
// Despite what the name suggests, PodMonitor and ServiceMonitor actually
// only accept port names as the input. So in operator mode, this port field
// need to be the user-provided port name.
Port string `yaml:"port,omitempty" json:"port,omitempty"`
Scheme string `yaml:"scheme,omitempty" json:"scheme,omitempty"`
}
16 changes: 15 additions & 1 deletion pkg/projectstack/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,27 @@ const (
KclFile = "kcl.yaml"
KCLGenerator GeneratorType = "KCL"
AppConfigurationGenerator GeneratorType = "AppConfiguration"
PodMonitorType MonitorType = "Pod"
ServiceMonitorType MonitorType = "Service"
)

type GeneratorType string
type (
GeneratorType string
MonitorType string
)

// GeneratorConfig represent Generator configs saved in project.yaml
type GeneratorConfig struct {
Type GeneratorType `json:"type"`
Configs map[string]interface{} `json:"configs,omitempty"`
}

// PrometheusConfig represent Prometheus configs saved in project.yaml
type PrometheusConfig struct {
OperatorMode bool `yaml:"operatorMode,omitempty" json:"operatorMode,omitempty"`
MonitorType MonitorType `yaml:"monitorType,omitempty" json:"monitorType,omitempty"`
}

// ProjectConfiguration is the project configuration
type ProjectConfiguration struct {
// Project name
Expand All @@ -51,6 +62,9 @@ type ProjectConfiguration struct {
// SpecGenerator configs
Generator *GeneratorConfig `json:"generator,omitempty" yaml:"generator,omitempty"`

// Prometheus configs
Prometheus *PrometheusConfig `json:"prometheus,omitempty" yaml:"prometheus,omitempty"`

// Secret stores
SecretStores *vals.SecretStores `json:"secret_stores,omitempty" yaml:"secret_stores,omitempty"`
}
Expand Down

0 comments on commit d0e9a87

Please sign in to comment.