From: Martina Ferrari <tina@debian.org>
Date: Tue, 12 Jul 2016 03:23:14 +0000
Subject: Do not embed blobs

Forwarded: not-needed
Last-Update: 2023-02-03

Upstream is interested in keeping the resources embedded, this patch is to
comply with Debian policy.
---
 cli/check_config.go        |  2 +-
 cli/template_render.go     |  2 +-
 cmd/alertmanager/main.go   |  5 +++--
 notify/email/email_test.go |  2 +-
 notify/sns/sns_test.go     |  2 +-
 notify/test/test.go        |  2 +-
 template/template.go       |  9 +++++----
 template/template_test.go  |  6 +++---
 ui/app/src/Views.elm       |  4 ++--
 ui/web.go                  | 26 ++++++++++----------------
 10 files changed, 28 insertions(+), 32 deletions(-)

diff --git a/cli/check_config.go b/cli/check_config.go
index c024c27..f3b5ae2 100644
--- a/cli/check_config.go
+++ b/cli/check_config.go
@@ -84,7 +84,7 @@ func CheckConfig(args []string) error {
 			fmt.Printf(" - %d receivers\n", len(cfg.Receivers))
 			fmt.Printf(" - %d templates\n", len(cfg.Templates))
 			if len(cfg.Templates) > 0 {
-				_, err = template.FromGlobs(cfg.Templates)
+				_, err = template.FromGlobs("", cfg.Templates)
 				if err != nil {
 					fmt.Printf("  FAILED: %s\n", err)
 					failed++
diff --git a/cli/template_render.go b/cli/template_render.go
index b02df7c..11a7d20 100644
--- a/cli/template_render.go
+++ b/cli/template_render.go
@@ -108,7 +108,7 @@ func configureTemplateRenderCmd(cc *kingpin.CmdClause) {
 }
 
 func (c *templateRenderCmd) render(ctx context.Context, _ *kingpin.ParseContext) error {
-	tmpl, err := template.FromGlobs(c.templateFilesGlobs)
+	tmpl, err := template.FromGlobs("", c.templateFilesGlobs)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/alertmanager/main.go b/cmd/alertmanager/main.go
index 87cdab8..872ad1d 100644
--- a/cmd/alertmanager/main.go
+++ b/cmd/alertmanager/main.go
@@ -156,6 +156,7 @@ func run() int {
 		routePrefix    = kingpin.Flag("web.route-prefix", "Prefix for the internal routes of web endpoints. Defaults to path of --web.external-url.").String()
 		getConcurrency = kingpin.Flag("web.get-concurrency", "Maximum number of GET requests processed concurrently. If negative or zero, the limit is GOMAXPROC or 8, whichever is larger.").Default("0").Int()
 		httpTimeout    = kingpin.Flag("web.timeout", "Timeout for HTTP requests. If negative or zero, no timeout is set.").Default("0").Duration()
+		uiPath = kingpin.Flag("web.ui-path", "Path to static UI directory.").Default("/usr/share/prometheus/alertmanager/ui/").String()
 
 		memlimitRatio = kingpin.Flag("auto-gomemlimit.ratio", "The ratio of reserved GOMEMLIMIT memory to the detected maximum container or system memory. The value must be greater than 0 and less than or equal to 1.").
 				Default("0.9").Float64()
@@ -415,7 +416,7 @@ func run() int {
 		configLogger,
 	)
 	configCoordinator.Subscribe(func(conf *config.Config) error {
-		tmpl, err = template.FromGlobs(conf.Templates)
+		tmpl, err = template.FromGlobs("/usr/share/prometheus/alertmanager", conf.Templates)
 		if err != nil {
 			return fmt.Errorf("failed to parse templates: %w", err)
 		}
@@ -546,7 +547,7 @@ func run() int {
 
 	webReload := make(chan chan error)
 
-	ui.Register(router, webReload, logger)
+	ui.Register(router, webReload, *uiPath)
 	reactapp.Register(router, logger)
 
 	mux := api.Register(router, *routePrefix)
diff --git a/notify/email/email_test.go b/notify/email/email_test.go
index 0eec715..ff24451 100644
--- a/notify/email/email_test.go
+++ b/notify/email/email_test.go
@@ -209,7 +209,7 @@ func prepare(cfg *config.EmailConfig) (*template.Template, *types.Alert, error)
 		cfg.Headers = make(map[string]string)
 	}
 
-	tmpl, err := template.FromGlobs([]string{})
+	tmpl, err := template.FromGlobs("../../template", []string{})
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/notify/sns/sns_test.go b/notify/sns/sns_test.go
index bb64b11..a4c2baa 100644
--- a/notify/sns/sns_test.go
+++ b/notify/sns/sns_test.go
@@ -143,7 +143,7 @@ func TestNotifyWithInvalidTemplate(t *testing.T) {
 
 // CreateTmpl returns a ready-to-use template.
 func createTmpl(t *testing.T) *template.Template {
-	tmpl, err := template.FromGlobs([]string{})
+	tmpl, err := template.FromGlobs("", []string{})
 	require.NoError(t, err)
 	tmpl.ExternalURL, _ = url.Parse("http://am")
 	return tmpl
diff --git a/notify/test/test.go b/notify/test/test.go
index 75729b2..c13e417 100644
--- a/notify/test/test.go
+++ b/notify/test/test.go
@@ -128,7 +128,7 @@ func DefaultRetryCodes() []int {
 
 // CreateTmpl returns a ready-to-use template.
 func CreateTmpl(t *testing.T) *template.Template {
-	tmpl, err := template.FromGlobs([]string{})
+	tmpl, err := template.FromGlobs("../../template", []string{})
 	require.NoError(t, err)
 	tmpl.ExternalURL, _ = url.Parse("http://am")
 	return tmpl
diff --git a/template/template.go b/template/template.go
index 92f8323..e0d308e 100644
--- a/template/template.go
+++ b/template/template.go
@@ -18,7 +18,7 @@ import (
 	tmplhtml "html/template"
 	"io"
 	"net/url"
-	"path"
+	"os"
 	"path/filepath"
 	"regexp"
 	"sort"
@@ -31,7 +31,6 @@ import (
 	"golang.org/x/text/cases"
 	"golang.org/x/text/language"
 
-	"github.com/prometheus/alertmanager/asset"
 	"github.com/prometheus/alertmanager/types"
 )
 
@@ -67,16 +66,17 @@ func New(options ...Option) (*Template, error) {
 
 // FromGlobs calls ParseGlob on all path globs provided and returns the
 // resulting Template.
-func FromGlobs(paths []string, options ...Option) (*Template, error) {
+func FromGlobs(defaultTemplatePath string, paths []string, options ...Option) (*Template, error) {
 	t, err := New(options...)
 	if err != nil {
 		return nil, err
 	}
 
+	if defaultTemplatePath != "" {
 	defaultTemplates := []string{"default.tmpl", "email.tmpl"}
 
 	for _, file := range defaultTemplates {
-		f, err := asset.Assets.Open(path.Join("/templates", file))
+		f, err := os.Open(filepath.Join(defaultTemplatePath, file))
 		if err != nil {
 			return nil, err
 		}
@@ -86,6 +86,7 @@ func FromGlobs(paths []string, options ...Option) (*Template, error) {
 		}
 		f.Close()
 	}
+	}
 
 	for _, tp := range paths {
 		if err := t.FromGlob(tp); err != nil {
diff --git a/template/template_test.go b/template/template_test.go
index ea8915b..f306bae 100644
--- a/template/template_test.go
+++ b/template/template_test.go
@@ -287,7 +287,7 @@ func TestData(t *testing.T) {
 }
 
 func TestTemplateExpansion(t *testing.T) {
-	tmpl, err := FromGlobs([]string{})
+	tmpl, err := FromGlobs("../template", []string{})
 	require.NoError(t, err)
 
 	for _, tc := range []struct {
@@ -450,7 +450,7 @@ func TestTemplateExpansionWithOptions(t *testing.T) {
 	} {
 		tc := tc
 		t.Run(tc.title, func(t *testing.T) {
-			tmpl, err := FromGlobs([]string{}, tc.options...)
+			tmpl, err := FromGlobs("../template", []string{}, tc.options...)
 			require.NoError(t, err)
 			f := tmpl.ExecuteTextString
 			if tc.html {
@@ -469,7 +469,7 @@ func TestTemplateExpansionWithOptions(t *testing.T) {
 
 // This test asserts that template functions are thread-safe.
 func TestTemplateFuncs(t *testing.T) {
-	tmpl, err := FromGlobs([]string{})
+	tmpl, err := FromGlobs("../template", []string{})
 	require.NoError(t, err)
 
 	for _, tc := range []struct {
diff --git a/ui/app/src/Views.elm b/ui/app/src/Views.elm
index 4bbcd06..ce29db8 100644
--- a/ui/app/src/Views.elm
+++ b/ui/app/src/Views.elm
@@ -55,8 +55,8 @@ failureView model err =
 renderCSS : String -> Html Msg
 renderCSS assetsUrl =
     div []
-        [ cssNode (assetsUrl ++ "lib/bootstrap-4.0.0-alpha.6-dist/css/bootstrap.min.css") BootstrapCSSLoaded
-        , cssNode (assetsUrl ++ "lib/font-awesome-4.7.0/css/font-awesome.min.css") FontAwesomeCSSLoaded
+        [ cssNode (assetsUrl ++ "lib/bootstrap/css/bootstrap.min.css") BootstrapCSSLoaded
+        , cssNode (assetsUrl ++ "lib/font-awesome/css/font-awesome.min.css") FontAwesomeCSSLoaded
         , cssNode (assetsUrl ++ "lib/elm-datepicker/css/elm-datepicker.css") ElmDatepickerCSSLoaded
         ]
 
diff --git a/ui/web.go b/ui/web.go
index 3b81135..aab56b4 100644
--- a/ui/web.go
+++ b/ui/web.go
@@ -15,51 +15,45 @@ package ui
 
 import (
 	"fmt"
-	"log/slog"
 	"net/http"
 	_ "net/http/pprof" // Comment this line to disable pprof endpoint.
 	"path"
+	"os"
 
 	"github.com/prometheus/client_golang/prometheus/promhttp"
 	"github.com/prometheus/common/route"
-
-	"github.com/prometheus/alertmanager/asset"
 )
 
 // Register registers handlers to serve files for the web interface.
-func Register(r *route.Router, reloadCh chan<- chan error, logger *slog.Logger) {
+func Register(r *route.Router, reloadCh chan<- chan error, uiPath string) {
+	if _, err := os.Stat(path.Join(uiPath, "index.html")); os.IsNotExist(err) {
+		// UI not installed, use stub site.
+		uiPath = "/usr/share/prometheus/alertmanager/no-ui"
+	}
 	r.Get("/metrics", promhttp.Handler().ServeHTTP)
 
 	r.Get("/", func(w http.ResponseWriter, req *http.Request) {
 		disableCaching(w)
 
-		req.URL.Path = "/static/"
-		fs := http.FileServer(asset.Assets)
-		fs.ServeHTTP(w, req)
+		http.ServeFile(w, req, path.Join(uiPath, "index.html"))
 	})
 
 	r.Get("/script.js", func(w http.ResponseWriter, req *http.Request) {
 		disableCaching(w)
 
-		req.URL.Path = "/static/script.js"
-		fs := http.FileServer(asset.Assets)
-		fs.ServeHTTP(w, req)
+		http.ServeFile(w, req, path.Join(uiPath, "script.js"))
 	})
 
 	r.Get("/favicon.ico", func(w http.ResponseWriter, req *http.Request) {
 		disableCaching(w)
 
-		req.URL.Path = "/static/favicon.ico"
-		fs := http.FileServer(asset.Assets)
-		fs.ServeHTTP(w, req)
+		http.ServeFile(w, req, path.Join(uiPath, "favicon.ico"))
 	})
 
 	r.Get("/lib/*path", func(w http.ResponseWriter, req *http.Request) {
 		disableCaching(w)
 
-		req.URL.Path = path.Join("/static/lib", route.Param(req.Context(), "path"))
-		fs := http.FileServer(asset.Assets)
-		fs.ServeHTTP(w, req)
+		http.ServeFile(w, req, path.Join(uiPath, "/lib", route.Param(req.Context(), "path")))
 	})
 
 	r.Post("/-/reload", func(w http.ResponseWriter, req *http.Request) {
