pkg/prom/connection.go
Rama main · réplica de lectura del repositorio original.
package prom
import (
"errors"
"fmt"
"maps"
"net/url"
"os"
"strings"
"github.com/skyhook-io/radar/pkg/urlutil"
)
type Connection struct {
URL string `json:"url"`
Headers map[string]string `json:"headers,omitempty"`
HeadersFromEnv map[string]string `json:"headersFromEnv,omitempty"`
}
func ValidateBaseURL(raw string) error {
if err := ValidateHTTPBaseURL(raw); err != nil {
return fmt.Errorf("Prometheus URL %w", err)
}
return nil
}
func ValidateHTTPBaseURL(raw string) error {
if raw == "" {
return nil
}
u, err := url.Parse(raw)
if err != nil || (u.Scheme != "http" && u.Scheme != "https") || u.Hostname() == "" || u.User != nil || u.RawQuery != "" || u.ForceQuery || u.Fragment != "" {
return errors.New("must be an HTTP(S) base URL without credentials, query parameters or fragments")
}
if _, valid := urlutil.NormalizeOrigin(raw); !valid {
return errors.New("has an invalid port")
}
return nil
}
func (c Connection) Validate() error {
if err := ValidateBaseURL(c.URL); err != nil {
return err
}
return c.ValidateHeaderSources()
}
// ValidateHeaderSources checks the headers and environment references without
// judging the URL, for callers that apply their own URL rule.
func (c Connection) ValidateHeaderSources() error {
if c.URL == "" && (len(c.Headers) > 0 || len(c.HeadersFromEnv) > 0) {
return ErrHeadersRequireURL
}
all := make(map[string]string)
for k, v := range c.Headers {
all[k] = v
}
for k, v := range c.HeadersFromEnv {
if !ValidEnvVarName(v) {
return errors.New("Prometheus header references an invalid environment variable name")
}
for existing := range all {
if strings.EqualFold(existing, k) {
return errors.New("Prometheus header is configured from multiple sources")
}
}
all[k] = ""
}
return ValidateHeaders(all)
}
func ValidEnvVarName(s string) bool {
if s == "" {
return false
}
for i, r := range s {
if r == '_' || r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' {
continue
}
if i > 0 && r >= '0' && r <= '9' {
continue
}
return false
}
return true
}
func ResolveHeaders(headers, refs map[string]string) (map[string]string, error) {
if err := ValidateHeaders(headers); err != nil {
return nil, err
}
out := maps.Clone(headers)
if out == nil {
out = map[string]string{}
}
for key, name := range refs {
key = strings.TrimSpace(key)
name = strings.TrimSpace(name)
if !ValidEnvVarName(name) {
return nil, fmt.Errorf("invalid env var name for prometheus header %q", key)
}
for existing := range out {
if strings.EqualFold(existing, key) {
return nil, fmt.Errorf("prometheus header %q is configured from multiple sources", key)
}
}
value, ok := os.LookupEnv(name)
if !ok {
return nil, fmt.Errorf("prometheus header %q references unset env var %q", key, name)
}
out[key] = value
}
if err := ValidateHeaders(out); err != nil {
return nil, err
}
if len(out) == 0 {
return nil, nil
}
return out, nil
}