1package proxy
2
3import (
4 "context"
5 "fmt"
6 "strconv"
7
8 "github.com/bnema/zerowrap"
9
10 "github.com/bnema/gordon/internal/domain"
11)
12
13// TargetMetadata holds resolved backend connection metadata for a container image.
14type TargetMetadata struct {
15 Port int // The port to proxy traffic to
16 Protocol string // "" for HTTP/1.1, "h2c" for cleartext HTTP/2
17}
18
19// resolveTargetMetadata determines the port and protocol for an image by reading
20// image labels. It checks gordon.proxy.port first, then the deprecated gordon.port
21// alias, then falls back to the first exposed port. Protocol is read from
22// gordon.proxy.protocol (only "h2c" is recognized; unknown values are ignored).
23func (s *Service) resolveTargetMetadata(ctx context.Context, imageRef string) (TargetMetadata, error) {
24 log := zerowrap.FromCtx(ctx)
25 log.Debug().Str("image_ref", imageRef).Msg("resolving target metadata for image")
26
27 labels, err := s.runtime.GetImageLabels(ctx, imageRef)
28 if err != nil {
29 log.Debug().Err(err).Msg("failed to get image labels, falling back to exposed ports")
30 labels = nil
31 }
32
33 port := 0
34 if labels != nil {
35 for _, key := range []string{domain.LabelProxyPort, domain.LabelPort} {
36 if portStr, ok := labels[key]; ok && portStr != "" {
37 p, convErr := strconv.Atoi(portStr)
38 if convErr == nil && p > 0 && p <= 65535 {
39 log.Debug().Int("port", p).Str("label", key).Msg("using proxy port from image label")
40 port = p
41 break
42 }
43 log.Warn().Str("label", key).Str("port_value", portStr).Msg("invalid port label value")
44 }
45 }
46 }
47
48 if port == 0 {
49 exposedPorts, portsErr := s.runtime.GetImageExposedPorts(ctx, imageRef)
50 if portsErr != nil {
51 return TargetMetadata{}, fmt.Errorf("failed to get exposed ports for image %s: %w", imageRef, portsErr)
52 }
53 if len(exposedPorts) == 0 {
54 return TargetMetadata{}, fmt.Errorf("no exposed ports found for image %s", imageRef)
55 }
56 port = exposedPorts[0]
57 log.Debug().Int("port", port).Msg("using first exposed port")
58 }
59
60 protocol := ""
61 if labels != nil {
62 proto := labels[domain.LabelProxyProtocol]
63 switch proto {
64 case "h2c":
65 protocol = "h2c"
66 log.Debug().Str("protocol", proto).Msg("container opts into h2c")
67 case "":
68 default:
69 log.Warn().Str("protocol", proto).Msg("unknown proxy protocol label value, ignoring")
70 }
71 }
72
73 return TargetMetadata{Port: port, Protocol: protocol}, nil
74}