From: Martina Ferrari <tina@debian.org>
Date: Sat, 20 Jun 2020 15:32:33 -0300
Subject: Do not embed blobs

Forwarded: not-needed
Last-Updated: Mon, 14 Mar 2022 23:20:08 +0000

Avoid embedding blobs into the prometheus binary, instead use files
installed on disk.
---
 cmd/prometheus/main.go     |  3 +++
 console_libraries/prom.lib | 16 ++++++++--------
 web/ui/ui.go               |  4 ++--
 web/web.go                 | 29 ++++++++++-------------------
 4 files changed, 23 insertions(+), 29 deletions(-)

diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go
index 18be4ee..6f5f9eb 100644
--- a/cmd/prometheus/main.go
+++ b/cmd/prometheus/main.go
@@ -270,6 +270,9 @@ func main() {
 		"Prefix for the internal routes of web endpoints. Defaults to path of --web.external-url.").
 		PlaceHolder("<path>").StringVar(&cfg.web.RoutePrefix)
 
+	a.Flag("web.local-assets", "Path to static asset/templates directory.").
+		Default("/usr/share/prometheus/web/").StringVar(&cfg.web.LocalAssets)
+
 	a.Flag("web.user-assets", "Path to user asset directory, available at /user.").
 		PlaceHolder("<path>").StringVar(&cfg.web.UserAssetsPath)
 
diff --git a/console_libraries/prom.lib b/console_libraries/prom.lib
index d7d436f..802810a 100644
--- a/console_libraries/prom.lib
+++ b/console_libraries/prom.lib
@@ -1,16 +1,16 @@
 {{/* vim: set ft=html: */}}
 {{/* Load Prometheus console library JS/CSS. Should go in <head> */}}
 {{ define "prom_console_head" }}
-<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/vendor/rickshaw/rickshaw.min.css">
-<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/vendor/bootstrap-4.5.2/css/bootstrap.min.css">
+<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/rickshaw/rickshaw.min.css">
+<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/bootstrap4/css/bootstrap.min.css">
 <link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/css/prom_console.css">
-<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/vendor/bootstrap4-glyphicons/css/bootstrap-glyphicons.min.css">
-<script src="{{ pathPrefix }}/classic/static/vendor/rickshaw/vendor/d3.v3.js"></script>
+<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/vendor/bootstrap4-glyphicons/css/bootstrap-glyphicons.css">
+<script src="{{ pathPrefix }}/classic/static/d3/d3.min.js"></script>
 <script src="{{ pathPrefix }}/classic/static/vendor/rickshaw/vendor/d3.layout.min.js"></script>
-<script src="{{ pathPrefix }}/classic/static/vendor/rickshaw/rickshaw.min.js"></script>
-<script src="{{ pathPrefix }}/classic/static/vendor/js/jquery-3.5.1.min.js"></script>
-<script src="{{ pathPrefix }}/classic/static/vendor/js/popper.min.js"></script>
-<script src="{{ pathPrefix }}/classic/static/vendor/bootstrap-4.5.2/js/bootstrap.min.js"></script>
+<script src="{{ pathPrefix }}/classic/static/rickshaw/rickshaw.min.js"></script>
+<script src="{{ pathPrefix }}/classic/static/jquery/jquery.min.js"></script>
+<script src="{{ pathPrefix }}/classic/static/popper.js"></script>
+<script src="{{ pathPrefix }}/classic/static/bootstrap4/js/bootstrap.min.js"></script>
 
 <script>
 var PATH_PREFIX = "{{ pathPrefix }}";
diff --git a/web/ui/ui.go b/web/ui/ui.go
index 99ae34e..842dd4a 100644
--- a/web/ui/ui.go
+++ b/web/ui/ui.go
@@ -11,8 +11,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//go:build !builtinassets
-// +build !builtinassets
+//go:build ignore
+// +build ignore
 
 package ui
 
