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,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, ""
|
||||
}
|
||||
Reference in new issue
Block a user