software steht so weit.
jetzt build + docker build und testen
This commit is contained in:
1 parent
42f0c0accb
commit
ca6cdcca63
90 files changed
+10298
-480
No files matched your search
@@ -0,0 +1,236 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"sultan/pkg/traefik"
|
||||
)
|
||||
|
||||
func parse(t *testing.T, yaml string) []ServiceSpec {
|
||||
t.Helper()
|
||||
specs, err := ParseServices([]byte(yaml))
|
||||
if err != nil {
|
||||
t.Fatalf("ParseServices: %v", err)
|
||||
}
|
||||
return specs
|
||||
}
|
||||
|
||||
func TestFileWorkloads_Minimal(t *testing.T) {
|
||||
specs := parse(t, `
|
||||
services:
|
||||
- name: shop
|
||||
url: http://127.0.0.1:8080
|
||||
hosts: [shop.kunde.de]
|
||||
`)
|
||||
ws := FileWorkloads(specs, "100.64.0.42")
|
||||
want := map[string]string{
|
||||
"traefik.enable": "true",
|
||||
"traefik.http.routers.shop.rule": "Host(`shop.kunde.de`)",
|
||||
"traefik.http.routers.shop.entrypoints": "web",
|
||||
"traefik.http.routers.shop.service": "shop",
|
||||
"traefik.http.services.shop.loadbalancer.server.port": "8080",
|
||||
}
|
||||
if len(ws) != 1 || ws[0].Name != "sultan.d/shop" || ws[0].Host != "100.64.0.42" || !reflect.DeepEqual(ws[0].Labels, want) {
|
||||
t.Errorf("Workloads = %+v", ws)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileWorkloads_ComfortSwitches(t *testing.T) {
|
||||
specs := parse(t, `
|
||||
services:
|
||||
- name: kunde-a
|
||||
url: https://127.0.0.1:8443/
|
||||
hosts: [Kunde-A.de]
|
||||
https: true
|
||||
certresolver: dns-resolver
|
||||
redirect_http_to_https: true
|
||||
redirect_www: to_non_www
|
||||
labels:
|
||||
traefik.http.middlewares.kunde-a-www.redirectregex.permanent: "false"
|
||||
`)
|
||||
rule := "Host(`kunde-a.de`) || Host(`www.kunde-a.de`)"
|
||||
want := map[string]string{
|
||||
"traefik.enable": "true",
|
||||
"traefik.http.routers.kunde-a.rule": rule,
|
||||
"traefik.http.routers.kunde-a.entrypoints": "websecure",
|
||||
"traefik.http.routers.kunde-a.service": "kunde-a",
|
||||
"traefik.http.routers.kunde-a.tls": "true",
|
||||
"traefik.http.routers.kunde-a.tls.certresolver": "dns-resolver",
|
||||
"traefik.http.routers.kunde-a.middlewares": "kunde-a-www",
|
||||
"traefik.http.services.kunde-a.loadbalancer.server.port": "8443",
|
||||
"traefik.http.services.kunde-a.loadbalancer.server.scheme": "https",
|
||||
"traefik.http.routers.kunde-a-http.rule": rule,
|
||||
"traefik.http.routers.kunde-a-http.entrypoints": "web",
|
||||
"traefik.http.routers.kunde-a-http.service": "kunde-a",
|
||||
"traefik.http.routers.kunde-a-http.middlewares": "kunde-a-https",
|
||||
"traefik.http.middlewares.kunde-a-https.redirectscheme.scheme": "https",
|
||||
"traefik.http.middlewares.kunde-a-https.redirectscheme.permanent": "true",
|
||||
"traefik.http.middlewares.kunde-a-www.redirectregex.regex": `^https?://www\.(kunde-a\.de)(.*)`,
|
||||
"traefik.http.middlewares.kunde-a-www.redirectregex.replacement": "https://${1}${2}",
|
||||
"traefik.http.middlewares.kunde-a-www.redirectregex.permanent": "false", // rohes Label gewinnt
|
||||
}
|
||||
got := FileWorkloads(specs, "h")[0].Labels
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
for k, v := range want {
|
||||
if got[k] != v {
|
||||
t.Errorf("%s = %q, erwartet %q", k, got[k], v)
|
||||
}
|
||||
}
|
||||
for k := range got {
|
||||
if _, ok := want[k]; !ok {
|
||||
t.Errorf("unerwartetes Label %s = %q", k, got[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileWorkloads_RedirectToWWW(t *testing.T) {
|
||||
specs := parse(t, `
|
||||
services:
|
||||
- name: web
|
||||
url: http://127.0.0.1
|
||||
hosts: [www.shop.de, kunde.de]
|
||||
redirect_www: to_www
|
||||
`)
|
||||
l := FileWorkloads(specs, "h")[0].Labels
|
||||
if l["traefik.http.routers.web.rule"] != "Host(`www.shop.de`) || Host(`kunde.de`) || Host(`shop.de`) || Host(`www.kunde.de`)" {
|
||||
t.Errorf("Regel = %s", l["traefik.http.routers.web.rule"])
|
||||
}
|
||||
if l["traefik.http.middlewares.web-www.redirectregex.regex"] != `^https?://(shop\.de|kunde\.de)(.*)` ||
|
||||
l["traefik.http.middlewares.web-www.redirectregex.replacement"] != "http://www.${1}${2}" {
|
||||
t.Errorf("Middleware: %v", l)
|
||||
}
|
||||
if l["traefik.http.services.web.loadbalancer.server.port"] != "80" {
|
||||
t.Errorf("Default-Port: %s", l["traefik.http.services.web.loadbalancer.server.port"])
|
||||
}
|
||||
}
|
||||
|
||||
// Die erzeugten Labels müssen dieselbe Übersetzung bestehen wie Docker-Labels.
|
||||
func TestFileWorkloads_LabelsAreValidTraefik(t *testing.T) {
|
||||
specs := parse(t, `
|
||||
services:
|
||||
- name: kunde-a
|
||||
url: https://127.0.0.1:8443/
|
||||
hosts: [kunde-a.de]
|
||||
https: true
|
||||
redirect_http_to_https: true
|
||||
redirect_www: to_non_www
|
||||
`)
|
||||
w := FileWorkloads(specs, "100.64.0.42")[0]
|
||||
cfg, err := traefik.FromLabels(w.Host, w.Labels)
|
||||
if err != nil {
|
||||
t.Fatalf("FromLabels: %v", err)
|
||||
}
|
||||
if len(cfg.HTTP.Routers) != 2 || cfg.HTTP.Services["kunde-a"].LoadBalancer.Servers[0].URL != "https://100.64.0.42:8443" {
|
||||
t.Errorf("Konfiguration: %+v", cfg.HTTP)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseServices_Healthcheck(t *testing.T) {
|
||||
specs := parse(t, `
|
||||
services:
|
||||
- name: shop
|
||||
url: http://127.0.0.1:8080/start
|
||||
hosts: [shop.kunde.de]
|
||||
healthcheck:
|
||||
interval: 10s
|
||||
expect_title: "Shop - Willkommen"
|
||||
expect_contains: { tag: h2, text: Impressum }
|
||||
- name: blog
|
||||
url: http://127.0.0.1:8081
|
||||
hosts: [blog.kunde.de]
|
||||
healthcheck: {}
|
||||
`)
|
||||
h := specs[0].Healthcheck
|
||||
if h == nil || h.Interval.Duration != 10*time.Second || h.Timeout.Duration != 5*time.Second || h.Failures != 2 ||
|
||||
h.ExpectStatus != 200 || h.URL != "http://127.0.0.1:8080/start" || h.ExpectTitle != "Shop - Willkommen" ||
|
||||
h.ExpectContains == nil || h.ExpectContains.Tag != "h2" || h.ExpectContains.Text != "Impressum" {
|
||||
t.Errorf("Healthcheck: %+v", h)
|
||||
}
|
||||
if d := specs[1].Healthcheck; d == nil || d.Interval.Duration != 30*time.Second || d.ExpectStatus != 200 {
|
||||
t.Errorf("Healthcheck-Defaults: %+v", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseServices_Errors(t *testing.T) {
|
||||
tests := map[string]string{
|
||||
"kein YAML": "services: [",
|
||||
"unbekanntes Feld": "services:\n - name: a\n url: http://x\n hosts: [a.de]\n farbe: blau\n",
|
||||
"Name fehlt": "services:\n - url: http://x\n hosts: [a.de]\n",
|
||||
"Name ungültig": "services:\n - name: a.b\n url: http://x\n hosts: [a.de]\n",
|
||||
"URL fehlt": "services:\n - name: a\n hosts: [a.de]\n",
|
||||
"URL kein http": "services:\n - name: a\n url: ftp://x\n hosts: [a.de]\n",
|
||||
"Hosts fehlen": "services:\n - name: a\n url: http://x\n",
|
||||
"Host mit Wildcard": "services:\n - name: a\n url: http://x\n hosts: ['*.a.de']\n",
|
||||
"redirect_www ungültig": "services:\n - name: a\n url: http://x\n hosts: [a.de]\n redirect_www: manchmal\n",
|
||||
"Redirect ohne https": "services:\n - name: a\n url: http://x\n hosts: [a.de]\n redirect_http_to_https: true\n",
|
||||
"Intervall unlesbar": "services:\n - name: a\n url: http://x\n hosts: [a.de]\n healthcheck: { interval: oft }\n",
|
||||
"Status ungültig": "services:\n - name: a\n url: http://x\n hosts: [a.de]\n healthcheck: { expect_status: 42 }\n",
|
||||
"contains ohne Tag": "services:\n - name: a\n url: http://x\n hosts: [a.de]\n healthcheck: { expect_contains: { text: x } }\n",
|
||||
"doppelter Name": "services:\n - name: a\n url: http://x\n hosts: [a.de]\n - name: a\n url: http://y\n hosts: [b.de]\n",
|
||||
}
|
||||
for name, yaml := range tests {
|
||||
if _, err := ParseServices([]byte(yaml)); err == nil {
|
||||
t.Errorf("%s: erwartet Fehler", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadServiceDir(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
write := func(name, content string) {
|
||||
if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
write("b.yaml", "services:\n - name: shop\n url: http://127.0.0.1:8080\n hosts: [shop.kunde.de]\n")
|
||||
write("a.yml", "services:\n - name: blog\n url: http://127.0.0.1:8081\n hosts: [blog.kunde.de]\n")
|
||||
write("c.yaml", "services: [") // kaputt
|
||||
write("d.yaml", "services:\n - name: shop\n url: http://127.0.0.1:9\n hosts: [x.kunde.de]\n") // doppelt
|
||||
write("notizen.txt", "services:\n - name: txt\n url: http://127.0.0.1:1\n hosts: [t.kunde.de]\n") // ignoriert
|
||||
|
||||
specs, warnings := LoadServiceDir(dir)
|
||||
var names []string
|
||||
for _, s := range specs {
|
||||
names = append(names, s.Name)
|
||||
}
|
||||
if !reflect.DeepEqual(names, []string{"blog", "shop"}) {
|
||||
t.Errorf("Dienste = %v (Dateien alphabetisch, Duplikat und .txt ignoriert)", names)
|
||||
}
|
||||
if len(warnings) != 2 || !strings.Contains(strings.Join(warnings, "\n"), "c.yaml") || !strings.Contains(strings.Join(warnings, "\n"), "d.yaml") {
|
||||
t.Errorf("Warnungen = %v", warnings)
|
||||
}
|
||||
|
||||
if specs, warnings := LoadServiceDir(filepath.Join(dir, "fehlt")); len(specs) != 0 || len(warnings) != 0 {
|
||||
t.Errorf("fehlendes Verzeichnis: %v %v", specs, warnings)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileWorkloads_Maintenance(t *testing.T) {
|
||||
specs := parse(t, `
|
||||
services:
|
||||
- name: shop
|
||||
url: http://127.0.0.1:8080
|
||||
hosts: [shop.kunde.de]
|
||||
maintenance: true
|
||||
maintenance_public_reason: "Inventur bis Montag"
|
||||
- name: blog
|
||||
url: http://127.0.0.1:8081
|
||||
hosts: [blog.kunde.de]
|
||||
`)
|
||||
ws := FileWorkloads(specs, "h")
|
||||
if ws[0].Labels["sultan.maintenance"] != "true" || ws[0].Labels["sultan.maintenance.public_reason"] != "Inventur bis Montag" {
|
||||
t.Errorf("Wartungs-Labels fehlen: %v", ws[0].Labels)
|
||||
}
|
||||
if _, ok := ws[1].Labels["sultan.maintenance"]; ok {
|
||||
t.Error("Dienst ohne Wartung hat Wartungs-Label")
|
||||
}
|
||||
if _, err := ParseServices([]byte("services:\n - name: a\n url: http://x\n hosts: [a.de]\n maintenance_public_reason: \"a\\nb\"\n")); err == nil {
|
||||
t.Error("mehrzeiliger Grund akzeptiert")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user