diff --git a/web/web.go b/web/web.go
index b9af281..d261ddb 100644
--- a/web/web.go
+++ b/web/web.go
@@ -19,6 +19,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
+	"io/fs"
 	stdlog "log"
 	"math"
 	"net"
@@ -46,7 +47,6 @@ import (
 	io_prometheus_client "github.com/prometheus/client_model/go"
 	"github.com/prometheus/common/model"
 	"github.com/prometheus/common/route"
-	"github.com/prometheus/common/server"
 	toolkit_web "github.com/prometheus/exporter-toolkit/web"
 	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
 	"go.uber.org/atomic"
@@ -61,7 +61,6 @@ import (
 	"github.com/prometheus/prometheus/template"
 	"github.com/prometheus/prometheus/util/httputil"
 	api_v1 "github.com/prometheus/prometheus/web/api/v1"
-	"github.com/prometheus/prometheus/web/ui"
 )
 
 // Paths that are handled by the React / Reach router that should all be served the main React app's index.html.
@@ -247,7 +246,7 @@ type Options struct {
 	MaxConnections             int
 	ExternalURL                *url.URL
 	RoutePrefix                string
-	UseLocalAssets             bool
+	LocalAssets                string
 	UserAssetsPath             string
 	ConsoleTemplatesPath       string
 	ConsoleLibrariesPath       string
@@ -370,11 +369,7 @@ func New(logger log.Logger, o *Options) *Handler {
 	})
 
 	// The console library examples at 'console_libraries/prom.lib' still depend on old asset files being served under `classic`.
-	router.Get("/classic/static/*filepath", func(w http.ResponseWriter, r *http.Request) {
-		r.URL.Path = path.Join("/static", route.Param(r.Context(), "filepath"))
-		fs := server.StaticFileServer(ui.Assets)
-		fs.ServeHTTP(w, r)
-	})
+	router.Get("/classic/static/*filepath", route.FileServe(filepath.Join(o.LocalAssets, "static")))
 
 	router.Get("/version", h.version)
 	router.Get("/metrics", promhttp.Handler().ServeHTTP)
@@ -386,8 +381,11 @@ func New(logger log.Logger, o *Options) *Handler {
 	router.Get("/consoles/*filepath", readyf(h.consoles))
 
 	serveReactApp := func(w http.ResponseWriter, r *http.Request) {
-		f, err := ui.Assets.Open("/static/react/index.html")
-		if err != nil {
+		f, err := os.Open(filepath.Join(o.LocalAssets, "static/react/index.html"))
+		if errors.Is(err, fs.ErrNotExist) {
+			http.ServeFile(w, r, "/usr/share/prometheus/web/no-ui/index.html")
+			return
+		} else if err != nil {
 			w.WriteHeader(http.StatusInternalServerError)
 			fmt.Fprintf(w, "Error opening React index.html: %v", err)
 			return
@@ -424,20 +422,13 @@ func New(logger log.Logger, o *Options) *Handler {
 	// The favicon and manifest are bundled as part of the React app, but we want to serve
 	// them on the root.
 	for _, p := range []string{"/favicon.ico", "/manifest.json"} {
-		assetPath := "/static/react" + p
 		router.Get(p, func(w http.ResponseWriter, r *http.Request) {
-			r.URL.Path = assetPath
-			fs := server.StaticFileServer(ui.Assets)
-			fs.ServeHTTP(w, r)
+			http.ServeFile(w, r, filepath.Join(o.LocalAssets, "static/react", p))
 		})
 	}
 
 	// Static files required by the React app.
-	router.Get("/static/*filepath", func(w http.ResponseWriter, r *http.Request) {
-		r.URL.Path = path.Join("/static/react/static", route.Param(r.Context(), "filepath"))
-		fs := server.StaticFileServer(ui.Assets)
-		fs.ServeHTTP(w, r)
-	})
+	router.Get("/static/*filepath", route.FileServe(filepath.Join(o.LocalAssets, "static/react/static")))
 
 	if o.UserAssetsPath != "" {
 		router.Get("/user/*filepath", route.FileServe(o.UserAssetsPath))
