Skip to content

Commit 2d7e242

Browse files
committed
use query instead of proxycache
1 parent a96a73b commit 2d7e242

File tree

17 files changed

+538
-311
lines changed

17 files changed

+538
-311
lines changed

coderd/coderd.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ import (
5151
"github.com/coder/coder/coderd/httpmw"
5252
"github.com/coder/coder/coderd/metricscache"
5353
"github.com/coder/coder/coderd/provisionerdserver"
54-
"github.com/coder/coder/coderd/proxycache"
5554
"github.com/coder/coder/coderd/rbac"
5655
"github.com/coder/coder/coderd/schedule"
5756
"github.com/coder/coder/coderd/telemetry"
@@ -279,9 +278,6 @@ func New(options *Options) *API {
279278
},
280279
)
281280

282-
ctx, cancel := context.WithCancel(context.Background())
283-
proxyCache := proxycache.New(ctx, options.Logger.Named("proxy_cache"), options.Database, time.Minute*5)
284-
285281
staticHandler := site.Handler(site.FS(), binFS, binHashes)
286282
// Static file handler must be wrapped with HSTS handler if the
287283
// StrictTransportSecurityAge is set. We only need to set this header on
@@ -293,6 +289,7 @@ func New(options *Options) *API {
293289
OIDC: options.OIDCConfig,
294290
}
295291

