eef60a4302
Secret UUID-based webhook endpoint for CI image push notifications. Project/stage matching via glob patterns, auto-creation of unknown projects from image inspection. Fix JSON response injection.
85 lines
2.4 KiB
Go
85 lines
2.4 KiB
Go
package deployer
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
// maxSubdomainLen is the maximum length of a single DNS label (RFC 1035).
|
|
const maxSubdomainLen = 63
|
|
|
|
// invalidDNSChars matches characters not allowed in a DNS label.
|
|
var invalidDNSChars = regexp.MustCompile(`[^a-z0-9-]`)
|
|
|
|
// GenerateSubdomain builds a subdomain string from the given pattern and parameters.
|
|
// The pattern may contain {stage}, {project}, and {tag} placeholders.
|
|
// If the stage has a custom subdomain override, that value is used instead of the pattern.
|
|
func GenerateSubdomain(pattern, project, stage, tag, stageSubdomain string) string {
|
|
if stageSubdomain != "" {
|
|
return SanitizeDNSLabel(stageSubdomain)
|
|
}
|
|
|
|
result := pattern
|
|
result = strings.ReplaceAll(result, "{stage}", stage)
|
|
result = strings.ReplaceAll(result, "{project}", project)
|
|
result = strings.ReplaceAll(result, "{tag}", tag)
|
|
|
|
return SanitizeDNSLabel(result)
|
|
}
|
|
|
|
// GenerateTaggedSubdomain builds a subdomain that includes the tag for multi-instance support.
|
|
// It appends "-{sanitized_tag}" to the base subdomain.
|
|
func GenerateTaggedSubdomain(pattern, project, stage, tag, stageSubdomain string) string {
|
|
base := GenerateSubdomain(pattern, project, stage, "", stageSubdomain)
|
|
sanitizedTag := SanitizeDNSLabel(tag)
|
|
|
|
if sanitizedTag == "" {
|
|
return base
|
|
}
|
|
|
|
combined := base + "-" + sanitizedTag
|
|
return truncateDNSLabel(combined)
|
|
}
|
|
|
|
// SanitizeDNSLabel converts an arbitrary string into a valid DNS label.
|
|
// It lowercases, replaces dots and invalid characters with hyphens,
|
|
// collapses consecutive hyphens, trims leading/trailing hyphens, and truncates.
|
|
func SanitizeDNSLabel(s string) string {
|
|
s = strings.ToLower(s)
|
|
s = strings.ReplaceAll(s, ".", "-")
|
|
s = invalidDNSChars.ReplaceAllString(s, "-")
|
|
s = collapseHyphens(s)
|
|
s = strings.Trim(s, "-")
|
|
return truncateDNSLabel(s)
|
|
}
|
|
|
|
// collapseHyphens replaces consecutive hyphens with a single hyphen.
|
|
func collapseHyphens(s string) string {
|
|
prev := false
|
|
var b strings.Builder
|
|
b.Grow(len(s))
|
|
|
|
for _, r := range s {
|
|
if r == '-' {
|
|
if !prev {
|
|
b.WriteRune(r)
|
|
}
|
|
prev = true
|
|
} else {
|
|
b.WriteRune(r)
|
|
prev = false
|
|
}
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
// truncateDNSLabel truncates a label to maxSubdomainLen characters,
|
|
// ensuring it does not end with a hyphen after truncation.
|
|
func truncateDNSLabel(s string) string {
|
|
if len(s) <= maxSubdomainLen {
|
|
return s
|
|
}
|
|
s = s[:maxSubdomainLen]
|
|
return strings.TrimRight(s, "-")
|
|
}
|