refactor: remove deprecated compatibility shims
- commit
- 18a333b3ff743c8f4e070f5decb697e645bf531b
- author
- brice · · 2026-04-08 17:53 UTC
- parent
- 1489816
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· 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 { | |||
| 16 | 16 | } | |
| 17 | 17 | ||
| 18 | 18 | var uiAdoptionExpectations = []uiAdoptionExpectation{ | |
| 19 | - | { | |
| 20 | - | family: "server", | |
| 21 | - | file: "serve.go", | |
| 22 | - | functions: []string{"newStartCmd"}, | |
| 23 | - | }, | |
| 24 | 19 | { | |
| 25 | 20 | family: "root/server", | |
| 26 | 21 | 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: | |||
| 68 | 68 | serveCmd.GroupID = groupServer | |
| 69 | 69 | rootCmd.AddCommand(serveCmd) | |
| 70 | 70 | ||
| 71 | - | startCmd := newStartCmd() // Deprecated alias for serve | |
| 72 | - | startCmd.GroupID = groupServer | |
| 73 | - | rootCmd.AddCommand(startCmd) | |
| 74 | - | ||
| 75 | 71 | authCmd := newAuthCmd() | |
| 76 | 72 | authCmd.GroupID = groupServer | |
| 77 | 73 | rootCmd.AddCommand(authCmd) | |
| @@ -68,10 +68,6 @@ Commands are organized by where they run: | |||||
| 68 | serveCmd.GroupID = groupServer | 68 | 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 = groupServer | 72 | 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 | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| 5 | - | "os" | |
| 6 | 5 | ||
| 7 | 6 | "github.com/bnema/gordon/internal/app" | |
| 8 | 7 | ||
| @@ -27,27 +26,3 @@ func newServeCmd() *cobra.Command { | |||
| 27 | 26 | ||
| 28 | 27 | 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 | - | } | |
| @@ -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 cmd | 27 | 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 ( | |||
| 9 | 9 | ||
| 10 | 10 | "github.com/bnema/zerowrap" | |
| 11 | 11 | ||
| 12 | + | "github.com/bnema/gordon/internal/adapters/in/http/httphelper" | |
| 12 | 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 | 46 | // cidr.go (same package) — no separate loopback definition needed. | |
| 46 | 47 | if excludeHealthChecks { | |
| 47 | 48 | if strings.HasPrefix(r.UserAgent(), out.HealthCheckUserAgentPrefix) || | |
| 48 | - | IsTrustedProxy(clientIP, localhostNets) { | |
| 49 | + | httphelper.IsTrustedProxy(clientIP, localhostNets) { | |
| 49 | 50 | return | |
| 50 | 51 | } | |
| 51 | 52 | } | |
| @@ -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 | return | 50 | 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 | |||
| 27 | 27 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
| 28 | 28 | clientIP := ipExtractor(r) | |
| 29 | 29 | ||
| 30 | - | if IsTrustedProxy(clientIP, localhostNets) { | |
| 30 | + | if httphelper.IsTrustedProxy(clientIP, localhostNets) { | |
| 31 | 31 | next.ServeHTTP(w, r) | |
| 32 | 32 | return | |
| 33 | 33 | } | |
| 34 | 34 | ||
| 35 | - | if IsTrustedProxy(clientIP, allowedNets) { | |
| 35 | + | if httphelper.IsTrustedProxy(clientIP, allowedNets) { | |
| 36 | 36 | next.ServeHTTP(w, r) | |
| 37 | 37 | 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 | 54 | // RegistryCIDRAllowlist returns middleware that restricts access to the given CIDR ranges. | |
| 80 | 55 | // Localhost is always allowed so Gordon can pull from its own registry. | |
| 81 | 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 | 85 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
| 111 | 86 | if !forceAll { | |
| 112 | - | remoteIP := ExtractRemoteIP(r.RemoteAddr) | |
| 113 | - | if IsTrustedOrLocal(remoteIP, proxyNets) { | |
| 87 | + | remoteIP := httphelper.ExtractRemoteIP(r.RemoteAddr) | |
| 88 | + | if httphelper.IsTrustedOrLocal(remoteIP, proxyNets) { | |
| 114 | 89 | next.ServeHTTP(w, r) | |
| 115 | 90 | return | |
| 116 | 91 | } | |
| @@ -134,7 +109,7 @@ func httpsRedirectTarget(host, requestURI string, httpPort, tlsPort int) string | |||
| 134 | 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 | 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 | 118 | // An empty allowedNets slice is a no-op (all traffic passes through). | |
| 144 | 119 | func ProxyCIDRAllowlist(allowedNets []*net.IPNet, log zerowrap.Logger) func(http.Handler) http.Handler { | |
| 145 | 120 | return cidrAllowlist(allowedNets, func(r *http.Request) string { | |
| 146 | - | return ExtractRemoteIP(r.RemoteAddr) | |
| 121 | + | return httphelper.ExtractRemoteIP(r.RemoteAddr) | |
| 147 | 122 | }, "proxy origin", log) | |
| 148 | 123 | } | |
| @@ -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 | return | 32 | 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 | return | 37 | 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 | return | 90 | 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 ( | |||
| 9 | 9 | "testing" | |
| 10 | 10 | ||
| 11 | 11 | "github.com/bnema/gordon/internal/adapters/dto" | |
| 12 | + | "github.com/bnema/gordon/internal/adapters/in/http/httphelper" | |
| 12 | 13 | "github.com/stretchr/testify/assert" | |
| 13 | 14 | "github.com/stretchr/testify/require" | |
| 14 | 15 | ) | |
| @@ -41,46 +42,46 @@ func TestRegistryCIDRAllowlist(t *testing.T) { | |||
| 41 | 42 | }, | |
| 42 | 43 | { | |
| 43 | 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 | 46 | remoteAddr: "100.100.1.1:1234", | |
| 46 | 47 | wantStatus: http.StatusOK, | |
| 47 | 48 | }, | |
| 48 | 49 | { | |
| 49 | 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 | 52 | remoteAddr: "203.0.113.50:1234", | |
| 52 | 53 | wantStatus: http.StatusForbidden, | |
| 53 | 54 | }, | |
| 54 | 55 | { | |
| 55 | 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 | 58 | remoteAddr: "127.0.0.1:1234", | |
| 58 | 59 | wantStatus: http.StatusOK, | |
| 59 | 60 | }, | |
| 60 | 61 | { | |
| 61 | 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 | 64 | remoteAddr: "127.0.0.2:1234", | |
| 64 | 65 | wantStatus: http.StatusOK, | |
| 65 | 66 | }, | |
| 66 | 67 | { | |
| 67 | 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 | 70 | remoteAddr: "[::1]:1234", | |
| 70 | 71 | wantStatus: http.StatusOK, | |
| 71 | 72 | }, | |
| 72 | 73 | { | |
| 73 | 74 | 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"}), | |
| 76 | 77 | remoteAddr: "10.0.0.1:1234", | |
| 77 | 78 | xff: "100.100.1.1", | |
| 78 | 79 | wantStatus: http.StatusOK, | |
| 79 | 80 | }, | |
| 80 | 81 | { | |
| 81 | 82 | 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"}), | |
| 84 | 85 | remoteAddr: "10.0.0.1:1234", | |
| 85 | 86 | xff: "203.0.113.50", | |
| 86 | 87 | wantStatus: http.StatusForbidden, | |
| @@ -227,25 +228,25 @@ func TestProxyCIDRAllowlist(t *testing.T) { | |||
| 227 | 228 | }, | |
| 228 | 229 | { | |
| 229 | 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 | 232 | remoteAddr: "173.245.48.1:1234", | |
| 232 | 233 | wantStatus: http.StatusPermanentRedirect, | |
| 233 | 234 | }, | |
| 234 | 235 | { | |
| 235 | 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 | 238 | remoteAddr: "203.0.113.50:1234", | |
| 238 | 239 | wantStatus: http.StatusForbidden, | |
| 239 | 240 | }, | |
| 240 | 241 | { | |
| 241 | 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 | 244 | remoteAddr: "127.0.0.1:1234", | |
| 244 | 245 | wantStatus: http.StatusPermanentRedirect, | |
| 245 | 246 | }, | |
| 246 | 247 | { | |
| 247 | 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 | 250 | remoteAddr: "203.0.113.50:1234", | |
| 250 | 251 | wantStatus: http.StatusForbidden, | |
| 251 | 252 | }, | |
| @@ -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 { | |||
| 56 | 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 | 59 | // GetClientIP extracts the client IP address from the request. | |
| 79 | 60 | // It only honors X-Forwarded-For and X-Real-IP headers when the request | |
| 80 | 61 | // 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 | 73 | // Only honor proxy headers if the request comes from a trusted proxy | |
| 93 | - | if IsTrustedProxy(remoteIP, trustedNets) { | |
| 74 | + | if httphelper.IsTrustedProxy(remoteIP, trustedNets) { | |
| 94 | 75 | // Cf-Connecting-IP is set by Cloudflare to the true client IP. | |
| 95 | 76 | // Only trust it when the immediate upstream is a known Cloudflare IP, | |
| 96 | 77 | // not any generic trusted proxy (e.g. local LB/nginx), because a | |
| 97 | 78 | // non-Cloudflare proxy would blindly forward a spoofed header. | |
| 98 | - | if IsTrustedProxy(remoteIP, cloudflareNets) { | |
| 79 | + | if httphelper.IsTrustedProxy(remoteIP, cloudflareNets) { | |
| 99 | 80 | if cfIP := r.Header.Get("Cf-Connecting-Ip"); cfIP != "" { | |
| 100 | 81 | if ip := normalizeIP(cfIP); ip != "" { | |
| 101 | 82 | return ip | |
| @@ -117,7 +98,7 @@ func GetClientIP(r *http.Request, trustedNets []*net.IPNet) string { | |||
| 117 | 98 | } | |
| 118 | 99 | ||
| 119 | 100 | for i := len(validChain) - 1; i >= 0; i-- { | |
| 120 | - | if !IsTrustedProxy(validChain[i], trustedNets) { | |
| 101 | + | if !httphelper.IsTrustedProxy(validChain[i], trustedNets) { | |
| 121 | 102 | return validChain[i] | |
| 122 | 103 | } | |
| 123 | 104 | } | |
| @@ -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 request | 60 | // It only honors X-Forwarded-For and X-Real-IP headers when the request | ||
| 80 | // originates from a trusted proxy. This prevents attackers from spoofing | 61 | // 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 proxy | 73 | // 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 a | 77 | // 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 ip | 82 | 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 ( | |||
| 6 | 6 | "net/http/httptest" | |
| 7 | 7 | "testing" | |
| 8 | 8 | ||
| 9 | + | "github.com/bnema/gordon/internal/adapters/in/http/httphelper" | |
| 9 | 10 | "github.com/stretchr/testify/assert" | |
| 10 | 11 | ) | |
| 11 | 12 | ||
| 12 | 13 | func TestGetClientIP(t *testing.T) { | |
| 13 | 14 | // 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 | 16 | noTrustedNets := []*net.IPNet{} | |
| 16 | 17 | // 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 | 20 | tests := []struct { | |
| 20 | 21 | name string | |
| @@ -216,8 +217,8 @@ func TestParseTrustedProxies(t *testing.T) { | |||
| 216 | 217 | ||
| 217 | 218 | for _, tt := range tests { | |
| 218 | 219 | 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) | |
| 221 | 222 | assert.Equal(t, tt.want, got) | |
| 222 | 223 | }) | |
| 223 | 224 | } | |
| @@ -245,20 +246,20 @@ func TestIsTrustedProxy(t *testing.T) { | |||
| 245 | 246 | { | |
| 246 | 247 | name: "invalid IP", | |
| 247 | 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 | 250 | want: false, | |
| 250 | 251 | }, | |
| 251 | 252 | { | |
| 252 | 253 | name: "empty IP", | |
| 253 | 254 | ip: "", | |
| 254 | - | trustedNets: ParseTrustedProxies([]string{"192.168.1.0/24"}), | |
| 255 | + | trustedNets: httphelper.ParseTrustedProxies([]string{"192.168.1.0/24"}), | |
| 255 | 256 | want: false, | |
| 256 | 257 | }, | |
| 257 | 258 | } | |
| 258 | 259 | ||
| 259 | 260 | for _, tt := range tests { | |
| 260 | 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 | 263 | assert.Equal(t, tt.want, got) | |
| 263 | 264 | }) | |
| 264 | 265 | } | |
| @@ -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 tests | 14 | // 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-Ip | 17 | // 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 string | 21 | 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 { | |||
| 89 | 89 | Port int `mapstructure:"port"` | |
| 90 | 90 | RegistryPort int `mapstructure:"registry_port"` | |
| 91 | 91 | GordonDomain string `mapstructure:"gordon_domain"` | |
| 92 | - | RegistryDomain string `mapstructure:"registry_domain"` // Deprecated: use gordon_domain | |
| 93 | 92 | TLSPort int `mapstructure:"tls_port"` | |
| 94 | 93 | TLSCertFile string `mapstructure:"tls_cert_file"` | |
| 95 | 94 | TLSKeyFile string `mapstructure:"tls_key_file"` | |
| @@ -315,11 +314,6 @@ func initConfig(configPath string) (*viper.Viper, Config, error) { | |||
| 315 | 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 | 317 | return v, cfg, nil | |
| 324 | 318 | } | |
| 325 | 319 | ||
| @@ -1386,7 +1380,7 @@ func buildProxyConfig(cfg Config, log zerowrap.Logger) (*proxyConfigResult, erro | |||
| 1386 | 1380 | ||
| 1387 | 1381 | return &proxyConfigResult{ | |
| 1388 | 1382 | proxyConfig: proxy.Config{ | |
| 1389 | - | RegistryDomain: cfg.Server.RegistryDomain, | |
| 1383 | + | RegistryDomain: cfg.Server.GordonDomain, | |
| 1390 | 1384 | RegistryPort: cfg.Server.RegistryPort, | |
| 1391 | 1385 | MaxBodySize: maxProxyBodySize, | |
| 1392 | 1386 | MaxResponseSize: maxProxyResponseSize, | |
| @@ -1421,9 +1415,11 @@ func createContainerService(ctx context.Context, v *viper.Viper, cfg Config, svc | |||
| 1421 | 1415 | defaultNanoCPUs = int64(cfg.Containers.CPULimit * 1e9) | |
| 1422 | 1416 | } | |
| 1423 | 1417 | ||
| 1418 | + | attachmentConfig := svc.configSvc.GetAttachmentConfig() | |
| 1419 | + | ||
| 1424 | 1420 | containerConfig := container.Config{ | |
| 1425 | 1421 | RegistryAuthEnabled: cfg.Auth.Enabled, | |
| 1426 | - | RegistryDomain: cfg.Server.RegistryDomain, | |
| 1422 | + | RegistryDomain: cfg.Server.GordonDomain, | |
| 1427 | 1423 | RegistryPort: cfg.Server.RegistryPort, | |
| 1428 | 1424 | InternalRegistryUsername: svc.internalRegUser, | |
| 1429 | 1425 | InternalRegistryPassword: svc.internalRegPass, | |
| @@ -1433,8 +1429,8 @@ func createContainerService(ctx context.Context, v *viper.Viper, cfg Config, svc | |||
| 1433 | 1429 | VolumePreserve: v.GetBool("volumes.preserve"), | |
| 1434 | 1430 | NetworkIsolation: v.GetBool("network_isolation.enabled"), | |
| 1435 | 1431 | 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, | |
| 1438 | 1434 | ReadinessDelay: v.GetDuration("deploy.readiness_delay"), | |
| 1439 | 1435 | ReadinessMode: v.GetString("deploy.readiness_mode"), | |
| 1440 | 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 | 1545 | // 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 | 1547 | WithEnvExtractor(svc.runtime, svc.envDir) | |
| 1552 | 1548 | ||
| 1553 | 1549 | // 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 | 1613 | log.Error().Err(err).Msg("failed to unmarshal config on reload") | |
| 1618 | 1614 | return | |
| 1619 | 1615 | } | |
| 1620 | - | if reloadCfg.Server.GordonDomain != "" { | |
| 1621 | - | reloadCfg.Server.RegistryDomain = reloadCfg.Server.GordonDomain | |
| 1622 | - | } | |
| 1623 | 1616 | reloadedProxy, err := buildProxyConfig(reloadCfg, log) | |
| 1624 | 1617 | if err != nil { | |
| 1625 | 1618 | 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, nil | 317 | 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 labels | 1545 | // 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 images | 1549 | // 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 | return | 1614 | 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 { | |||
| 20 | 20 | // network groups, read under a single lock to prevent cross-field races. | |
| 21 | 21 | 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 | 23 | // GetNetworkGroups returns a deep copy of the current network group config: group -> []domain. | |
| 27 | 24 | GetNetworkGroups() map[string][]string | |
| 28 | 25 | } | |
| @@ -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() AttachmentConfigSnapshot | 21 | 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][]string | 24 | 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 | |||
| 80 | 80 | 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 | 83 | // GetNetworkGroups provides a mock function for the type MockAttachmentConfigProvider | |
| 130 | 84 | func (_mock *MockAttachmentConfigProvider) GetNetworkGroups() map[string][]string { | |
| 131 | 85 | ret := _mock.Called() | |
| @@ -80,52 +80,6 @@ func (_c *MockAttachmentConfigProvider_GetAttachmentConfig_Call) RunAndReturn(ru | |||||
| 80 | return _c | 80 | 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 MockAttachmentConfigProvider | 83 | // 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 { | |||
| 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 | 936 | // GetAllAttachments returns all configured attachments. | |
| 950 | 937 | func (s *Service) GetAllAttachments(_ context.Context) map[string][]string { | |
| 951 | 938 | 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) { | |||
| 469 | 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 | 472 | func TestService_GetAttachmentConfig(t *testing.T) { | |
| 491 | 473 | v := viper.New() | |
| 492 | 474 | 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{}{ | ||