Fix merge conflicts
This commit is contained in:
parent
fdfa9c610c
commit
42d5e75dc5
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,5 @@
|
||||||
|
var Bar = {};
|
||||||
|
|
||||||
|
Bar.hello = function(){
|
||||||
|
return "I am a bar";
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
// foo.js
|
||||||
|
|
||||||
|
var Foo = {};
|
||||||
|
|
||||||
|
Foo.hello = function(){
|
||||||
|
return "I am a foo";
|
||||||
|
}
|
||||||
|
|
||||||
|
// bar.js
|
||||||
|
|
||||||
|
var Bar = {};
|
||||||
|
|
||||||
|
Bar.hello = function(){
|
||||||
|
return "I am a bar";
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
var Foo = {};
|
||||||
|
|
||||||
|
Foo.hello = function(){
|
||||||
|
return "I am a foo";
|
||||||
|
}
|
|
@ -51,5 +51,13 @@ func uiTemplateDataFromConfig(cfg *config.RuntimeConfig) (map[string]interface{}
|
||||||
// account for in the source...
|
// account for in the source...
|
||||||
d["UIConfigJSON"] = url.PathEscape(string(bs))
|
d["UIConfigJSON"] = url.PathEscape(string(bs))
|
||||||
|
|
||||||
|
// Also inject additional provider scripts if needed, otherwise strip the
|
||||||
|
// comment.
|
||||||
|
if len(cfg.UIConfig.MetricsProviderFiles) > 0 {
|
||||||
|
d["ExtraScripts"] = []string{
|
||||||
|
cfg.UIConfig.ContentPath + compiledProviderJSPath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return d, err
|
return d, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
@ -16,6 +17,10 @@ import (
|
||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
compiledProviderJSPath = "assets/compiled-metrics-providers.js"
|
||||||
|
)
|
||||||
|
|
||||||
// Handler is the http.Handler that serves the Consul UI. It may serve from the
|
// Handler is the http.Handler that serves the Consul UI. It may serve from the
|
||||||
// compiled-in AssetFS or from and external dir. It provides a few important
|
// compiled-in AssetFS or from and external dir. It provides a few important
|
||||||
// transformations on the index.html file and includes a proxy for metrics
|
// transformations on the index.html file and includes a proxy for metrics
|
||||||
|
@ -57,7 +62,16 @@ func NewHandler(agentCfg *config.RuntimeConfig, logger hclog.Logger) *Handler {
|
||||||
|
|
||||||
// ServeHTTP implements http.Handler and serves UI HTTP requests
|
// ServeHTTP implements http.Handler and serves UI HTTP requests
|
||||||
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// TODO: special case for compiled metrics assets in later PR
|
|
||||||
|
// We need to support the path being trimmed by http.StripTags just like the
|
||||||
|
// file servers do since http.StripPrefix will remove the leading slash in our
|
||||||
|
// current config. Everything else works fine that way so we should to.
|
||||||
|
pathTrimmed := strings.TrimLeft(r.URL.Path, "/")
|
||||||
|
if pathTrimmed == compiledProviderJSPath {
|
||||||
|
h.serveUIMetricsProviders(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
s := h.getState()
|
s := h.getState()
|
||||||
if s == nil {
|
if s == nil {
|
||||||
panic("nil state")
|
panic("nil state")
|
||||||
|
@ -133,6 +147,59 @@ func (h *Handler) getState() *reloadableState {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) serveUIMetricsProviders(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
// Reload config in case it's changed
|
||||||
|
state := h.getState()
|
||||||
|
|
||||||
|
if len(state.cfg.MetricsProviderFiles) < 1 {
|
||||||
|
http.Error(resp, "No provider JS files configured", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
// Open each one and concatenate them
|
||||||
|
for _, file := range state.cfg.MetricsProviderFiles {
|
||||||
|
if err := concatFile(&buf, file); err != nil {
|
||||||
|
http.Error(resp, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
h.logger.Error("failed serving metrics provider js file", "file", file, "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Done!
|
||||||
|
resp.Header()["Content-Type"] = []string{"application/javascript"}
|
||||||
|
_, err := buf.WriteTo(resp)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(resp, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
h.logger.Error("failed writing ui metrics provider files: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func concatFile(buf *bytes.Buffer, file string) error {
|
||||||
|
base := path.Base(file)
|
||||||
|
_, err := buf.WriteString("// " + base + "\n\n")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed writing provider JS files: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to open the file
|
||||||
|
f, err := os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed opening ui metrics provider JS file: %w", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
_, err = buf.ReadFrom(f)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed reading ui metrics provider JS file: %w", err)
|
||||||
|
}
|
||||||
|
_, err = buf.WriteString("\n\n")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed writing provider JS files: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func renderIndex(cfg *config.RuntimeConfig, fs http.FileSystem) ([]byte, os.FileInfo, error) {
|
func renderIndex(cfg *config.RuntimeConfig, fs http.FileSystem) ([]byte, os.FileInfo, error) {
|
||||||
// Open the original index.html
|
// Open the original index.html
|
||||||
f, err := fs.Open("/index.html")
|
f, err := fs.Open("/index.html")
|
||||||
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUIServer(t *testing.T) {
|
func TestUIServerIndex(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
cfg *config.RuntimeConfig
|
cfg *config.RuntimeConfig
|
||||||
|
@ -80,6 +80,19 @@ func TestUIServer(t *testing.T) {
|
||||||
"CONSUL_ACLS_ENABLED": true,
|
"CONSUL_ACLS_ENABLED": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "serving metrics provider js",
|
||||||
|
cfg: basicUIEnabledConfig(
|
||||||
|
withMetricsProvider("foo"),
|
||||||
|
withMetricsProviderFiles("testdata/foo.js", "testdata/bar.js"),
|
||||||
|
),
|
||||||
|
path: "/",
|
||||||
|
wantStatus: http.StatusOK,
|
||||||
|
wantContains: []string{
|
||||||
|
"<!-- CONSUL_VERSION:",
|
||||||
|
`<script src="/ui/assets/compiled-metrics-providers.js">`,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
|
@ -152,7 +165,8 @@ type cfgFunc func(cfg *config.RuntimeConfig)
|
||||||
func basicUIEnabledConfig(opts ...cfgFunc) *config.RuntimeConfig {
|
func basicUIEnabledConfig(opts ...cfgFunc) *config.RuntimeConfig {
|
||||||
cfg := &config.RuntimeConfig{
|
cfg := &config.RuntimeConfig{
|
||||||
UIConfig: config.UIConfig{
|
UIConfig: config.UIConfig{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
|
ContentPath: "/ui/",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, f := range opts {
|
for _, f := range opts {
|
||||||
|
@ -175,6 +189,12 @@ func withMetricsProvider(name string) cfgFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func withMetricsProviderFiles(names ...string) cfgFunc {
|
||||||
|
return func(cfg *config.RuntimeConfig) {
|
||||||
|
cfg.UIConfig.MetricsProviderFiles = names
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func withMetricsProviderOptions(jsonStr string) cfgFunc {
|
func withMetricsProviderOptions(jsonStr string) cfgFunc {
|
||||||
return func(cfg *config.RuntimeConfig) {
|
return func(cfg *config.RuntimeConfig) {
|
||||||
cfg.UIConfig.MetricsProviderOptionsJSON = jsonStr
|
cfg.UIConfig.MetricsProviderOptionsJSON = jsonStr
|
||||||
|
@ -251,3 +271,43 @@ func TestCustomDir(t *testing.T) {
|
||||||
require.Equal(t, http.StatusOK, rec.Code)
|
require.Equal(t, http.StatusOK, rec.Code)
|
||||||
require.Contains(t, rec.Body.String(), "test")
|
require.Contains(t, rec.Body.String(), "test")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCompiledJS(t *testing.T) {
|
||||||
|
cfg := basicUIEnabledConfig(
|
||||||
|
withMetricsProvider("foo"),
|
||||||
|
withMetricsProviderFiles("testdata/foo.js", "testdata/bar.js"),
|
||||||
|
)
|
||||||
|
h := NewHandler(cfg, testutil.Logger(t))
|
||||||
|
|
||||||
|
paths := []string{
|
||||||
|
"/" + compiledProviderJSPath,
|
||||||
|
// We need to work even without the initial slash because the agent uses
|
||||||
|
// http.StripPrefix with the entire ContentPath which includes a trailing
|
||||||
|
// slash. This apparently works fine for the assetFS etc. so we need to
|
||||||
|
// also tolerate it when the URL doesn't have a slash at the start of the
|
||||||
|
// path.
|
||||||
|
compiledProviderJSPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, path := range paths {
|
||||||
|
t.Run(path, func(t *testing.T) {
|
||||||
|
// NewRequest doesn't like paths with no leading slash but we need to test
|
||||||
|
// a request with a URL that has that so just create with root path and
|
||||||
|
// then manually modify the URL path so it emulates one that has been
|
||||||
|
// doctored by http.StripPath.
|
||||||
|
req := httptest.NewRequest("GET", "/", nil)
|
||||||
|
req.URL.Path = path
|
||||||
|
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
|
||||||
|
h.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
require.Equal(t, http.StatusOK, rec.Code)
|
||||||
|
require.Equal(t, rec.Result().Header["Content-Type"][0], "application/javascript")
|
||||||
|
wantCompiled, err := ioutil.ReadFile("testdata/compiled-metrics-providers-golden.js")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, rec.Body.String(), string(wantCompiled))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -41,5 +41,6 @@ module.exports = ({ appName, environment, rootURL, config }) => `
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
${environment === 'production' ? `{{ range .ExtraScripts }} <script src="{{.}}"></script> {{ end }}` : ``}
|
||||||
${environment === 'test' ? `<script src="${rootURL}assets/tests.js"></script>` : ``}
|
${environment === 'test' ? `<script src="${rootURL}assets/tests.js"></script>` : ``}
|
||||||
`;
|
`;
|
||||||
|
|
Loading…
Reference in New Issue