292+
ctx, cancel := context.WithCancel(context.Background())
296293
r := chi.NewRouter()
297294
api := &API{
298295
ctx: ctx,
@@ -317,7 +314,6 @@ func New(options *Options) *API {
317314
options.AppSecurityKey,
318315
),
319316
metricsCache: metricsCache,
320-
ProxyCache: proxyCache,
321317
Auditor: atomic.Pointer[audit.Auditor]{},
322318
TemplateScheduleStore: options.TemplateScheduleStore,
323319
Experiments: experiments,
@@ -826,8 +822,7 @@ type API struct {
826822
workspaceAgentCache *wsconncache.Cache
827823
updateChecker *updatecheck.Checker
828824
WorkspaceAppsProvider workspaceapps.SignedTokenProvider
829-
workspaceAppServer *workspaceapps.Server
830-
ProxyCache *proxycache.Cache
825+
workspaceAppServer *workspaceapps.Server
831826

832827
// Experiments contains the list of experiments currently enabled.
833828
// This is used to gate features that are not yet ready for production.

coderd/database/dbauthz/querier.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,6 +1697,10 @@ func (q *querier) GetWorkspaceProxyByID(ctx context.Context, id uuid.UUID) (data
16971697
return fetch(q.log, q.auth, q.db.GetWorkspaceProxyByID)(ctx, id)
16981698
}
16991699

1700+
func (q *querier) GetWorkspaceProxyByHostname(ctx context.Context, hostname string) (database.WorkspaceProxy, error) {
1701+
return fetch(q.log, q.auth, q.db.GetWorkspaceProxyByHostname)(ctx, hostname)
1702+
}
1703+
17001704
func (q *querier) InsertWorkspaceProxy(ctx context.Context, arg database.InsertWorkspaceProxyParams) (database.WorkspaceProxy, error) {
17011705
return insert(q.log, q.auth, rbac.ResourceWorkspaceProxy, q.db.InsertWorkspaceProxy)(ctx, arg)
17021706
}

coderd/database/dbfake/databasefake.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"encoding/json"
77
"fmt"
88
"reflect"
9+
"regexp"
910
"sort"
1011
"strings"
1112
"sync"
@@ -18,10 +19,13 @@ import (
1819
"golang.org/x/xerrors"
1920

2021
"github.com/coder/coder/coderd/database"
22+
"github.com/coder/coder/coderd/httpapi"
2123
"github.com/coder/coder/coderd/rbac"
2224
"github.com/coder/coder/coderd/util/slice"
2325
)
2426

27+
var validProxyByHostnameRegex = regexp.MustCompile(`^[a-zA-Z0-9.-]+$`)
28+
2529
// FakeDatabase is helpful for knowing if the underlying db is an in memory fake
2630
// database. This is only in the databasefake package, so will only be used
2731
// by unit tests.
@@ -5022,6 +5026,40 @@ func (q *fakeQuerier) GetWorkspaceProxyByID(_ context.Context, id uuid.UUID) (da
50225026
return database.WorkspaceProxy{}, sql.ErrNoRows
50235027
}
50245028

5029+
func (q *fakeQuerier) GetWorkspaceProxyByHostname(_ context.Context, hostname string) (database.WorkspaceProxy, error) {
5030+
q.mutex.Lock()
5031+
defer q.mutex.Unlock()
5032+
5033+
// Return zero rows if this is called with a non-sanitized hostname. The SQL
5034+
// version of this query does the same thing.
5035+
if !validProxyByHostnameRegex.MatchString(hostname) {
5036+
return database.WorkspaceProxy{}, sql.ErrNoRows
5037+
}
5038+
5039+
// This regex matches the SQL version.
5040+
accessURLRegex := regexp.MustCompile(`[^:]*://` + regexp.QuoteMeta(hostname) + `([:/]?.)*`)
5041+
5042+
for _, proxy := range q.workspaceProxies {
5043+
if proxy.Deleted {
5044+
continue
5045+
}
5046+
if accessURLRegex.MatchString(proxy.Url) {
5047+
return proxy, nil
5048+
}
5049+
5050+
// Compile the app hostname regex. This is slow sadly.
5051+
wildcardRegexp, err := httpapi.CompileHostnamePattern(proxy.WildcardHostname)
5052+
if err != nil {
5053+
return database.WorkspaceProxy{}, xerrors.Errorf("compile hostname pattern %q for proxy %q (%s): %w", proxy.WildcardHostname, proxy.Name, proxy.ID.String(), err)
5054+
}
5055+
if _, ok := httpapi.ExecuteHostnamePattern(wildcardRegexp, hostname); ok {
5056+
return proxy, nil
5057+
}
5058+
}
5059+
5060+
return database.WorkspaceProxy{}, sql.ErrNoRows
5061+
}
5062+
50255063
func (q *fakeQuerier) InsertWorkspaceProxy(_ context.Context, arg database.InsertWorkspaceProxyParams) (database.WorkspaceProxy, error) {
50265064
q.mutex.Lock()
50275065
defer q.mutex.Unlock()

coderd/database/dbfake/databasefake_test.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,96 @@ func TestUserOrder(t *testing.T) {
129129
}
130130
}
131131

132+
func TestProxyByHostname(t *testing.T) {
133+
t.Parallel()
134+
135+
db := dbfake.New()
136+
137+
// Insert a bunch of different proxies.
138+
proxies := []struct {
139+
name string
140+
accessURL string
141+
wildcardHostname string
142+
}{
143+
{
144+
name: "one",
145+
accessURL: "https://one.coder.com",
146+
wildcardHostname: "*.wildcard.one.coder.com",
147+
},
148+
{
149+
name: "two",
150+
accessURL: "https://two.coder.com",
151+
wildcardHostname: "*--suffix.two.coder.com",
152+
},
153+
}
154+
for _, p := range proxies {
155+
dbgen.WorkspaceProxy(t, db, database.WorkspaceProxy{
156+
Name: p.name,
157+
Url: p.accessURL,
158+
WildcardHostname: p.wildcardHostname,
159+
})
160+
}
161+
162+
cases := []struct {
163+
name string
164+
testHostname string
165+
matchProxyName string
166+
}{
167+
{
168+
name: "NoMatch",
169+
testHostname: "test.com",
170+
matchProxyName: "",
171+
},
172+
{
173+
name: "MatchAccessURL",
174+
testHostname: "one.coder.com",
175+
matchProxyName: "one",
176+
},
177+
{
178+
name: "MatchWildcard",
179+
testHostname: "something.wildcard.one.coder.com",
180+
matchProxyName: "one",
181+
},
182+
{
183+
name: "MatchSuffix",
184+
testHostname: "something--suffix.two.coder.com",
185+
matchProxyName: "two",
186+
},
187+
{
188+
name: "ValidateHostname/1",
189+
testHostname: ".*ne.coder.com",
190+
matchProxyName: "",
191+
},
192+
{
193+
name: "ValidateHostname/2",
194+
testHostname: "https://one.coder.com",
195+
matchProxyName: "",
196+
},
197+
{
198+
name: "ValidateHostname/3",
199+
testHostname: "one.coder.com:8080/hello",
200+
matchProxyName: "",
201+
},
202+
}
203+
204+
for _, c := range cases {
205+
c := c
206+
t.Run(c.name, func(t *testing.T) {
207+
t.Parallel()
208+
209+
proxy, err := db.GetWorkspaceProxyByHostname(context.Background(), c.testHostname)
210+
if c.matchProxyName == "" {
211+
require.ErrorIs(t, err, sql.ErrNoRows)
212+
require.Empty(t, proxy)
213+
} else {
214+
require.NoError(t, err)
215+
require.NotEmpty(t, proxy)
216+
require.Equal(t, c.matchProxyName, proxy.Name)
217+
}
218+
})
219+
}
220+
}
221+
132222
func methods(rt reflect.Type) map[string]bool {
133223
methods := make(map[string]bool)
134224
for i := 0; i < rt.NumMethod(); i++ {

coderd/database/querier.go

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/querier_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package database_test
44

55
import (
66
"context"
7+
"database/sql"
78
"testing"
89
"time"
910

@@ -127,3 +128,98 @@ func TestInsertWorkspaceAgentStartupLogs(t *testing.T) {
127128
})
128129
require.True(t, database.IsStartupLogsLimitError(err))
129130
}
131+
132+
func TestProxyByHostname(t *testing.T) {
133+
t.Parallel()
134+
if testing.Short() {
135+
t.SkipNow()
136+
}
137+
sqlDB := testSQLDB(t)
138+
err := migrations.Up(sqlDB)
139+
require.NoError(t, err)
140+
db := database.New(sqlDB)
141+
142+
// Insert a bunch of different proxies.
143+
proxies := []struct {
144+
name string
145+
accessURL string
146+
wildcardHostname string
147+
}{
148+
{
149+
name: "one",
150+
accessURL: "https://one.coder.com",
151+
wildcardHostname: "*.wildcard.one.coder.com",
152+
},
153+
{
154+
name: "two",
155+
accessURL: "https://two.coder.com",
156+
wildcardHostname: "*--suffix.two.coder.com",
157+
},
158+
}
159+
for _, p := range proxies {
160+
dbgen.WorkspaceProxy(t, db, database.WorkspaceProxy{
161+
Name: p.name,
162+
Url: p.accessURL,
163+
WildcardHostname: p.wildcardHostname,
164+
})
165+
}
166+
167+
cases := []struct {
168+
name string
169+
testHostname string
170+
matchProxyName string
171+
}{
172+
{
173+
name: "NoMatch",
174+
testHostname: "test.com",
175+
matchProxyName: "",
176+
},
177+
{
178+
name: "MatchAccessURL",
179+
testHostname: "one.coder.com",
180+
matchProxyName: "one",
181+
},
182+
{
183+
name: "MatchWildcard",
184+
testHostname: "something.wildcard.one.coder.com",
185+
matchProxyName: "one",
186+
},
187+
{
188+
name: "MatchSuffix",
189+
testHostname: "something--suffix.two.coder.com",
190+
matchProxyName: "two",
191+
},
192+
{
193+
name: "ValidateHostname/1",
194+
testHostname: ".*ne.coder.com",
195+
matchProxyName: "",
196+
},
197+
{
198+
name: "ValidateHostname/2",
199+
testHostname: "https://one.coder.com",
200+
matchProxyName: "",
201+
},
202+
{
203+
name: "ValidateHostname/3",
204+
testHostname: "one.coder.com:8080/hello",
205+
matchProxyName: "",
206+
},
207+
}
208+
209+
for _, c := range cases {
210+
c := c
211+
t.Run(c.name, func(t *testing.T) {
212+
t.Parallel()
213+
214+
proxy, err := db.GetWorkspaceProxyByHostname(context.Background(), c.testHostname)
215+
if c.matchProxyName == "" {
216+
require.ErrorIs(t, err, sql.ErrNoRows)
217+
require.Empty(t, proxy)
218+
} else {
219+
require.NoError(t, err)
220+
require.NotEmpty(t, proxy)
221+
require.Equal(t, c.matchProxyName, proxy.Name)
222+
}
223+
})
224+
}
225+
}

coderd/database/queries.sql.go

Lines changed: 50 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)