diff --git a/agent/ui_endpoint_test.go b/agent/ui_endpoint_test.go index 7338417e4..006aa841d 100644 --- a/agent/ui_endpoint_test.go +++ b/agent/ui_endpoint_test.go @@ -28,18 +28,20 @@ func TestUiIndex(t *testing.T) { // Make the server a := NewTestAgent(t, ` - ui_dir = "`+uiDir+`" + ui_config { + dir = "`+uiDir+`" + } `) defer a.Shutdown() testrpc.WaitForLeader(t, a.RPC, "dc1") // Create file path := filepath.Join(a.Config.UIConfig.Dir, "my-file") - if err := ioutil.WriteFile(path, []byte("test"), 0777); err != nil { + if err := ioutil.WriteFile(path, []byte("test"), 0644); err != nil { t.Fatalf("err: %v", err) } - // Register node + // Request the custom file req, _ := http.NewRequest("GET", "/ui/my-file", nil) req.URL.Scheme = "http" req.URL.Host = a.HTTPAddr() diff --git a/agent/uiserver/ui_template_data.go b/agent/uiserver/ui_template_data.go index cb80f4235..13852ba68 100644 --- a/agent/uiserver/ui_template_data.go +++ b/agent/uiserver/ui_template_data.go @@ -35,6 +35,9 @@ func uiTemplateDataFromConfig(cfg *config.RuntimeConfig) (map[string]interface{} } err := uiTemplateDataFromConfigEnterprise(cfg, d, uiCfg) + if err != nil { + return nil, err + } // Render uiCfg down to JSON ready to inject into the template bs, err := json.Marshal(uiCfg) diff --git a/agent/uiserver/uiserver.go b/agent/uiserver/uiserver.go index 2c3d807fe..436f847a8 100644 --- a/agent/uiserver/uiserver.go +++ b/agent/uiserver/uiserver.go @@ -88,22 +88,36 @@ func (h *Handler) ReloadConfig(newCfg *config.RuntimeConfig) error { // Render a new index.html with the new config values ready to serve. buf, info, err := renderIndex(newCfg, fs) - if err != nil { + if _, ok := err.(*os.PathError); ok && newCfg.UIConfig.Dir != "" { + // A Path error indicates that there is no index.html. This could happen if + // the user configured their own UI dir and is serving something that is not + // our usual UI. This won't work perfectly because our uiserver will still + // redirect everything to the UI but we shouldn't fail the entire UI server + // with a 500 in this case. Partly that's just bad UX and partly it's a + // breaking change although quite an edge case. Instead, continue but just + // return a 404 response for the index.html and log a warning. + h.logger.Warn("ui_config.dir does not contain an index.html. Index templating and redirects to index.html are disabled.") + } else if err != nil { return err } - // Create a new fs that serves the rendered index file or falls back to the - // underlying FS. - fs = &bufIndexFS{ - fs: fs, - indexRendered: buf, - indexInfo: info, + // buf can be nil in the PathError case above. We should skip this part but + // still serve the rest of the files in that case. + if buf != nil { + // Create a new fs that serves the rendered index file or falls back to the + // underlying FS. + fs = &bufIndexFS{ + fs: fs, + indexRendered: buf, + indexInfo: info, + } + + // Wrap the buffering FS our redirect FS. This needs to happen later so that + // redirected requests for /index.html get served the rendered version not the + // original. + fs = &redirectFS{fs: fs} } - // Wrap the buffering FS our redirect FS. This needs to happen later so that - // redirected requests for /index.html get served the rendered version not the - // original. - fs = &redirectFS{fs: fs} newState.srv = http.FileServer(fs) // Store the new state diff --git a/agent/uiserver/uiserver_test.go b/agent/uiserver/uiserver_test.go index 6ab43eb73..ea6f623b3 100644 --- a/agent/uiserver/uiserver_test.go +++ b/agent/uiserver/uiserver_test.go @@ -2,9 +2,12 @@ package uiserver import ( "encoding/json" + "io/ioutil" "net/http" "net/http/httptest" "net/url" + "os" + "path/filepath" "regexp" "testing" @@ -228,3 +231,23 @@ func TestReload(t *testing.T) { require.Contains(t, rec.Body.String(), "exotic-metrics-provider-name") } } + +func TestCustomDir(t *testing.T) { + uiDir := testutil.TempDir(t, "consul-uiserver") + defer os.RemoveAll(uiDir) + + path := filepath.Join(uiDir, "test-file") + require.NoError(t, ioutil.WriteFile(path, []byte("test"), 0644)) + + cfg := basicUIEnabledConfig() + cfg.UIConfig.Dir = uiDir + h := NewHandler(cfg, testutil.Logger(t)) + + req := httptest.NewRequest("GET", "/test-file", nil) + rec := httptest.NewRecorder() + + h.ServeHTTP(rec, req) + + require.Equal(t, http.StatusOK, rec.Code) + require.Contains(t, rec.Body.String(), "test") +}