Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include and serve static prometheus web UI in admin portal #3303

Merged
merged 2 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/admin-portal.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ The portal front end lives in the top level directory of the ARO-RP repo within

The front end code is compiled into go code using the bindata golang module. This front end code is then served through the RP.

The admin portal also serves a static Prometheus web frontend. The contents are taken from a Prometheus release's web-ui artifact (e.g. [2.48](https://github.com/prometheus/prometheus/releases/download/v2.48.0/prometheus-web-ui-2.48.0.tar.gz)), and the static/react subdirectory is mirrored to this repository's pkg/portal/assets/prometheus-ui directory.

## Developing

Expand Down
1 change: 1 addition & 0 deletions pkg/portal/assets/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ import (

//go:embed v1/*
//go:embed v2/*
//go:embed prometheus-ui/*
var EmbeddedFiles embed.FS
14 changes: 14 additions & 0 deletions pkg/portal/assets/prometheus-ui/asset-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"files": {
"main.css": "./static/css/main.132f8bd2.css",
"main.js": "./static/js/main.8abd4fa4.js",
"static/media/codicon.ttf": "./static/media/codicon.b3726f0165bf67ac6849.ttf",
"static/media/prometheus_logo_grey.svg": "./static/media/prometheus_logo_grey.3cf697e5443028ca5e5255b93c7906c5.svg",
"index.html": "./index.html",
"static/media/index.cjs": "./static/media/index.cd351d7c31d0d3fccf96.cjs"
},
"entrypoints": [
"static/css/main.132f8bd2.css",
"static/js/main.8abd4fa4.js"
]
}
Binary file added pkg/portal/assets/prometheus-ui/favicon.ico
Binary file not shown.
1 change: 1 addition & 0 deletions pkg/portal/assets/prometheus-ui/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="theme-color" content="#000000"/><script>const GLOBAL_CONSOLES_LINK="CONSOLES_LINK_PLACEHOLDER",GLOBAL_AGENT_MODE="AGENT_MODE_PLACEHOLDER",GLOBAL_READY="READY_PLACEHOLDER"</script><link rel="manifest" href="./manifest.json" crossorigin="use-credentials"/><title>TITLE_PLACEHOLDER</title><script defer="defer" src="./static/js/main.8abd4fa4.js"></script><link href="./static/css/main.132f8bd2.css" rel="stylesheet"></head><body class="bootstrap"><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
15 changes: 15 additions & 0 deletions pkg/portal/assets/prometheus-ui/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"short_name": "Prometheus UI",
"name": "Prometheus Server Web Interface",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions pkg/portal/assets/prometheus-ui/static/js/main.8abd4fa4.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/

/*!
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/

/*!
* is-plain-object <https://github.com/jonschlinkert/is-plain-object>
*
* Copyright (c) 2014-2017, Jon Schlinkert.
* Released under the MIT License.
*/

/*!
* jQuery JavaScript Library v3.7.0
* https://jquery.com/
*
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
*
* Date: 2023-05-11T18:29Z
*/

/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */

/*!@preserve
* Tempus Dominus Bootstrap4 v5.39.0 (https://tempusdominus.github.io/bootstrap-4/)
* Copyright 2016-2020 Jonathan Peterson and contributors
* Licensed under MIT (https://github.com/tempusdominus/bootstrap-3/blob/master/LICENSE)
*/

