detached 18a333b

refactor: remove deprecated compatibility shims

commit
18a333b3ff743c8f4e070f5decb697e645bf531b
author
brice · · 2026-04-08 17:53 UTC
parent
1489816
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
13 files · +40 -202
M internal/adapters/in/cli/parity_matrix_test.go +0 -5
@@ -16,11 +16,6 @@ type uiAdoptionExpectation struct {
1616 }
1717
1818 var uiAdoptionExpectations = []uiAdoptionExpectation{
19- {
20- family: "server",
21- file: "serve.go",
22- functions: []string{"newStartCmd"},
23- },
2419 {
2520 family: "root/server",
2621 file: "root.go",
@@ -16,11 +16,6 @@ type uiAdoptionExpectation struct {
16 }16 }
17 17
18 var uiAdoptionExpectations = []uiAdoptionExpectation{18 var uiAdoptionExpectations = []uiAdoptionExpectation{
19- {
20- family: "server",
21- file: "serve.go",
22- functions: []string{"newStartCmd"},
23- },
24 {19 {
25 family: "root/server",20 family: "root/server",
26 file: "root.go",21 file: "root.go",
M internal/adapters/in/cli/root.go +0 -4
@@ -68,10 +68,6 @@ Commands are organized by where they run:
6868 serveCmd.GroupID = groupServer
6969 rootCmd.AddCommand(serveCmd)
7070
71- startCmd := newStartCmd() // Deprecated alias for serve
72- startCmd.GroupID = groupServer
73- rootCmd.AddCommand(startCmd)
74-
7571 authCmd := newAuthCmd()
7672 authCmd.GroupID = groupServer
7773 rootCmd.AddCommand(authCmd)
@@ -68,10 +68,6 @@ Commands are organized by where they run:
68 serveCmd.GroupID = groupServer68 serveCmd.GroupID = groupServer
69 rootCmd.AddCommand(serveCmd)69 rootCmd.AddCommand(serveCmd)
70 70
71- startCmd := newStartCmd() // Deprecated alias for serve
72- startCmd.GroupID = groupServer
73- rootCmd.AddCommand(startCmd)
74-
75 authCmd := newAuthCmd()71 authCmd := newAuthCmd()
76 authCmd.GroupID = groupServer72 authCmd.GroupID = groupServer
77 rootCmd.AddCommand(authCmd)73 rootCmd.AddCommand(authCmd)
M internal/adapters/in/cli/serve.go +0 -25
@@ -2,7 +2,6 @@ package cli
22
33 import (
44 "context"
5- "os"
65
76 "github.com/bnema/gordon/internal/app"
87
@@ -27,27 +26,3 @@ func newServeCmd() *cobra.Command {
2726
2827 return cmd
2928 }
30-
31-// newStartCmd creates a deprecated alias for serve.
32-func newStartCmd() *cobra.Command {
33- var configPath string
34-
35- cmd := &cobra.Command{
36- Use: "start",
37- Short: "Start the Gordon server (deprecated: use 'serve')",
38- Long: `Start the Gordon server. This command is deprecated, please use 'gordon serve' instead.`,
39- Deprecated: "use 'gordon serve' instead",
40- Hidden: true, // Hide from help but still works
41- RunE: func(cmd *cobra.Command, args []string) error {
42- if err := cliWriteLine(os.Stderr, cliRenderWarning("Warning: 'gordon start' is deprecated, use 'gordon serve' instead")); err != nil {
43- return err
44- }
45- return app.Run(context.Background(), configPath)
46- },
47- }
48-
49- // Add flags (same as serve)
50- cmd.Flags().StringVarP(&configPath, "config", "c", "", "Path to config file")
51-
52- return cmd
53-}
@@ -2,7 +2,6 @@ package cli
2 2
3 import (3 import (
4 "context"4 "context"
5- "os"
6 5
7 "github.com/bnema/gordon/internal/app"6 "github.com/bnema/gordon/internal/app"
8 7
@@ -27,27 +26,3 @@ func newServeCmd() *cobra.Command {
27 26
28 return cmd27 return cmd
29 }28 }
30-
31-// newStartCmd creates a deprecated alias for serve.
32-func newStartCmd() *cobra.Command {
33- var configPath string
34-
35- cmd := &cobra.Command{
36- Use: "start",
37- Short: "Start the Gordon server (deprecated: use 'serve')",
38- Long: `Start the Gordon server. This command is deprecated, please use 'gordon serve' instead.`,
39- Deprecated: "use 'gordon serve' instead",
40- Hidden: true, // Hide from help but still works
41- RunE: func(cmd *cobra.Command, args []string) error {
42- if err := cliWriteLine(os.Stderr, cliRenderWarning("Warning: 'gordon start' is deprecated, use 'gordon serve' instead")); err != nil {
43- return err
44- }
45- return app.Run(context.Background(), configPath)
46- },
47- }
48-
49- // Add flags (same as serve)
50- cmd.Flags().StringVarP(&configPath, "config", "c", "", "Path to config file")
51-
52- return cmd
53-}
M internal/adapters/in/http/middleware/accesslog.go +2 -1
@@ -9,6 +9,7 @@ import (
99
1010 "github.com/bnema/zerowrap"
1111
12+ "github.com/bnema/gordon/internal/adapters/in/http/httphelper"
1213 out "github.com/bnema/gordon/internal/boundaries/out"
1314 )
1415
@@ -45,7 +46,7 @@ func AccessLogger(writer out.AccessLogWriter, excludeHealthChecks bool, log zero
4546 // cidr.go (same package) — no separate loopback definition needed.
4647 if excludeHealthChecks {
4748 if strings.HasPrefix(r.UserAgent(), out.HealthCheckUserAgentPrefix) ||
48- IsTrustedProxy(clientIP, localhostNets) {
49+ httphelper.IsTrustedProxy(clientIP, localhostNets) {
4950 return
5051 }
5152 }
@@ -9,6 +9,7 @@ import (
9 9
10 "github.com/bnema/zerowrap"10 "github.com/bnema/zerowrap"
11 11
12+ "github.com/bnema/gordon/internal/adapters/in/http/httphelper"
12 out "github.com/bnema/gordon/internal/boundaries/out"13 out "github.com/bnema/gordon/internal/boundaries/out"
13 )14 )
14 15
@@ -45,7 +46,7 @@ func AccessLogger(writer out.AccessLogWriter, excludeHealthChecks bool, log zero
45 // cidr.go (same package) — no separate loopback definition needed.46 // cidr.go (same package) — no separate loopback definition needed.
46 if excludeHealthChecks {47 if excludeHealthChecks {
47 if strings.HasPrefix(r.UserAgent(), out.HealthCheckUserAgentPrefix) ||48 if strings.HasPrefix(r.UserAgent(), out.HealthCheckUserAgentPrefix) ||
48- IsTrustedProxy(clientIP, localhostNets) {49+ httphelper.IsTrustedProxy(clientIP, localhostNets) {
49 return50 return
50 }51 }
51 }52 }
M internal/adapters/in/http/middleware/cidr.go +6 -31
@@ -27,12 +27,12 @@ func cidrAllowlist(allowedNets []*net.IPNet, ipExtractor func(*http.Request) str
2727 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2828 clientIP := ipExtractor(r)
2929
30- if IsTrustedProxy(clientIP, localhostNets) {
30+ if httphelper.IsTrustedProxy(clientIP, localhostNets) {
3131 next.ServeHTTP(w, r)
3232 return
3333 }
3434
35- if IsTrustedProxy(clientIP, allowedNets) {
35+ if httphelper.IsTrustedProxy(clientIP, allowedNets) {
3636 next.ServeHTTP(w, r)
3737 return
3838 }
@@ -51,31 +51,6 @@ func cidrAllowlist(allowedNets []*net.IPNet, ipExtractor func(*http.Request) str
5151 }
5252 }
5353
54-// ExtractRemoteIP returns the IP portion of a RemoteAddr, stripping the port.
55-//
56-// Deprecated: use httphelper.ExtractRemoteIP directly. This wrapper preserves
57-// the existing middleware API for callers that have not migrated yet.
58-func ExtractRemoteIP(remoteAddr string) string {
59- return httphelper.ExtractRemoteIP(remoteAddr)
60-}
61-
62-// IsTrustedOrLocal reports whether an IP belongs to localhost or trusted proxy nets.
63-//
64-// Deprecated: use httphelper.IsTrustedOrLocal directly. This wrapper preserves
65-// the existing middleware API for callers that have not migrated yet.
66-func IsTrustedOrLocal(ip string, proxyNets []*net.IPNet) bool {
67- return httphelper.IsTrustedOrLocal(ip, proxyNets)
68-}
69-
70-// HTTPSAuthority converts an incoming Host header plus httpPort/tlsPort into the
71-// correct HTTPS authority (host or host:port).
72-//
73-// Deprecated: use httphelper.HTTPSAuthority directly. This wrapper preserves
74-// the existing middleware API for callers that have not migrated yet.
75-func HTTPSAuthority(host string, httpPort, tlsPort int) string {
76- return httphelper.HTTPSAuthority(host, httpPort, tlsPort)
77-}
78-
7954 // RegistryCIDRAllowlist returns middleware that restricts access to the given CIDR ranges.
8055 // Localhost is always allowed so Gordon can pull from its own registry.
8156 // An empty allowedNets slice is a no-op (all traffic passes through).
@@ -109,8 +84,8 @@ func HTTPSRedirect(proxyNets []*net.IPNet, httpPort, tlsPort int, forceAll bool,
10984 }
11085 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
11186 if !forceAll {
112- remoteIP := ExtractRemoteIP(r.RemoteAddr)
113- if IsTrustedOrLocal(remoteIP, proxyNets) {
87+ remoteIP := httphelper.ExtractRemoteIP(r.RemoteAddr)
88+ if httphelper.IsTrustedOrLocal(remoteIP, proxyNets) {
11489 next.ServeHTTP(w, r)
11590 return
11691 }
@@ -134,7 +109,7 @@ func httpsRedirectTarget(host, requestURI string, httpPort, tlsPort int) string
134109 path = "/"
135110 }
136111
137- return fmt.Sprintf("https://%s%s", HTTPSAuthority(host, httpPort, tlsPort), path)
112+ return fmt.Sprintf("https://%s%s", httphelper.HTTPSAuthority(host, httpPort, tlsPort), path)
138113 }
139114
140115 // ProxyCIDRAllowlist returns middleware that restricts proxy access to the given CIDR ranges.
@@ -143,6 +118,6 @@ func httpsRedirectTarget(host, requestURI string, httpPort, tlsPort int) string
143118 // An empty allowedNets slice is a no-op (all traffic passes through).
144119 func ProxyCIDRAllowlist(allowedNets []*net.IPNet, log zerowrap.Logger) func(http.Handler) http.Handler {
145120 return cidrAllowlist(allowedNets, func(r *http.Request) string {
146- return ExtractRemoteIP(r.RemoteAddr)
121+ return httphelper.ExtractRemoteIP(r.RemoteAddr)
147122 }, "proxy origin", log)
148123 }
@@ -27,12 +27,12 @@ func cidrAllowlist(allowedNets []*net.IPNet, ipExtractor func(*http.Request) str
27 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {27 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
28 clientIP := ipExtractor(r)28 clientIP := ipExtractor(r)
29 29
30- if IsTrustedProxy(clientIP, localhostNets) {30+ if httphelper.IsTrustedProxy(clientIP, localhostNets) {
31 next.ServeHTTP(w, r)31 next.ServeHTTP(w, r)
32 return32 return
33 }33 }
34 34
35- if IsTrustedProxy(clientIP, allowedNets) {35+ if httphelper.IsTrustedProxy(clientIP, allowedNets) {
36 next.ServeHTTP(w, r)36 next.ServeHTTP(w, r)
37 return37 return
38 }38 }
@@ -51,31 +51,6 @@ func cidrAllowlist(allowedNets []*net.IPNet, ipExtractor func(*http.Request) str
51 }51 }
52 }52 }
53 53
54-// ExtractRemoteIP returns the IP portion of a RemoteAddr, stripping the port.
55-//
56-// Deprecated: use httphelper.ExtractRemoteIP directly. This wrapper preserves
57-// the existing middleware API for callers that have not migrated yet.
58-func ExtractRemoteIP(remoteAddr string) string {
59- return httphelper.ExtractRemoteIP(remoteAddr)
60-}
61-
62-// IsTrustedOrLocal reports whether an IP belongs to localhost or trusted proxy nets.
63-//
64-// Deprecated: use httphelper.IsTrustedOrLocal directly. This wrapper preserves
65-// the existing middleware API for callers that have not migrated yet.
66-func IsTrustedOrLocal(ip string, proxyNets []*net.IPNet) bool {
67- return httphelper.IsTrustedOrLocal(ip, proxyNets)
68-}
69-
70-// HTTPSAuthority converts an incoming Host header plus httpPort/tlsPort into the
71-// correct HTTPS authority (host or host:port).
72-//
73-// Deprecated: use httphelper.HTTPSAuthority directly. This wrapper preserves
74-// the existing middleware API for callers that have not migrated yet.
75-func HTTPSAuthority(host string, httpPort, tlsPort int) string {
76- return httphelper.HTTPSAuthority(host, httpPort, tlsPort)
77-}
78-
79 // RegistryCIDRAllowlist returns middleware that restricts access to the given CIDR ranges.54 // RegistryCIDRAllowlist returns middleware that restricts access to the given CIDR ranges.
80 // Localhost is always allowed so Gordon can pull from its own registry.55 // Localhost is always allowed so Gordon can pull from its own registry.
81 // An empty allowedNets slice is a no-op (all traffic passes through).56 // An empty allowedNets slice is a no-op (all traffic passes through).
@@ -109,8 +84,8 @@ func HTTPSRedirect(proxyNets []*net.IPNet, httpPort, tlsPort int, forceAll bool,
109 }84 }
110 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {85 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
111 if !forceAll {86 if !forceAll {
112- remoteIP := ExtractRemoteIP(r.RemoteAddr)87+ remoteIP := httphelper.ExtractRemoteIP(r.RemoteAddr)
113- if IsTrustedOrLocal(remoteIP, proxyNets) {88+ if httphelper.IsTrustedOrLocal(remoteIP, proxyNets) {
114 next.ServeHTTP(w, r)89 next.ServeHTTP(w, r)
115 return90 return
116 }91 }
@@ -134,7 +109,7 @@ func httpsRedirectTarget(host, requestURI string, httpPort, tlsPort int) string
134 path = "/"109 path = "/"
135 }110 }
136 111
137- return fmt.Sprintf("https://%s%s", HTTPSAuthority(host, httpPort, tlsPort), path)112+ return fmt.Sprintf("https://%s%s", httphelper.HTTPSAuthority(host, httpPort, tlsPort), path)
138 }113 }
139 114
140 // ProxyCIDRAllowlist returns middleware that restricts proxy access to the given CIDR ranges.115 // ProxyCIDRAllowlist returns middleware that restricts proxy access to the given CIDR ranges.
@@ -143,6 +118,6 @@ func httpsRedirectTarget(host, requestURI string, httpPort, tlsPort int) string
143 // An empty allowedNets slice is a no-op (all traffic passes through).118 // An empty allowedNets slice is a no-op (all traffic passes through).
144 func ProxyCIDRAllowlist(allowedNets []*net.IPNet, log zerowrap.Logger) func(http.Handler) http.Handler {119 func ProxyCIDRAllowlist(allowedNets []*net.IPNet, log zerowrap.Logger) func(http.Handler) http.Handler {
145 return cidrAllowlist(allowedNets, func(r *http.Request) string {120 return cidrAllowlist(allowedNets, func(r *http.Request) string {
146- return ExtractRemoteIP(r.RemoteAddr)121+ return httphelper.ExtractRemoteIP(r.RemoteAddr)
147 }, "proxy origin", log)122 }, "proxy origin", log)
148 }123 }
M internal/adapters/in/http/middleware/cidr_test.go +14 -13
@@ -9,6 +9,7 @@ import (
99 "testing"
1010
1111 "github.com/bnema/gordon/internal/adapters/dto"
12+ "github.com/bnema/gordon/internal/adapters/in/http/httphelper"
1213 "github.com/stretchr/testify/assert"
1314 "github.com/stretchr/testify/require"
1415 )
@@ -41,46 +42,46 @@ func TestRegistryCIDRAllowlist(t *testing.T) {
4142 },
4243 {
4344 name: "allowed IP returns 200",
44- allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),
45+ allowedNets: httphelper.ParseTrustedProxies([]string{"100.64.0.0/10"}),
4546 remoteAddr: "100.100.1.1:1234",
4647 wantStatus: http.StatusOK,
4748 },
4849 {
4950 name: "denied IP returns 403",
50- allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),
51+ allowedNets: httphelper.ParseTrustedProxies([]string{"100.64.0.0/10"}),
5152 remoteAddr: "203.0.113.50:1234",
5253 wantStatus: http.StatusForbidden,
5354 },
5455 {
5556 name: "localhost always allowed even when not in allowlist",
56- allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),
57+ allowedNets: httphelper.ParseTrustedProxies([]string{"100.64.0.0/10"}),
5758 remoteAddr: "127.0.0.1:1234",
5859 wantStatus: http.StatusOK,
5960 },
6061 {
6162 name: "127.x.x.x always allowed",
62- allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),
63+ allowedNets: httphelper.ParseTrustedProxies([]string{"100.64.0.0/10"}),
6364 remoteAddr: "127.0.0.2:1234",
6465 wantStatus: http.StatusOK,
6566 },
6667 {
6768 name: "IPv6 localhost always allowed",
68- allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),
69+ allowedNets: httphelper.ParseTrustedProxies([]string{"100.64.0.0/10"}),
6970 remoteAddr: "[::1]:1234",
7071 wantStatus: http.StatusOK,
7172 },
7273 {
7374 name: "trusted proxy forwards allowed client IP",
74- allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),
75- trustedNets: ParseTrustedProxies([]string{"10.0.0.1"}),
75+ allowedNets: httphelper.ParseTrustedProxies([]string{"100.64.0.0/10"}),
76+ trustedNets: httphelper.ParseTrustedProxies([]string{"10.0.0.1"}),
7677 remoteAddr: "10.0.0.1:1234",
7778 xff: "100.100.1.1",
7879 wantStatus: http.StatusOK,
7980 },
8081 {
8182 name: "trusted proxy forwards denied client IP",
82- allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),
83- trustedNets: ParseTrustedProxies([]string{"10.0.0.1"}),
83+ allowedNets: httphelper.ParseTrustedProxies([]string{"100.64.0.0/10"}),
84+ trustedNets: httphelper.ParseTrustedProxies([]string{"10.0.0.1"}),
8485 remoteAddr: "10.0.0.1:1234",
8586 xff: "203.0.113.50",
8687 wantStatus: http.StatusForbidden,
@@ -227,25 +228,25 @@ func TestProxyCIDRAllowlist(t *testing.T) {
227228 },
228229 {
229230 name: "allowed IP gets redirect",
230- allowedNets: ParseTrustedProxies([]string{"173.245.48.0/20"}),
231+ allowedNets: httphelper.ParseTrustedProxies([]string{"173.245.48.0/20"}),
231232 remoteAddr: "173.245.48.1:1234",
232233 wantStatus: http.StatusPermanentRedirect,
233234 },
234235 {
235236 name: "blocked IP gets 403",
236- allowedNets: ParseTrustedProxies([]string{"173.245.48.0/20"}),
237+ allowedNets: httphelper.ParseTrustedProxies([]string{"173.245.48.0/20"}),
237238 remoteAddr: "203.0.113.50:1234",
238239 wantStatus: http.StatusForbidden,
239240 },
240241 {
241242 name: "localhost always allowed",
242- allowedNets: ParseTrustedProxies([]string{"173.245.48.0/20"}),
243+ allowedNets: httphelper.ParseTrustedProxies([]string{"173.245.48.0/20"}),
243244 remoteAddr: "127.0.0.1:1234",
244245 wantStatus: http.StatusPermanentRedirect,
245246 },
246247 {
247248 name: "ignores XFF (uses RemoteAddr only)",
248- allowedNets: ParseTrustedProxies([]string{"173.245.48.0/20"}),
249+ allowedNets: httphelper.ParseTrustedProxies([]string{"173.245.48.0/20"}),
249250 remoteAddr: "203.0.113.50:1234",
250251 wantStatus: http.StatusForbidden,
251252 },
@@ -9,6 +9,7 @@ import (
9 "testing"9 "testing"
10 10
11 "github.com/bnema/gordon/internal/adapters/dto"11 "github.com/bnema/gordon/internal/adapters/dto"
12+ "github.com/bnema/gordon/internal/adapters/in/http/httphelper"
12 "github.com/stretchr/testify/assert"13 "github.com/stretchr/testify/assert"
13 "github.com/stretchr/testify/require"14 "github.com/stretchr/testify/require"
14 )15 )
@@ -41,46 +42,46 @@ func TestRegistryCIDRAllowlist(t *testing.T) {
41 },42 },
42 {43 {
43 name: "allowed IP returns 200",44 name: "allowed IP returns 200",
44- allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),45+ allowedNets: httphelper.ParseTrustedProxies([]string{"100.64.0.0/10"}),
45 remoteAddr: "100.100.1.1:1234",46 remoteAddr: "100.100.1.1:1234",
46 wantStatus: http.StatusOK,47 wantStatus: http.StatusOK,
47 },48 },
48 {49 {
49 name: "denied IP returns 403",50 name: "denied IP returns 403",
50- allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),51+ allowedNets: httphelper.ParseTrustedProxies([]string{"100.64.0.0/10"}),
51 remoteAddr: "203.0.113.50:1234",52 remoteAddr: "203.0.113.50:1234",
52 wantStatus: http.StatusForbidden,53 wantStatus: http.StatusForbidden,
53 },54 },
54 {55 {
55 name: "localhost always allowed even when not in allowlist",56 name: "localhost always allowed even when not in allowlist",
56- allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),57+ allowedNets: httphelper.ParseTrustedProxies([]string{"100.64.0.0/10"}),
57 remoteAddr: "127.0.0.1:1234",58 remoteAddr: "127.0.0.1:1234",
58 wantStatus: http.StatusOK,59 wantStatus: http.StatusOK,
59 },60 },
60 {61 {
61 name: "127.x.x.x always allowed",62 name: "127.x.x.x always allowed",
62- allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),63+ allowedNets: httphelper.ParseTrustedProxies([]string{"100.64.0.0/10"}),
63 remoteAddr: "127.0.0.2:1234",64 remoteAddr: "127.0.0.2:1234",
64 wantStatus: http.StatusOK,65 wantStatus: http.StatusOK,
65 },66 },
66 {67 {
67 name: "IPv6 localhost always allowed",68 name: "IPv6 localhost always allowed",
68- allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),69+ allowedNets: httphelper.ParseTrustedProxies([]string{"100.64.0.0/10"}),
69 remoteAddr: "[::1]:1234",70 remoteAddr: "[::1]:1234",
70 wantStatus: http.StatusOK,71 wantStatus: http.StatusOK,
71 },72 },
72 {73 {
73 name: "trusted proxy forwards allowed client IP",74 name: "trusted proxy forwards allowed client IP",
74- allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),75+ allowedNets: httphelper.ParseTrustedProxies([]string{"100.64.0.0/10"}),
75- trustedNets: ParseTrustedProxies([]string{"10.0.0.1"}),76+ trustedNets: httphelper.ParseTrustedProxies([]string{"10.0.0.1"}),
76 remoteAddr: "10.0.0.1:1234",77 remoteAddr: "10.0.0.1:1234",
77 xff: "100.100.1.1",78 xff: "100.100.1.1",
78 wantStatus: http.StatusOK,79 wantStatus: http.StatusOK,
79 },80 },
80 {81 {
81 name: "trusted proxy forwards denied client IP",82 name: "trusted proxy forwards denied client IP",
82- allowedNets: ParseTrustedProxies([]string{"100.64.0.0/10"}),83+ allowedNets: httphelper.ParseTrustedProxies([]string{"100.64.0.0/10"}),
83- trustedNets: ParseTrustedProxies([]string{"10.0.0.1"}),84+ trustedNets: httphelper.ParseTrustedProxies([]string{"10.0.0.1"}),
84 remoteAddr: "10.0.0.1:1234",85 remoteAddr: "10.0.0.1:1234",
85 xff: "203.0.113.50",86 xff: "203.0.113.50",
86 wantStatus: http.StatusForbidden,87 wantStatus: http.StatusForbidden,
@@ -227,25 +228,25 @@ func TestProxyCIDRAllowlist(t *testing.T) {
227 },228 },
228 {229 {
229 name: "allowed IP gets redirect",230 name: "allowed IP gets redirect",
230- allowedNets: ParseTrustedProxies([]string{"173.245.48.0/20"}),231+ allowedNets: httphelper.ParseTrustedProxies([]string{"173.245.48.0/20"}),
231 remoteAddr: "173.245.48.1:1234",232 remoteAddr: "173.245.48.1:1234",
232 wantStatus: http.StatusPermanentRedirect,233 wantStatus: http.StatusPermanentRedirect,
233 },234 },
234 {235 {
235 name: "blocked IP gets 403",236 name: "blocked IP gets 403",
236- allowedNets: ParseTrustedProxies([]string{"173.245.48.0/20"}),237+ allowedNets: httphelper.ParseTrustedProxies([]string{"173.245.48.0/20"}),
237 remoteAddr: "203.0.113.50:1234",238 remoteAddr: "203.0.113.50:1234",
238 wantStatus: http.StatusForbidden,239 wantStatus: http.StatusForbidden,
239 },240 },
240 {241 {
241 name: "localhost always allowed",242 name: "localhost always allowed",
242- allowedNets: ParseTrustedProxies([]string{"173.245.48.0/20"}),243+ allowedNets: httphelper.ParseTrustedProxies([]string{"173.245.48.0/20"}),
243 remoteAddr: "127.0.0.1:1234",244 remoteAddr: "127.0.0.1:1234",
244 wantStatus: http.StatusPermanentRedirect,245 wantStatus: http.StatusPermanentRedirect,
245 },246 },
246 {247 {
247 name: "ignores XFF (uses RemoteAddr only)",248 name: "ignores XFF (uses RemoteAddr only)",
248- allowedNets: ParseTrustedProxies([]string{"173.245.48.0/20"}),249+ allowedNets: httphelper.ParseTrustedProxies([]string{"173.245.48.0/20"}),
249 remoteAddr: "203.0.113.50:1234",250 remoteAddr: "203.0.113.50:1234",
250 wantStatus: http.StatusForbidden,251 wantStatus: http.StatusForbidden,
251 },252 },
M internal/adapters/in/http/middleware/clientip.go +3 -22
@@ -56,25 +56,6 @@ func normalizeIP(raw string) string {
5656 return parsed.String()
5757 }
5858
59-// ParseTrustedProxies converts a list of IP addresses and CIDR ranges to net.IPNet.
60-// It accepts both single IPs (e.g., "10.0.0.1") and CIDR notation (e.g., "10.0.0.0/8").
61-// Single IPs are converted to /32 (IPv4) or /128 (IPv6) CIDR blocks.
62-//
63-// Deprecated: use httphelper.ParseTrustedProxies directly. This wrapper preserves
64-// the existing middleware API for callers that have not migrated yet.
65-func ParseTrustedProxies(proxies []string) []*net.IPNet {
66- return httphelper.ParseTrustedProxies(proxies)
67-}
68-
69-// IsTrustedProxy checks if the given IP address is from a trusted proxy.
70-// Returns false if trustedNets is empty or the IP cannot be parsed.
71-//
72-// Deprecated: use httphelper.IsTrustedProxy directly. This wrapper preserves
73-// the existing middleware API for callers that have not migrated yet.
74-func IsTrustedProxy(ip string, trustedNets []*net.IPNet) bool {
75- return httphelper.IsTrustedProxy(ip, trustedNets)
76-}
77-
7859 // GetClientIP extracts the client IP address from the request.
7960 // It only honors X-Forwarded-For and X-Real-IP headers when the request
8061 // originates from a trusted proxy. This prevents attackers from spoofing
@@ -90,12 +71,12 @@ func GetClientIP(r *http.Request, trustedNets []*net.IPNet) string {
9071 }
9172
9273 // Only honor proxy headers if the request comes from a trusted proxy
93- if IsTrustedProxy(remoteIP, trustedNets) {
74+ if httphelper.IsTrustedProxy(remoteIP, trustedNets) {
9475 // Cf-Connecting-IP is set by Cloudflare to the true client IP.
9576 // Only trust it when the immediate upstream is a known Cloudflare IP,
9677 // not any generic trusted proxy (e.g. local LB/nginx), because a
9778 // non-Cloudflare proxy would blindly forward a spoofed header.
98- if IsTrustedProxy(remoteIP, cloudflareNets) {
79+ if httphelper.IsTrustedProxy(remoteIP, cloudflareNets) {
9980 if cfIP := r.Header.Get("Cf-Connecting-Ip"); cfIP != "" {
10081 if ip := normalizeIP(cfIP); ip != "" {
10182 return ip
@@ -117,7 +98,7 @@ func GetClientIP(r *http.Request, trustedNets []*net.IPNet) string {
11798 }
11899
119100 for i := len(validChain) - 1; i >= 0; i-- {
120- if !IsTrustedProxy(validChain[i], trustedNets) {
101+ if !httphelper.IsTrustedProxy(validChain[i], trustedNets) {
121102 return validChain[i]
122103 }
123104 }
@@ -56,25 +56,6 @@ func normalizeIP(raw string) string {
56 return parsed.String()56 return parsed.String()
57 }57 }
58 58
59-// ParseTrustedProxies converts a list of IP addresses and CIDR ranges to net.IPNet.
60-// It accepts both single IPs (e.g., "10.0.0.1") and CIDR notation (e.g., "10.0.0.0/8").
61-// Single IPs are converted to /32 (IPv4) or /128 (IPv6) CIDR blocks.
62-//
63-// Deprecated: use httphelper.ParseTrustedProxies directly. This wrapper preserves
64-// the existing middleware API for callers that have not migrated yet.
65-func ParseTrustedProxies(proxies []string) []*net.IPNet {
66- return httphelper.ParseTrustedProxies(proxies)
67-}
68-
69-// IsTrustedProxy checks if the given IP address is from a trusted proxy.
70-// Returns false if trustedNets is empty or the IP cannot be parsed.
71-//
72-// Deprecated: use httphelper.IsTrustedProxy directly. This wrapper preserves
73-// the existing middleware API for callers that have not migrated yet.
74-func IsTrustedProxy(ip string, trustedNets []*net.IPNet) bool {
75- return httphelper.IsTrustedProxy(ip, trustedNets)
76-}
77-
78 // GetClientIP extracts the client IP address from the request.59 // GetClientIP extracts the client IP address from the request.
79 // It only honors X-Forwarded-For and X-Real-IP headers when the request60 // It only honors X-Forwarded-For and X-Real-IP headers when the request
80 // originates from a trusted proxy. This prevents attackers from spoofing61 // originates from a trusted proxy. This prevents attackers from spoofing
@@ -90,12 +71,12 @@ func GetClientIP(r *http.Request, trustedNets []*net.IPNet) string {
90 }71 }
91 72
92 // Only honor proxy headers if the request comes from a trusted proxy73 // Only honor proxy headers if the request comes from a trusted proxy
93- if IsTrustedProxy(remoteIP, trustedNets) {74+ if httphelper.IsTrustedProxy(remoteIP, trustedNets) {
94 // Cf-Connecting-IP is set by Cloudflare to the true client IP.75 // Cf-Connecting-IP is set by Cloudflare to the true client IP.
95 // Only trust it when the immediate upstream is a known Cloudflare IP,76 // Only trust it when the immediate upstream is a known Cloudflare IP,
96 // not any generic trusted proxy (e.g. local LB/nginx), because a77 // not any generic trusted proxy (e.g. local LB/nginx), because a
97 // non-Cloudflare proxy would blindly forward a spoofed header.78 // non-Cloudflare proxy would blindly forward a spoofed header.
98- if IsTrustedProxy(remoteIP, cloudflareNets) {79+ if httphelper.IsTrustedProxy(remoteIP, cloudflareNets) {
99 if cfIP := r.Header.Get("Cf-Connecting-Ip"); cfIP != "" {80 if cfIP := r.Header.Get("Cf-Connecting-Ip"); cfIP != "" {
100 if ip := normalizeIP(cfIP); ip != "" {81 if ip := normalizeIP(cfIP); ip != "" {
101 return ip82 return ip
@@ -117,7 +98,7 @@ func GetClientIP(r *http.Request, trustedNets []*net.IPNet) string {
117 }98 }
118 99
119 for i := len(validChain) - 1; i >= 0; i-- {100 for i := len(validChain) - 1; i >= 0; i-- {
120- if !IsTrustedProxy(validChain[i], trustedNets) {101+ if !httphelper.IsTrustedProxy(validChain[i], trustedNets) {
121 return validChain[i]102 return validChain[i]
122 }103 }
123 }104 }
M internal/adapters/in/http/middleware/clientip_test.go +8 -7
@@ -6,15 +6,16 @@ import (
66 "net/http/httptest"
77 "testing"
88
9+ "github.com/bnema/gordon/internal/adapters/in/http/httphelper"
910 "github.com/stretchr/testify/assert"
1011 )
1112
1213 func TestGetClientIP(t *testing.T) {
1314 // Parse trusted proxies for tests
14- trustedNets := ParseTrustedProxies([]string{"127.0.0.1", "10.0.0.0/8"})
15+ trustedNets := httphelper.ParseTrustedProxies([]string{"127.0.0.1", "10.0.0.0/8"})
1516 noTrustedNets := []*net.IPNet{}
1617 // Include a Cloudflare IP in trusted proxies to test Cf-Connecting-Ip
17- trustedWithCF := ParseTrustedProxies([]string{"127.0.0.1", "10.0.0.0/8", "173.245.48.0/20"})
18+ trustedWithCF := httphelper.ParseTrustedProxies([]string{"127.0.0.1", "10.0.0.0/8", "173.245.48.0/20"})
1819
1920 tests := []struct {
2021 name string
@@ -216,8 +217,8 @@ func TestParseTrustedProxies(t *testing.T) {
216217
217218 for _, tt := range tests {
218219 t.Run(tt.name, func(t *testing.T) {
219- nets := ParseTrustedProxies(tt.proxies)
220- got := IsTrustedProxy(tt.testIP, nets)
220+ nets := httphelper.ParseTrustedProxies(tt.proxies)
221+ got := httphelper.IsTrustedProxy(tt.testIP, nets)
221222 assert.Equal(t, tt.want, got)
222223 })
223224 }
@@ -245,20 +246,20 @@ func TestIsTrustedProxy(t *testing.T) {
245246 {
246247 name: "invalid IP",
247248 ip: "not-an-ip",
248- trustedNets: ParseTrustedProxies([]string{"192.168.1.0/24"}),
249+ trustedNets: httphelper.ParseTrustedProxies([]string{"192.168.1.0/24"}),
249250 want: false,
250251 },
251252 {
252253 name: "empty IP",
253254 ip: "",
254- trustedNets: ParseTrustedProxies([]string{"192.168.1.0/24"}),
255+ trustedNets: httphelper.ParseTrustedProxies([]string{"192.168.1.0/24"}),
255256 want: false,
256257 },
257258 }
258259
259260 for _, tt := range tests {
260261 t.Run(tt.name, func(t *testing.T) {
261- got := IsTrustedProxy(tt.ip, tt.trustedNets)
262+ got := httphelper.IsTrustedProxy(tt.ip, tt.trustedNets)
262263 assert.Equal(t, tt.want, got)
263264 })
264265 }
@@ -6,15 +6,16 @@ import (
6 "net/http/httptest"6 "net/http/httptest"
7 "testing"7 "testing"
8 8
9+ "github.com/bnema/gordon/internal/adapters/in/http/httphelper"
9 "github.com/stretchr/testify/assert"10 "github.com/stretchr/testify/assert"
10 )11 )
11 12
12 func TestGetClientIP(t *testing.T) {13 func TestGetClientIP(t *testing.T) {
13 // Parse trusted proxies for tests14 // Parse trusted proxies for tests
14- trustedNets := ParseTrustedProxies([]string{"127.0.0.1", "10.0.0.0/8"})15+ trustedNets := httphelper.ParseTrustedProxies([]string{"127.0.0.1", "10.0.0.0/8"})
15 noTrustedNets := []*net.IPNet{}16 noTrustedNets := []*net.IPNet{}
16 // Include a Cloudflare IP in trusted proxies to test Cf-Connecting-Ip17 // Include a Cloudflare IP in trusted proxies to test Cf-Connecting-Ip
17- trustedWithCF := ParseTrustedProxies([]string{"127.0.0.1", "10.0.0.0/8", "173.245.48.0/20"})18+ trustedWithCF := httphelper.ParseTrustedProxies([]string{"127.0.0.1", "10.0.0.0/8", "173.245.48.0/20"})
18 19
19 tests := []struct {20 tests := []struct {
20 name string21 name string
@@ -216,8 +217,8 @@ func TestParseTrustedProxies(t *testing.T) {
216 217
217 for _, tt := range tests {218 for _, tt := range tests {
218 t.Run(tt.name, func(t *testing.T) {219 t.Run(tt.name, func(t *testing.T) {
219- nets := ParseTrustedProxies(tt.proxies)220+ nets := httphelper.ParseTrustedProxies(tt.proxies)
220- got := IsTrustedProxy(tt.testIP, nets)221+ got := httphelper.IsTrustedProxy(tt.testIP, nets)
221 assert.Equal(t, tt.want, got)222 assert.Equal(t, tt.want, got)
222 })223 })
223 }224 }
@@ -245,20 +246,20 @@ func TestIsTrustedProxy(t *testing.T) {
245 {246 {
246 name: "invalid IP",247 name: "invalid IP",
247 ip: "not-an-ip",248 ip: "not-an-ip",
248- trustedNets: ParseTrustedProxies([]string{"192.168.1.0/24"}),249+ trustedNets: httphelper.ParseTrustedProxies([]string{"192.168.1.0/24"}),
249 want: false,250 want: false,
250 },251 },
251 {252 {
252 name: "empty IP",253 name: "empty IP",
253 ip: "",254 ip: "",
254- trustedNets: ParseTrustedProxies([]string{"192.168.1.0/24"}),255+ trustedNets: httphelper.ParseTrustedProxies([]string{"192.168.1.0/24"}),
255 want: false,256 want: false,
256 },257 },
257 }258 }
258 259
259 for _, tt := range tests {260 for _, tt := range tests {
260 t.Run(tt.name, func(t *testing.T) {261 t.Run(tt.name, func(t *testing.T) {
261- got := IsTrustedProxy(tt.ip, tt.trustedNets)262+ got := httphelper.IsTrustedProxy(tt.ip, tt.trustedNets)
262 assert.Equal(t, tt.want, got)263 assert.Equal(t, tt.want, got)
263 })264 })
264 }265 }
M internal/app/run.go +7 -14
@@ -89,7 +89,6 @@ type Config struct {
8989 Port int `mapstructure:"port"`
9090 RegistryPort int `mapstructure:"registry_port"`
9191 GordonDomain string `mapstructure:"gordon_domain"`
92- RegistryDomain string `mapstructure:"registry_domain"` // Deprecated: use gordon_domain
9392 TLSPort int `mapstructure:"tls_port"`
9493 TLSCertFile string `mapstructure:"tls_cert_file"`
9594 TLSKeyFile string `mapstructure:"tls_key_file"`
@@ -315,11 +314,6 @@ func initConfig(configPath string) (*viper.Viper, Config, error) {
315314 return nil, Config{}, fmt.Errorf("failed to unmarshal config: %w", err)
316315 }
317316
318- // Normalize domain config: prefer gordon_domain over registry_domain
319- if cfg.Server.GordonDomain != "" {
320- cfg.Server.RegistryDomain = cfg.Server.GordonDomain
321- }
322-
323317 return v, cfg, nil
324318 }
325319
@@ -1386,7 +1380,7 @@ func buildProxyConfig(cfg Config, log zerowrap.Logger) (*proxyConfigResult, erro
13861380
13871381 return &proxyConfigResult{
13881382 proxyConfig: proxy.Config{
1389- RegistryDomain: cfg.Server.RegistryDomain,
1383+ RegistryDomain: cfg.Server.GordonDomain,
13901384 RegistryPort: cfg.Server.RegistryPort,
13911385 MaxBodySize: maxProxyBodySize,
13921386 MaxResponseSize: maxProxyResponseSize,
@@ -1421,9 +1415,11 @@ func createContainerService(ctx context.Context, v *viper.Viper, cfg Config, svc
14211415 defaultNanoCPUs = int64(cfg.Containers.CPULimit * 1e9)
14221416 }
14231417
1418+ attachmentConfig := svc.configSvc.GetAttachmentConfig()
1419+
14241420 containerConfig := container.Config{
14251421 RegistryAuthEnabled: cfg.Auth.Enabled,
1426- RegistryDomain: cfg.Server.RegistryDomain,
1422+ RegistryDomain: cfg.Server.GordonDomain,
14271423 RegistryPort: cfg.Server.RegistryPort,
14281424 InternalRegistryUsername: svc.internalRegUser,
14291425 InternalRegistryPassword: svc.internalRegPass,
@@ -1433,8 +1429,8 @@ func createContainerService(ctx context.Context, v *viper.Viper, cfg Config, svc
14331429 VolumePreserve: v.GetBool("volumes.preserve"),
14341430 NetworkIsolation: v.GetBool("network_isolation.enabled"),
14351431 NetworkPrefix: v.GetString("network_isolation.network_prefix"),
1436- NetworkGroups: svc.configSvc.GetNetworkGroups(),
1437- Attachments: svc.configSvc.GetAttachments(),
1432+ NetworkGroups: attachmentConfig.NetworkGroups,
1433+ Attachments: attachmentConfig.Attachments,
14381434 ReadinessDelay: v.GetDuration("deploy.readiness_delay"),
14391435 ReadinessMode: v.GetString("deploy.readiness_mode"),
14401436 HealthTimeout: v.GetDuration("deploy.health_timeout"),
@@ -1547,7 +1543,7 @@ func registerEventHandlers(ctx context.Context, svc *services, cfg Config) (func
15471543 }
15481544
15491545 // Auto-route handler for creating routes from image labels
1550- autoRouteHandler := container.NewAutoRouteHandler(ctx, svc.configSvc, svc.containerSvc, svc.blobStorage, cfg.Server.RegistryDomain).
1546+ autoRouteHandler := container.NewAutoRouteHandler(ctx, svc.configSvc, svc.containerSvc, svc.blobStorage, cfg.Server.GordonDomain).
15511547 WithEnvExtractor(svc.runtime, svc.envDir)
15521548
15531549 // Preview handler for creating preview environments from tagged images
@@ -1617,9 +1613,6 @@ func setupConfigHotReload(ctx context.Context, v *viper.Viper, svc *services, lo
16171613 log.Error().Err(err).Msg("failed to unmarshal config on reload")
16181614 return
16191615 }
1620- if reloadCfg.Server.GordonDomain != "" {
1621- reloadCfg.Server.RegistryDomain = reloadCfg.Server.GordonDomain
1622- }
16231616 reloadedProxy, err := buildProxyConfig(reloadCfg, log)
16241617 if err != nil {
16251618 log.Error().Err(err).Msg("failed to parse proxy config on reload")
@@ -89,7 +89,6 @@ type Config struct {
89 Port int `mapstructure:"port"`89 Port int `mapstructure:"port"`
90 RegistryPort int `mapstructure:"registry_port"`90 RegistryPort int `mapstructure:"registry_port"`
91 GordonDomain string `mapstructure:"gordon_domain"`91 GordonDomain string `mapstructure:"gordon_domain"`
92- RegistryDomain string `mapstructure:"registry_domain"` // Deprecated: use gordon_domain
93 TLSPort int `mapstructure:"tls_port"`92 TLSPort int `mapstructure:"tls_port"`
94 TLSCertFile string `mapstructure:"tls_cert_file"`93 TLSCertFile string `mapstructure:"tls_cert_file"`
95 TLSKeyFile string `mapstructure:"tls_key_file"`94 TLSKeyFile string `mapstructure:"tls_key_file"`
@@ -315,11 +314,6 @@ func initConfig(configPath string) (*viper.Viper, Config, error) {
315 return nil, Config{}, fmt.Errorf("failed to unmarshal config: %w", err)314 return nil, Config{}, fmt.Errorf("failed to unmarshal config: %w", err)
316 }315 }
317 316
318- // Normalize domain config: prefer gordon_domain over registry_domain
319- if cfg.Server.GordonDomain != "" {
320- cfg.Server.RegistryDomain = cfg.Server.GordonDomain
321- }
322-
323 return v, cfg, nil317 return v, cfg, nil
324 }318 }
325 319
@@ -1386,7 +1380,7 @@ func buildProxyConfig(cfg Config, log zerowrap.Logger) (*proxyConfigResult, erro
1386 1380
1387 return &proxyConfigResult{1381 return &proxyConfigResult{
1388 proxyConfig: proxy.Config{1382 proxyConfig: proxy.Config{
1389- RegistryDomain: cfg.Server.RegistryDomain,1383+ RegistryDomain: cfg.Server.GordonDomain,
1390 RegistryPort: cfg.Server.RegistryPort,1384 RegistryPort: cfg.Server.RegistryPort,
1391 MaxBodySize: maxProxyBodySize,1385 MaxBodySize: maxProxyBodySize,
1392 MaxResponseSize: maxProxyResponseSize,1386 MaxResponseSize: maxProxyResponseSize,
@@ -1421,9 +1415,11 @@ func createContainerService(ctx context.Context, v *viper.Viper, cfg Config, svc
1421 defaultNanoCPUs = int64(cfg.Containers.CPULimit * 1e9)1415 defaultNanoCPUs = int64(cfg.Containers.CPULimit * 1e9)
1422 }1416 }
1423 1417
1418+ attachmentConfig := svc.configSvc.GetAttachmentConfig()
1419+
1424 containerConfig := container.Config{1420 containerConfig := container.Config{
1425 RegistryAuthEnabled: cfg.Auth.Enabled,1421 RegistryAuthEnabled: cfg.Auth.Enabled,
1426- RegistryDomain: cfg.Server.RegistryDomain,1422+ RegistryDomain: cfg.Server.GordonDomain,
1427 RegistryPort: cfg.Server.RegistryPort,1423 RegistryPort: cfg.Server.RegistryPort,
1428 InternalRegistryUsername: svc.internalRegUser,1424 InternalRegistryUsername: svc.internalRegUser,
1429 InternalRegistryPassword: svc.internalRegPass,1425 InternalRegistryPassword: svc.internalRegPass,
@@ -1433,8 +1429,8 @@ func createContainerService(ctx context.Context, v *viper.Viper, cfg Config, svc
1433 VolumePreserve: v.GetBool("volumes.preserve"),1429 VolumePreserve: v.GetBool("volumes.preserve"),
1434 NetworkIsolation: v.GetBool("network_isolation.enabled"),1430 NetworkIsolation: v.GetBool("network_isolation.enabled"),
1435 NetworkPrefix: v.GetString("network_isolation.network_prefix"),1431 NetworkPrefix: v.GetString("network_isolation.network_prefix"),
1436- NetworkGroups: svc.configSvc.GetNetworkGroups(),1432+ NetworkGroups: attachmentConfig.NetworkGroups,
1437- Attachments: svc.configSvc.GetAttachments(),1433+ Attachments: attachmentConfig.Attachments,
1438 ReadinessDelay: v.GetDuration("deploy.readiness_delay"),1434 ReadinessDelay: v.GetDuration("deploy.readiness_delay"),
1439 ReadinessMode: v.GetString("deploy.readiness_mode"),1435 ReadinessMode: v.GetString("deploy.readiness_mode"),
1440 HealthTimeout: v.GetDuration("deploy.health_timeout"),1436 HealthTimeout: v.GetDuration("deploy.health_timeout"),
@@ -1547,7 +1543,7 @@ func registerEventHandlers(ctx context.Context, svc *services, cfg Config) (func
1547 }1543 }
1548 1544
1549 // Auto-route handler for creating routes from image labels1545 // Auto-route handler for creating routes from image labels
1550- autoRouteHandler := container.NewAutoRouteHandler(ctx, svc.configSvc, svc.containerSvc, svc.blobStorage, cfg.Server.RegistryDomain).1546+ autoRouteHandler := container.NewAutoRouteHandler(ctx, svc.configSvc, svc.containerSvc, svc.blobStorage, cfg.Server.GordonDomain).
1551 WithEnvExtractor(svc.runtime, svc.envDir)1547 WithEnvExtractor(svc.runtime, svc.envDir)
1552 1548
1553 // Preview handler for creating preview environments from tagged images1549 // Preview handler for creating preview environments from tagged images
@@ -1617,9 +1613,6 @@ func setupConfigHotReload(ctx context.Context, v *viper.Viper, svc *services, lo
1617 log.Error().Err(err).Msg("failed to unmarshal config on reload")1613 log.Error().Err(err).Msg("failed to unmarshal config on reload")
1618 return1614 return
1619 }1615 }
1620- if reloadCfg.Server.GordonDomain != "" {
1621- reloadCfg.Server.RegistryDomain = reloadCfg.Server.GordonDomain
1622- }
1623 reloadedProxy, err := buildProxyConfig(reloadCfg, log)1616 reloadedProxy, err := buildProxyConfig(reloadCfg, log)
1624 if err != nil {1617 if err != nil {
1625 log.Error().Err(err).Msg("failed to parse proxy config on reload")1618 log.Error().Err(err).Msg("failed to parse proxy config on reload")
M internal/boundaries/out/attachment_config.go +0 -3
@@ -20,9 +20,6 @@ type AttachmentConfigProvider interface {
2020 // network groups, read under a single lock to prevent cross-field races.
2121 GetAttachmentConfig() AttachmentConfigSnapshot
2222
23- // GetAttachments returns a deep copy of the current attachment config: domain/group -> []image.
24- GetAttachments() map[string][]string
25-
2623 // GetNetworkGroups returns a deep copy of the current network group config: group -> []domain.
2724 GetNetworkGroups() map[string][]string
2825 }
@@ -20,9 +20,6 @@ type AttachmentConfigProvider interface {
20 // network groups, read under a single lock to prevent cross-field races.20 // network groups, read under a single lock to prevent cross-field races.
21 GetAttachmentConfig() AttachmentConfigSnapshot21 GetAttachmentConfig() AttachmentConfigSnapshot
22 22
23- // GetAttachments returns a deep copy of the current attachment config: domain/group -> []image.
24- GetAttachments() map[string][]string
25-
26 // GetNetworkGroups returns a deep copy of the current network group config: group -> []domain.23 // GetNetworkGroups returns a deep copy of the current network group config: group -> []domain.
27 GetNetworkGroups() map[string][]string24 GetNetworkGroups() map[string][]string
28 }25 }
M internal/boundaries/out/mocks/mock_attachment_config_provider.go +0 -46
@@ -80,52 +80,6 @@ func (_c *MockAttachmentConfigProvider_GetAttachmentConfig_Call) RunAndReturn(ru
8080 return _c
8181 }
8282
83-// GetAttachments provides a mock function for the type MockAttachmentConfigProvider
84-func (_mock *MockAttachmentConfigProvider) GetAttachments() map[string][]string {
85- ret := _mock.Called()
86-
87- if len(ret) == 0 {
88- panic("no return value specified for GetAttachments")
89- }
90-
91- var r0 map[string][]string
92- if returnFunc, ok := ret.Get(0).(func() map[string][]string); ok {
93- r0 = returnFunc()
94- } else {
95- if ret.Get(0) != nil {
96- r0 = ret.Get(0).(map[string][]string)
97- }
98- }
99- return r0
100-}
101-
102-// MockAttachmentConfigProvider_GetAttachments_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAttachments'
103-type MockAttachmentConfigProvider_GetAttachments_Call struct {
104- *mock.Call
105-}
106-
107-// GetAttachments is a helper method to define mock.On call
108-func (_e *MockAttachmentConfigProvider_Expecter) GetAttachments() *MockAttachmentConfigProvider_GetAttachments_Call {
109- return &MockAttachmentConfigProvider_GetAttachments_Call{Call: _e.mock.On("GetAttachments")}
110-}
111-
112-func (_c *MockAttachmentConfigProvider_GetAttachments_Call) Run(run func()) *MockAttachmentConfigProvider_GetAttachments_Call {
113- _c.Call.Run(func(args mock.Arguments) {
114- run()
115- })
116- return _c
117-}
118-
119-func (_c *MockAttachmentConfigProvider_GetAttachments_Call) Return(stringToStrings map[string][]string) *MockAttachmentConfigProvider_GetAttachments_Call {
120- _c.Call.Return(stringToStrings)
121- return _c
122-}
123-
124-func (_c *MockAttachmentConfigProvider_GetAttachments_Call) RunAndReturn(run func() map[string][]string) *MockAttachmentConfigProvider_GetAttachments_Call {
125- _c.Call.Return(run)
126- return _c
127-}
128-
12983 // GetNetworkGroups provides a mock function for the type MockAttachmentConfigProvider
13084 func (_mock *MockAttachmentConfigProvider) GetNetworkGroups() map[string][]string {
13185 ret := _mock.Called()
@@ -80,52 +80,6 @@ func (_c *MockAttachmentConfigProvider_GetAttachmentConfig_Call) RunAndReturn(ru
80 return _c80 return _c
81 }81 }
82 82
83-// GetAttachments provides a mock function for the type MockAttachmentConfigProvider
84-func (_mock *MockAttachmentConfigProvider) GetAttachments() map[string][]string {
85- ret := _mock.Called()
86-
87- if len(ret) == 0 {
88- panic("no return value specified for GetAttachments")
89- }
90-
91- var r0 map[string][]string
92- if returnFunc, ok := ret.Get(0).(func() map[string][]string); ok {
93- r0 = returnFunc()
94- } else {
95- if ret.Get(0) != nil {
96- r0 = ret.Get(0).(map[string][]string)
97- }
98- }
99- return r0
100-}
101-
102-// MockAttachmentConfigProvider_GetAttachments_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAttachments'
103-type MockAttachmentConfigProvider_GetAttachments_Call struct {
104- *mock.Call
105-}
106-
107-// GetAttachments is a helper method to define mock.On call
108-func (_e *MockAttachmentConfigProvider_Expecter) GetAttachments() *MockAttachmentConfigProvider_GetAttachments_Call {
109- return &MockAttachmentConfigProvider_GetAttachments_Call{Call: _e.mock.On("GetAttachments")}
110-}
111-
112-func (_c *MockAttachmentConfigProvider_GetAttachments_Call) Run(run func()) *MockAttachmentConfigProvider_GetAttachments_Call {
113- _c.Call.Run(func(args mock.Arguments) {
114- run()
115- })
116- return _c
117-}
118-
119-func (_c *MockAttachmentConfigProvider_GetAttachments_Call) Return(stringToStrings map[string][]string) *MockAttachmentConfigProvider_GetAttachments_Call {
120- _c.Call.Return(stringToStrings)
121- return _c
122-}
123-
124-func (_c *MockAttachmentConfigProvider_GetAttachments_Call) RunAndReturn(run func() map[string][]string) *MockAttachmentConfigProvider_GetAttachments_Call {
125- _c.Call.Return(run)
126- return _c
127-}
128-
129 // GetNetworkGroups provides a mock function for the type MockAttachmentConfigProvider83 // GetNetworkGroups provides a mock function for the type MockAttachmentConfigProvider
130 func (_mock *MockAttachmentConfigProvider) GetNetworkGroups() map[string][]string {84 func (_mock *MockAttachmentConfigProvider) GetNetworkGroups() map[string][]string {
131 ret := _mock.Called()85 ret := _mock.Called()
M internal/usecase/config/service.go +0 -13
@@ -933,19 +933,6 @@ func (s *Service) GetAttachmentConfig() out.AttachmentConfigSnapshot {
933933 }
934934 }
935935
936-// GetAttachments returns attachment configuration.
937-// Deprecated: Use GetAllAttachments instead.
938-func (s *Service) GetAttachments() map[string][]string {
939- s.mu.RLock()
940- defer s.mu.RUnlock()
941-
942- result := make(map[string][]string)
943- for k, v := range s.config.Attachments {
944- result[k] = append([]string{}, v...)
945- }
946- return result
947-}
948-
949936 // GetAllAttachments returns all configured attachments.
950937 func (s *Service) GetAllAttachments(_ context.Context) map[string][]string {
951938 s.mu.RLock()
@@ -933,19 +933,6 @@ func (s *Service) GetAttachmentConfig() out.AttachmentConfigSnapshot {
933 }933 }
934 }934 }
935 935
936-// GetAttachments returns attachment configuration.
937-// Deprecated: Use GetAllAttachments instead.
938-func (s *Service) GetAttachments() map[string][]string {
939- s.mu.RLock()
940- defer s.mu.RUnlock()
941-
942- result := make(map[string][]string)
943- for k, v := range s.config.Attachments {
944- result[k] = append([]string{}, v...)
945- }
946- return result
947-}
948-
949 // GetAllAttachments returns all configured attachments.936 // GetAllAttachments returns all configured attachments.
950 func (s *Service) GetAllAttachments(_ context.Context) map[string][]string {937 func (s *Service) GetAllAttachments(_ context.Context) map[string][]string {
951 s.mu.RLock()938 s.mu.RLock()
M internal/usecase/config/service_test.go +0 -18
@@ -469,24 +469,6 @@ func TestService_GetNetworkGroups(t *testing.T) {
469469 assert.ElementsMatch(t, []string{"api.example.com", "db.example.com"}, groups["backend"])
470470 }
471471
472-func TestService_GetAttachments(t *testing.T) {
473- v := viper.New()
474- v.Set("attachments", map[string]interface{}{
475- "app.example.com": []interface{}{"redis:latest", "postgres:18"},
476- })
477-
478- eventBus := mocks.NewMockEventPublisher(t)
479- svc := NewService(v, eventBus)
480- ctx := testContext()
481-
482- _ = svc.Load(ctx)
483-
484- attachments := svc.GetAttachments()
485-
486- assert.Len(t, attachments, 1)
487- assert.ElementsMatch(t, []string{"redis:latest", "postgres:18"}, attachments["app.example.com"])
488-}
489-
490472 func TestService_GetAttachmentConfig(t *testing.T) {
491473 v := viper.New()
492474 v.Set("attachments", map[string]interface{}{
@@ -469,24 +469,6 @@ func TestService_GetNetworkGroups(t *testing.T) {
469 assert.ElementsMatch(t, []string{"api.example.com", "db.example.com"}, groups["backend"])469 assert.ElementsMatch(t, []string{"api.example.com", "db.example.com"}, groups["backend"])
470 }470 }
471 471
472-func TestService_GetAttachments(t *testing.T) {
473- v := viper.New()
474- v.Set("attachments", map[string]interface{}{
475- "app.example.com": []interface{}{"redis:latest", "postgres:18"},
476- })
477-
478- eventBus := mocks.NewMockEventPublisher(t)
479- svc := NewService(v, eventBus)
480- ctx := testContext()
481-
482- _ = svc.Load(ctx)
483-
484- attachments := svc.GetAttachments()
485-
486- assert.Len(t, attachments, 1)
487- assert.ElementsMatch(t, []string{"redis:latest", "postgres:18"}, attachments["app.example.com"])
488-}
489-
490 func TestService_GetAttachmentConfig(t *testing.T) {472 func TestService_GetAttachmentConfig(t *testing.T) {
491 v := viper.New()473 v := viper.New()
492 v.Set("attachments", map[string]interface{}{474 v.Set("attachments", map[string]interface{}{

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close