software steht so weit.

jetzt build + docker build und testen
This commit is contained in:
br committed 2026-10-02 13:25:34 +02:00
1 parent 42f0c0accb
commit ca6cdcca63
90 files changed
+10298 -480

No files matched your search

+147
View File
@@ -0,0 +1,147 @@
package agent
import (
"fmt"
"maps"
"net"
"regexp"
"slices"
"strconv"
"strings"
"sultan/pkg/docker"
"sultan/pkg/registry"
)
const servicesPrefix = "traefik.http.services."
var unsafeName = regexp.MustCompile(`[^A-Za-z0-9_-]`)
// Workloads übersetzt laufende Container in Workloads für service_state.
//
// Berücksichtigt werden nur Container mit traefik.enable=true. Weil der
// Ingress auf einem anderen Host sitzt, wird loadbalancer.server.port (Port
// im Container) auf den veröffentlichten Host-Port umgerechnet. Fehlt das
// Port-Label, gilt wie bei Traefiks Docker-Provider der einzige
// veröffentlichte Port; ohne Service-Labels entsteht ein Service mit dem
// Containernamen. Container, deren Ports von außen nicht erreichbar sind
// (nicht veröffentlicht, nur Loopback, nur UDP), werden mit Warnung
// weggelassen.
func Workloads(containers []docker.Container, host string) ([]registry.Workload, []string) {
var out []registry.Workload
var warnings []string
for _, c := range containers {
if !enabled(c.Labels) {
continue
}
labels, warning := translatePorts(c)
if warning != "" {
warnings = append(warnings, warning)
continue
}
out = append(out, registry.Workload{Name: c.Name(), Host: host, Labels: labels})
}
slices.SortFunc(out, func(a, b registry.Workload) int { return strings.Compare(a.Name, b.Name) })
return out, warnings
}
func enabled(labels map[string]string) bool {
for k, v := range labels {
if strings.EqualFold(k, "traefik.enable") {
on, err := strconv.ParseBool(strings.TrimSpace(v))
return err == nil && on
}
}
return false
}
// reachablePorts: Container-Port → von außen erreichbarer Host-Port (TCP).
func reachablePorts(ports []docker.Port) map[uint16]uint16 {
out := map[uint16]uint16{}
for _, p := range ports {
if p.Type != "tcp" || p.PublicPort == 0 {
continue
}
if ip := net.ParseIP(p.IP); ip != nil && ip.IsLoopback() {
continue
}
out[p.PrivatePort] = p.PublicPort
}
return out
}
type serviceLabels struct {
name string // Schreibweise aus dem Label
portKey string // Original-Schlüssel des Port-Labels, "" wenn keins
}
func translatePorts(c docker.Container) (map[string]string, string) {
labels := maps.Clone(c.Labels)
reachable := reachablePorts(c.Ports)
single := func() (uint16, bool) {
for _, pub := range reachable {
return pub, len(reachable) == 1
}
return 0, false
}
services := map[string]*serviceLabels{}
hasRouters := false
for k := range labels {
lk := strings.ToLower(k)
if strings.HasPrefix(lk, "traefik.http.routers.") {
hasRouters = true
}
rest, ok := strings.CutPrefix(lk, servicesPrefix)
if !ok {
continue
}
svc, attr, ok := strings.Cut(rest, ".")
if !ok {
continue
}
s := services[svc]
if s == nil {
name, _, _ := strings.Cut(k[len(servicesPrefix):], ".")
s = &serviceLabels{name: name}
services[svc] = s
}
if attr == "loadbalancer.server.port" {
s.portKey = k
}
}
if len(services) == 0 {
if !hasRouters {
return labels, ""
}
pub, ok := single()
if !ok {
return nil, fmt.Sprintf("Container %s: %d erreichbare Ports – Label loadbalancer.server.port angeben", c.Name(), len(reachable))
}
svc := unsafeName.ReplaceAllString(c.Name(), "-")
labels[servicesPrefix+svc+".loadbalancer.server.port"] = strconv.Itoa(int(pub))
return labels, ""
}
for _, s := range services {
if s.portKey == "" {
pub, ok := single()
if !ok {
return nil, fmt.Sprintf("Container %s, Service %s: %d erreichbare Ports – Label loadbalancer.server.port angeben", c.Name(), s.name, len(reachable))
}
labels[servicesPrefix+s.name+".loadbalancer.server.port"] = strconv.Itoa(int(pub))
continue
}
private, err := strconv.Atoi(strings.TrimSpace(labels[s.portKey]))
if err != nil {
return nil, fmt.Sprintf("Container %s: Port %q nicht lesbar", c.Name(), labels[s.portKey])
}
pub, ok := reachable[uint16(private)]
if !ok {
return nil, fmt.Sprintf("Container %s: Port %d ist von außen nicht erreichbar (nicht veröffentlicht oder nur 127.0.0.1)", c.Name(), private)
}
labels[s.portKey] = strconv.Itoa(int(pub))
}
return labels, ""
}