/** @license React v0.20.2
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/** @license React v16.13.1
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/** @license React v17.0.2
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/** @license React v17.0.2
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/** @license React v17.0.2
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

//! Copyright (c) JS Foundation and other contributors

//! github.com/moment/moment-timezone

//! license : MIT

//! moment-timezone.js

//! moment.js

//! version : 0.5.43
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let urlAlphabet="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict",customAlphabet=(t,e=21)=>(a=e)=>{let l="",o=a;for(;o--;)l+=t[Math.random()*t.length|0];return l},nanoid=(t=21)=>{let e="",a=t;for(;a--;)e+=urlAlphabet[64*Math.random()|0];return e};module.exports={nanoid:nanoid,customAlphabet:customAlphabet};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 43 additions & 5 deletions pkg/portal/portal.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,9 @@ type portal struct {

dialer proxy.Dialer

templateV1 *template.Template
templateV2 *template.Template
templateV1 *template.Template
templateV2 *template.Template
templatePrometheus *template.Template

aad middleware.AAD

Expand Down Expand Up @@ -143,6 +144,11 @@ func (p *portal) setupRouter(kconfig *kubeconfig.Kubeconfig, prom *prometheus.Pr
return nil, err
}

assetPrometheus, err := assets.EmbeddedFiles.ReadFile("prometheus-ui/index.html")
if err != nil {
return nil, err
}

p.templateV1, err = template.New("index.html").Parse(string(assetv1))
if err != nil {
return nil, err
Expand All @@ -153,6 +159,11 @@ func (p *portal) setupRouter(kconfig *kubeconfig.Kubeconfig, prom *prometheus.Pr
return nil, err
}

p.templatePrometheus, err = template.New("index.html").Parse(string(assetPrometheus))
if err != nil {
return nil, err
}

unauthenticatedRouter := r.NewRoute().Subrouter()
bearerRoutes(unauthenticatedRouter, kconfig)
p.unauthenticatedRoutes(unauthenticatedRouter)
Expand Down Expand Up @@ -253,14 +264,19 @@ func (p *portal) unauthenticatedRoutes(r *mux.Router) {

func (p *portal) aadAuthenticatedRoutes(r *mux.Router, prom *prometheus.Prometheus, kconfig *kubeconfig.Kubeconfig, sshStruct *ssh.SSH) {
var names []string
var promNames []string

err := fs.WalkDir(assets.EmbeddedFiles, ".", func(path string, entry fs.DirEntry, err error) error {
if err != nil {
return err
}

if !entry.IsDir() {
names = append(names, path)
if strings.HasPrefix(path, "prometheus-ui") {
promNames = append(promNames, path)
} else {
names = append(names, path)
}
}
return nil
})
Expand All @@ -284,12 +300,19 @@ func (p *portal) aadAuthenticatedRoutes(r *mux.Router, prom *prometheus.Promethe

// prometheus
if prom != nil {
r.Path("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/microsoft.redhatopenshift/openshiftclusters/{resourceName}/prometheus/-/ready").Handler(prom.ReverseProxy)
r.PathPrefix("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/microsoft.redhatopenshift/openshiftclusters/{resourceName}/prometheus/api/").Handler(prom.ReverseProxy)

for _, name := range promNames {
fmtName := strings.TrimPrefix(name, "prometheus-ui/")
r.Methods(http.MethodGet).Path("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/microsoft.redhatopenshift/openshiftclusters/{resourceName}/prometheus/" + fmtName).HandlerFunc(p.serve(name))
}

r.Path("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/microsoft.redhatopenshift/openshiftclusters/{resourceName}/prometheus").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.URL.Path += "/"
http.Redirect(w, r, r.URL.String(), http.StatusTemporaryRedirect)
})

r.PathPrefix("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/microsoft.redhatopenshift/openshiftclusters/{resourceName}/prometheus/").Handler(prom.ReverseProxy)
r.PathPrefix("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/microsoft.redhatopenshift/openshiftclusters/{resourceName}/prometheus/").HandlerFunc(p.indexPrometheus)
}

//kubeconfig
Expand Down Expand Up @@ -350,6 +373,21 @@ func (p *portal) indexV2(w http.ResponseWriter, r *http.Request) {
http.ServeContent(w, r, "index.html", time.Time{}, bytes.NewReader(buf.Bytes()))
}

func (p *portal) indexPrometheus(w http.ResponseWriter, r *http.Request) {
buf := &bytes.Buffer{}

err := p.templatePrometheus.ExecuteTemplate(buf, "index.html", map[string]interface{}{
csrf.TemplateTag: csrf.TemplateField(r),
})

if err != nil {
p.internalServerError(w, err)
return
}

http.ServeContent(w, r, "index.html", time.Time{}, bytes.NewReader(buf.Bytes()))
}

// makeFetcher creates a cluster.FetchClient suitable for use by the Portal REST API
func (p *portal) makeFetcher(ctx context.Context, r *http.Request) (cluster.FetchClient, error) {
apiVars := mux.Vars(r)
Expand Down
Loading