mirror of
https://github.com/portainer/portainer.git
synced 2026-06-23 06:20:12 +00:00
153 lines
3.8 KiB
Go
153 lines
3.8 KiB
Go
package libprometheus
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
const configTemplate = `# Prometheus configuration.
|
|
# Generated automatically — do not edit.
|
|
global:
|
|
scrape_interval: %s
|
|
evaluation_interval: %s
|
|
|
|
scrape_configs:
|
|
- job_name: '%s'
|
|
scheme: '%s'
|
|
metrics_path: '%s'
|
|
%s static_configs:
|
|
- targets: ['%s']
|
|
|
|
%s
|
|
`
|
|
|
|
const alertingTemplate = `alerting:
|
|
alertmanagers:
|
|
- scheme: '%s'
|
|
path_prefix: '%s'
|
|
api_version: 'v2'
|
|
static_configs:
|
|
- targets: ['%s']
|
|
%s
|
|
`
|
|
|
|
type PrometheusConfigOptions struct {
|
|
ScrapeInterval string
|
|
JobName string
|
|
ScrapeTarget string
|
|
AlertmanagerTarget string
|
|
AlertmanagerHeaders map[string]string
|
|
InsecureSkipVerify bool
|
|
}
|
|
|
|
// WritePrometheusConfig writes a Prometheus config.yaml to dataDir/config.yaml.
|
|
// ScrapeTarget is a full scrape URL (e.g. "http://localhost:9001/api/metrics").
|
|
// AlertmanagerTarget is an optional base URL used by Prometheus notifier
|
|
// (e.g. "https://portainer.example/api/endpoints/1/edge/alerts").
|
|
func WritePrometheusConfig(dataDir string, options PrometheusConfigOptions) error {
|
|
if err := os.MkdirAll(dataDir, 0o750); err != nil {
|
|
return fmt.Errorf("create prometheus config dir: %w", err)
|
|
}
|
|
|
|
scheme, metricsPath, hostPort, err := parseTarget(options.ScrapeTarget, "/metrics")
|
|
if err != nil {
|
|
return fmt.Errorf("parse scrape target: %w", err)
|
|
}
|
|
|
|
escape := func(s string) string { return strings.ReplaceAll(s, "'", "''") }
|
|
alertingSection, err := renderAlertingSection(options.AlertmanagerTarget, options.AlertmanagerHeaders, options.InsecureSkipVerify, escape)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
scrapeTLSBlock := ""
|
|
if scheme == "https" && options.InsecureSkipVerify {
|
|
scrapeTLSBlock = " tls_config:\n insecure_skip_verify: true\n"
|
|
}
|
|
|
|
content := fmt.Sprintf(configTemplate,
|
|
options.ScrapeInterval, options.ScrapeInterval,
|
|
escape(options.JobName),
|
|
escape(scheme),
|
|
escape(metricsPath),
|
|
scrapeTLSBlock,
|
|
escape(hostPort),
|
|
alertingSection,
|
|
)
|
|
|
|
return os.WriteFile(filepath.Join(dataDir, "config.yaml"), []byte(content), 0o600)
|
|
}
|
|
|
|
// parseTarget splits a full URL into its scheme, path, and host:port components
|
|
// for use in a Prometheus static_configs entry.
|
|
func parseTarget(target string, defaultPath string) (scheme, parsedPath, hostPort string, err error) {
|
|
u, err := url.Parse(target)
|
|
if err != nil {
|
|
return "", "", "", err
|
|
}
|
|
|
|
scheme = u.Scheme
|
|
if scheme == "" {
|
|
scheme = "http"
|
|
}
|
|
|
|
parsedPath = u.Path
|
|
if parsedPath == "" {
|
|
parsedPath = defaultPath
|
|
}
|
|
|
|
hostPort = u.Host
|
|
if hostPort == "" {
|
|
hostPort = target // fallback: treat the whole string as host:port
|
|
}
|
|
|
|
return scheme, parsedPath, hostPort, nil
|
|
}
|
|
func renderAlertingSection(target string, headers map[string]string, insecureSkipVerify bool, escape func(string) string) (string, error) {
|
|
if target == "" {
|
|
return "", nil
|
|
}
|
|
|
|
scheme, pathPrefix, hostPort, err := parseTarget(target, "")
|
|
if err != nil {
|
|
return "", fmt.Errorf("parse alertmanager target: %w", err)
|
|
}
|
|
|
|
tlsBlock := ""
|
|
if insecureSkipVerify {
|
|
tlsBlock = " tls_config:\n insecure_skip_verify: true\n"
|
|
}
|
|
headersBlock := renderHTTPHeaders(headers, escape, " ")
|
|
return fmt.Sprintf(alertingTemplate,
|
|
escape(scheme),
|
|
escape(pathPrefix),
|
|
escape(hostPort),
|
|
tlsBlock+headersBlock,
|
|
), nil
|
|
}
|
|
|
|
func renderHTTPHeaders(headers map[string]string, escape func(string) string, indent string) string {
|
|
if len(headers) == 0 {
|
|
return ""
|
|
}
|
|
|
|
keys := make([]string, 0, len(headers))
|
|
for name := range headers {
|
|
keys = append(keys, name)
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
var builder strings.Builder
|
|
_, _ = fmt.Fprintf(&builder, "%shttp_headers:\n", indent)
|
|
for _, name := range keys {
|
|
_, _ = fmt.Fprintf(&builder, "%s %s:\n", indent, name)
|
|
_, _ = fmt.Fprintf(&builder, "%s values: ['%s']\n", indent, escape(headers[name]))
|
|
}
|
|
|
|
return builder.String()
|
|
}
|