mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
chore: refactor site handler to take cache dir (#21918)
relates to: https://github.com/coder/internal/issues/1300 Refactors the options to the site handler to take the cache directory, rather than expecting the caller to call `ExtractOrReadBinFS` and pass the results. This is important in this stack because we need direct access to the cache directory for compressed file caching.
This commit is contained in:
+5
-7
@@ -462,10 +462,6 @@ func New(options *Options) *API {
|
|||||||
if siteCacheDir != "" {
|
if siteCacheDir != "" {
|
||||||
siteCacheDir = filepath.Join(siteCacheDir, "site")
|
siteCacheDir = filepath.Join(siteCacheDir, "site")
|
||||||
}
|
}
|
||||||
binFS, binHashes, err := site.ExtractOrReadBinFS(siteCacheDir, site.FS())
|
|
||||||
if err != nil {
|
|
||||||
panic(xerrors.Errorf("read site bin failed: %w", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
metricsCache := metricscache.New(
|
metricsCache := metricscache.New(
|
||||||
options.Database,
|
options.Database,
|
||||||
@@ -658,9 +654,8 @@ func New(options *Options) *API {
|
|||||||
WebPushPublicKey: api.WebpushDispatcher.PublicKey(),
|
WebPushPublicKey: api.WebpushDispatcher.PublicKey(),
|
||||||
Telemetry: api.Telemetry.Enabled(),
|
Telemetry: api.Telemetry.Enabled(),
|
||||||
}
|
}
|
||||||
api.SiteHandler = site.New(&site.Options{
|
api.SiteHandler, err = site.New(&site.Options{
|
||||||
BinFS: binFS,
|
CacheDir: siteCacheDir,
|
||||||
BinHashes: binHashes,
|
|
||||||
Database: options.Database,
|
Database: options.Database,
|
||||||
SiteFS: site.FS(),
|
SiteFS: site.FS(),
|
||||||
OAuth2Configs: oauthConfigs,
|
OAuth2Configs: oauthConfigs,
|
||||||
@@ -672,6 +667,9 @@ func New(options *Options) *API {
|
|||||||
Logger: options.Logger.Named("site"),
|
Logger: options.Logger.Named("site"),
|
||||||
HideAITasks: options.DeploymentValues.HideAITasks.Value(),
|
HideAITasks: options.DeploymentValues.HideAITasks.Value(),
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
options.Logger.Fatal(ctx, "failed to initialize site handler", slog.Error(err))
|
||||||
|
}
|
||||||
api.SiteHandler.Experiments.Store(&experiments)
|
api.SiteHandler.Experiments.Store(&experiments)
|
||||||
|
|
||||||
if options.UpdateCheckOptions != nil {
|
if options.UpdateCheckOptions != nil {
|
||||||
|
|||||||
+8
-4
@@ -89,11 +89,15 @@ func (h *binHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
|||||||
http.FileServer(h.binFS).ServeHTTP(rw, r)
|
http.FileServer(h.binFS).ServeHTTP(rw, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBinHandler(options *Options) *binHandler {
|
func newBinHandler(options *Options) (*binHandler, error) {
|
||||||
return &binHandler{
|
binFS, binHashes, err := ExtractOrReadBinFS(options.CacheDir, options.SiteFS)
|
||||||
binFS: options.BinFS,
|
if err != nil {
|
||||||
metadataCache: newBinMetadataCache(options.BinFS, options.BinHashes),
|
return nil, xerrors.Errorf("extract or read bin filesystem: %w", err)
|
||||||
}
|
}
|
||||||
|
return &binHandler{
|
||||||
|
binFS: binFS,
|
||||||
|
metadataCache: newBinMetadataCache(binFS, binHashes),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractOrReadBinFS checks the provided fs for compressed coder binaries and
|
// ExtractOrReadBinFS checks the provided fs for compressed coder binaries and
|
||||||
|
|||||||
+11
-7
@@ -68,8 +68,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
BinFS http.FileSystem
|
CacheDir string
|
||||||
BinHashes map[string]string
|
|
||||||
Database database.Store
|
Database database.Store
|
||||||
SiteFS fs.FS
|
SiteFS fs.FS
|
||||||
OAuth2Configs *httpmw.OAuth2Configs
|
OAuth2Configs *httpmw.OAuth2Configs
|
||||||
@@ -82,7 +81,7 @@ type Options struct {
|
|||||||
HideAITasks bool
|
HideAITasks bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(opts *Options) *Handler {
|
func New(opts *Options) (*Handler, error) {
|
||||||
if opts.AppearanceFetcher == nil {
|
if opts.AppearanceFetcher == nil {
|
||||||
daf := atomic.Pointer[appearance.Fetcher]{}
|
daf := atomic.Pointer[appearance.Fetcher]{}
|
||||||
f := appearance.NewDefaultFetcher(opts.DocsURL)
|
f := appearance.NewDefaultFetcher(opts.DocsURL)
|
||||||
@@ -100,11 +99,16 @@ func New(opts *Options) *Handler {
|
|||||||
var err error
|
var err error
|
||||||
handler.htmlTemplates, err = findAndParseHTMLFiles(opts.SiteFS)
|
handler.htmlTemplates, err = findAndParseHTMLFiles(opts.SiteFS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("Failed to parse html files: %v", err))
|
return nil, xerrors.Errorf("failed to parse html files: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
binHand, err := newBinHandler(opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("create bin handler: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
mux.Handle("/bin/", newBinHandler(opts))
|
mux.Handle("/bin/", binHand)
|
||||||
mux.Handle("/", http.FileServer(
|
mux.Handle("/", http.FileServer(
|
||||||
http.FS(
|
http.FS(
|
||||||
// OnlyFiles is a wrapper around the file system that prevents directory
|
// OnlyFiles is a wrapper around the file system that prevents directory
|
||||||
@@ -116,7 +120,7 @@ func New(opts *Options) *Handler {
|
|||||||
)
|
)
|
||||||
buildInfoResponse, err := json.Marshal(opts.BuildInfo)
|
buildInfoResponse, err := json.Marshal(opts.BuildInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to marshal build info: " + err.Error())
|
return nil, xerrors.Errorf("failed to marshal build info: %w", err)
|
||||||
}
|
}
|
||||||
handler.buildInfoJSON = html.EscapeString(string(buildInfoResponse))
|
handler.buildInfoJSON = html.EscapeString(string(buildInfoResponse))
|
||||||
handler.handler = mux.ServeHTTP
|
handler.handler = mux.ServeHTTP
|
||||||
@@ -126,7 +130,7 @@ func New(opts *Options) *Handler {
|
|||||||
opts.Logger.Warn(context.Background(), "could not parse install.sh, it will be unavailable", slog.Error(err))
|
opts.Logger.Warn(context.Background(), "could not parse install.sh, it will be unavailable", slog.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler
|
return handler, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
|
|||||||
+22
-24
@@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
|
||||||
"github.com/coder/coder/v2/coderd/database"
|
"github.com/coder/coder/v2/coderd/database"
|
||||||
"github.com/coder/coder/v2/coderd/database/db2sdk"
|
"github.com/coder/coder/v2/coderd/database/db2sdk"
|
||||||
@@ -44,14 +45,13 @@ func TestInjection(t *testing.T) {
|
|||||||
Data: []byte("{{ .User }}"),
|
Data: []byte("{{ .User }}"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
binFs := http.FS(fstest.MapFS{})
|
|
||||||
db, _ := dbtestutil.NewDB(t)
|
db, _ := dbtestutil.NewDB(t)
|
||||||
handler := site.New(&site.Options{
|
handler, err := site.New(&site.Options{
|
||||||
Telemetry: telemetry.NewNoop(),
|
Telemetry: telemetry.NewNoop(),
|
||||||
BinFS: binFs,
|
|
||||||
Database: db,
|
Database: db,
|
||||||
SiteFS: siteFS,
|
SiteFS: siteFS,
|
||||||
})
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
user := dbgen.User(t, db, database.User{})
|
user := dbgen.User(t, db, database.User{})
|
||||||
_, token := dbgen.APIKey(t, db, database.APIKey{
|
_, token := dbgen.APIKey(t, db, database.APIKey{
|
||||||
@@ -66,7 +66,7 @@ func TestInjection(t *testing.T) {
|
|||||||
handler.ServeHTTP(rw, r)
|
handler.ServeHTTP(rw, r)
|
||||||
require.Equal(t, http.StatusOK, rw.Code)
|
require.Equal(t, http.StatusOK, rw.Code)
|
||||||
var got codersdk.User
|
var got codersdk.User
|
||||||
err := json.Unmarshal([]byte(html.UnescapeString(rw.Body.String())), &got)
|
err = json.Unmarshal([]byte(html.UnescapeString(rw.Body.String())), &got)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// This will update as part of the request!
|
// This will update as part of the request!
|
||||||
@@ -101,15 +101,13 @@ func TestInjectionFailureProducesCleanHTML(t *testing.T) {
|
|||||||
OAuthExpiry: dbtime.Now().Add(-time.Second),
|
OAuthExpiry: dbtime.Now().Add(-time.Second),
|
||||||
})
|
})
|
||||||
|
|
||||||
binFs := http.FS(fstest.MapFS{})
|
|
||||||
siteFS := fstest.MapFS{
|
siteFS := fstest.MapFS{
|
||||||
"index.html": &fstest.MapFile{
|
"index.html": &fstest.MapFile{
|
||||||
Data: []byte("<html>{{ .User }}</html>"),
|
Data: []byte("<html>{{ .User }}</html>"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
handler := site.New(&site.Options{
|
handler, err := site.New(&site.Options{
|
||||||
Telemetry: telemetry.NewNoop(),
|
Telemetry: telemetry.NewNoop(),
|
||||||
BinFS: binFs,
|
|
||||||
Database: db,
|
Database: db,
|
||||||
SiteFS: siteFS,
|
SiteFS: siteFS,
|
||||||
|
|
||||||
@@ -119,6 +117,7 @@ func TestInjectionFailureProducesCleanHTML(t *testing.T) {
|
|||||||
OIDC: nil,
|
OIDC: nil,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
r := httptest.NewRequest("GET", "/", nil)
|
r := httptest.NewRequest("GET", "/", nil)
|
||||||
r.Header.Set(codersdk.SessionTokenHeader, token)
|
r.Header.Set(codersdk.SessionTokenHeader, token)
|
||||||
@@ -153,15 +152,15 @@ func TestCaching(t *testing.T) {
|
|||||||
Data: []byte("folderFile"),
|
Data: []byte("folderFile"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
binFS := http.FS(fstest.MapFS{})
|
|
||||||
|
|
||||||
db, _ := dbtestutil.NewDB(t)
|
db, _ := dbtestutil.NewDB(t)
|
||||||
srv := httptest.NewServer(site.New(&site.Options{
|
s, err := site.New(&site.Options{
|
||||||
Telemetry: telemetry.NewNoop(),
|
Telemetry: telemetry.NewNoop(),
|
||||||
BinFS: binFS,
|
|
||||||
SiteFS: rootFS,
|
SiteFS: rootFS,
|
||||||
Database: db,
|
Database: db,
|
||||||
}))
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
srv := httptest.NewServer(s)
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
|
|
||||||
// Create a context
|
// Create a context
|
||||||
@@ -222,15 +221,15 @@ func TestServingFiles(t *testing.T) {
|
|||||||
Data: []byte("install-sh-bytes"),
|
Data: []byte("install-sh-bytes"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
binFS := http.FS(fstest.MapFS{})
|
|
||||||
|
|
||||||
db, _ := dbtestutil.NewDB(t)
|
db, _ := dbtestutil.NewDB(t)
|
||||||
srv := httptest.NewServer(site.New(&site.Options{
|
handler, err := site.New(&site.Options{
|
||||||
Telemetry: telemetry.NewNoop(),
|
Telemetry: telemetry.NewNoop(),
|
||||||
BinFS: binFS,
|
|
||||||
SiteFS: rootFS,
|
SiteFS: rootFS,
|
||||||
Database: db,
|
Database: db,
|
||||||
}))
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
srv := httptest.NewServer(handler)
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
||||||
@@ -506,21 +505,20 @@ func TestServingBin(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
dest := t.TempDir()
|
dest := t.TempDir()
|
||||||
binFS, binHashes, err := site.ExtractOrReadBinFS(dest, tt.fs)
|
testFS := maps.Clone(rootFS)
|
||||||
|
maps.Copy(testFS, tt.fs)
|
||||||
|
handler, err := site.New(&site.Options{
|
||||||
|
Telemetry: telemetry.NewNoop(),
|
||||||
|
SiteFS: testFS,
|
||||||
|
CacheDir: dest,
|
||||||
|
})
|
||||||
if !tt.wantErr && err != nil {
|
if !tt.wantErr && err != nil {
|
||||||
require.NoError(t, err, "extract or read failed")
|
require.NoError(t, err, "extract or read failed")
|
||||||
} else if tt.wantErr {
|
} else if tt.wantErr {
|
||||||
require.Error(t, err, "extraction or read did not fail")
|
require.Error(t, err, "extraction or read did not fail")
|
||||||
}
|
}
|
||||||
|
|
||||||
site := site.New(&site.Options{
|
|
||||||
Telemetry: telemetry.NewNoop(),
|
|
||||||
BinFS: binFS,
|
|
||||||
BinHashes: binHashes,
|
|
||||||
SiteFS: rootFS,
|
|
||||||
})
|
|
||||||
compressor := middleware.NewCompressor(1, "text/*", "application/*")
|
compressor := middleware.NewCompressor(1, "text/*", "application/*")
|
||||||
srv := httptest.NewServer(compressor.Handler(site))
|
srv := httptest.NewServer(compressor.Handler(handler))
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user