mirror of
https://github.com/wweir/sower.git
synced 2026-10-19 16:14:33 +00:00
fix(config): keep the checked-in sowerd example in sync with the embedded one
config/sowerd.toml and ExampleSowerdConfigTOML are the same artifact kept in two places: the constant is what `sowerd -i` writes to /etc/sower/sowerd.toml (internal/install/service.go), the file is what `-c config/sowerd.toml` loads during development. They had drifted in both directions -- the constant carried the [site_routes.routes] path-prefix example and the older cert.domains wording, while the file carried the newer wording and left `domains` uncommented -- so a deployment got guidance contradicting the shipped binary depending on which copy it read. Take the constant as the source of truth, comment out the placeholder cert.domains entry (a placeholder domain is not one the operator owns) and pin the two copies byte for byte with a test that prints the differing lines.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -128,6 +129,42 @@ upstream = "https://backend.example.com"
|
||||
}
|
||||
}
|
||||
|
||||
// TestExampleSowerdConfigTOMLMatchesFile pins the embedded example to the
|
||||
// checked-in file byte for byte. They are the same artifact in two places:
|
||||
// the constant is what the installer writes to /etc/sower/sowerd.toml, and
|
||||
// the file is what `-c config/sowerd.toml` loads during development. Let
|
||||
// them drift and operators get guidance that contradicts shipped behavior.
|
||||
func TestExampleSowerdConfigTOMLMatchesFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
file := mustReadFileForTest(t, "sowerd.toml")
|
||||
if ExampleSowerdConfigTOML != file {
|
||||
t.Fatalf("ExampleSowerdConfigTOML and config/sowerd.toml have drifted:\n%s",
|
||||
exampleConfigDiff(ExampleSowerdConfigTOML, file))
|
||||
}
|
||||
}
|
||||
|
||||
func exampleConfigDiff(embedded, file string) string {
|
||||
embeddedLines := strings.Split(strings.TrimRight(embedded, "\n"), "\n")
|
||||
fileLines := strings.Split(strings.TrimRight(file, "\n"), "\n")
|
||||
|
||||
var b strings.Builder
|
||||
fmt.Fprintf(&b, " const has %d lines, file has %d lines\n", len(embeddedLines), len(fileLines))
|
||||
for i := 0; i < len(embeddedLines) || i < len(fileLines); i++ {
|
||||
var e, f string
|
||||
if i < len(embeddedLines) {
|
||||
e = embeddedLines[i]
|
||||
}
|
||||
if i < len(fileLines) {
|
||||
f = fileLines[i]
|
||||
}
|
||||
if e != f {
|
||||
fmt.Fprintf(&b, " line %d\n const: %q\n file: %q\n", i+1, e, f)
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func loadConfigFileForTest(path string, dst any) error {
|
||||
return aconfig.LoaderFor(dst, aconfig.Config{
|
||||
SkipEnv: true,
|
||||
|
||||
+3
-1
@@ -41,7 +41,9 @@ fake_site = "/var/www" # Fallback fake site directory or address (def
|
||||
# SSL/TLS certificate configuration
|
||||
[cert]
|
||||
email = "your-email@example.com" # Email for Let's Encrypt certificate
|
||||
# domains = ["proxy.example.com"] # Domains autocert may issue for, in addition to site_routes domains (required for direct connections like the sower client remote addr)
|
||||
# domains = ["proxy.example.com"] # Issuance whitelist beyond site_routes (autocert mode).
|
||||
# The FIRST entry answers TLS handshakes for unknown/empty SNI: keep it a concrete
|
||||
# domain, not a wildcard. See docs/security.md.
|
||||
cert = "" # Path to custom certificate file (optional)
|
||||
key = "" # Path to custom private key file (optional)
|
||||
`
|
||||
|
||||
+9
-4
@@ -9,15 +9,18 @@ serve_ip = "0.0.0.0" # IP address to listen on for ports 80 and 443
|
||||
password = "change_me" # Password for client authentication
|
||||
fake_site = "/var/www" # Fallback fake site directory or address (default: /var/www)
|
||||
|
||||
# Site routing configuration
|
||||
# When a TLS connection does not match sower transport, sowerd uses
|
||||
# the TLS SNI to select an upstream URL. Unmatched names fall back to fake_site.
|
||||
# Site routing: map domains to upstream URLs for fallback traffic.
|
||||
# When a TLS connection does not match sower transport,
|
||||
# the SNI is looked up here. Unmatched connections fall through to fake_site.
|
||||
# Custom certificate mode requires the certificate SANs to cover all routed domains.
|
||||
#
|
||||
# [[site_routes]]
|
||||
# domains = ["a.example.com", "b.example.com"]
|
||||
# upstream = "http://127.0.0.1:8080"
|
||||
#
|
||||
# [site_routes.routes]
|
||||
# "/ws" = "http://127.0.0.1:8082"
|
||||
#
|
||||
# [[site_routes]]
|
||||
# domains = ["c.example.com"]
|
||||
# upstream = "https://backend.example.com"
|
||||
@@ -25,6 +28,8 @@ fake_site = "/var/www" # Fallback fake site directory or address (def
|
||||
# SSL/TLS certificate configuration
|
||||
[cert]
|
||||
email = "your-email@example.com" # Email for Let's Encrypt certificate
|
||||
domains = ["proxy.example.com"] # Issuance whitelist beyond site_routes. The FIRST entry answers TLS handshakes for unknown/empty SNI (keep it a concrete domain, not a wildcard); see docs/security.md
|
||||
# domains = ["proxy.example.com"] # Issuance whitelist beyond site_routes (autocert mode).
|
||||
# The FIRST entry answers TLS handshakes for unknown/empty SNI: keep it a concrete
|
||||
# domain, not a wildcard. See docs/security.md.
|
||||
cert = "" # Path to custom certificate file (optional)
|
||||
key = "" # Path to custom private key file (optional)
|
||||
|
||||
Reference in New Issue
Block a user