From 7ccd37eaa255ac9b575a72a7c84b644c2c125bad Mon Sep 17 00:00:00 2001 From: Samuel Padgett Date: Wed, 5 Dec 2018 12:47:19 -0500 Subject: [PATCH] Update console for monitoring changes * Proxy to port 9092, which has the tenancy proxy in front of it * Remove the `CAN_LIST_NS` check since users can now see metrics in their own namespaces https://jira.coreos.com/browse/CONSOLE-1035 --- cmd/bridge/main.go | 8 +++ frontend/public/components/build.tsx | 8 ++- .../public/components/cluster-overview.jsx | 9 +-- frontend/public/components/graphs/base.jsx | 10 +-- frontend/public/components/graphs/index.jsx | 36 ++-------- frontend/public/components/namespace.jsx | 5 +- frontend/public/components/overview/index.tsx | 7 +- frontend/public/components/pod.jsx | 6 +- server/server.go | 71 +++++++++++-------- 9 files changed, 78 insertions(+), 82 deletions(-) diff --git a/cmd/bridge/main.go b/cmd/bridge/main.go index 909e72c5271..ce5f7243f42 100644 --- a/cmd/bridge/main.go +++ b/cmd/bridge/main.go @@ -31,6 +31,9 @@ const ( // Well-known location of Prometheus service for OpenShift. This is only accessible in-cluster. openshiftPrometheusHost = "prometheus-k8s.openshift-monitoring.svc:9091" + // The tenancy service port (9092) performs RBAC checks for namespace-specific queries. + openshiftPrometheusTenancyHost = "prometheus-k8s.openshift-monitoring.svc:9092" + // Well-known location of Alert Manager service for OpenShift. This is only accessible in-cluster. openshiftAlertManagerHost = "alertmanager-main.openshift-monitoring.svc:9094" ) @@ -262,6 +265,11 @@ func main() { HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, Endpoint: &url.URL{Scheme: "https", Host: openshiftPrometheusHost, Path: "/api"}, } + srv.PrometheusTenancyProxyConfig = &proxy.Config{ + TLSClientConfig: monitoringProxyTLSConfig, + HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + Endpoint: &url.URL{Scheme: "https", Host: openshiftPrometheusTenancyHost, Path: "/api"}, + } srv.AlertManagerProxyConfig = &proxy.Config{ TLSClientConfig: monitoringProxyTLSConfig, HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, diff --git a/frontend/public/components/build.tsx b/frontend/public/components/build.tsx index bf934d38992..ef93c26315b 100644 --- a/frontend/public/components/build.tsx +++ b/frontend/public/components/build.tsx @@ -87,16 +87,18 @@ const BuildGraphs = requirePrometheus(({build}) => { return null; } + const namespace = build.metadata.namespace; + return
- +
- +
- +
diff --git a/frontend/public/components/cluster-overview.jsx b/frontend/public/components/cluster-overview.jsx index fcad9c87b60..eb7b876b0ba 100644 --- a/frontend/public/components/cluster-overview.jsx +++ b/frontend/public/components/cluster-overview.jsx @@ -2,7 +2,6 @@ import * as _ from 'lodash-es'; import * as React from 'react'; import * as classNames from 'classnames'; import { Helmet } from 'react-helmet'; -import { Link } from 'react-router-dom'; import { EventsList } from './events'; import { SoftwareDetails } from './software-details'; @@ -20,15 +19,11 @@ import { StatusBox, } from './utils'; - -const DashboardLink = ({to, id}) => View Grafana Dashboard; - -const Graphs = requirePrometheus(({namespace, isOpenShift}) => { +const Graphs = requirePrometheus(({namespace}) => { return

Health

- {!isOpenShift && }
@@ -38,7 +33,6 @@ const Graphs = requirePrometheus(({namespace, isOpenShift}) => {

Control Plane Status

- {!isOpenShift && }
@@ -62,7 +56,6 @@ const Graphs = requirePrometheus(({namespace, isOpenShift}) => {

Capacity Planning

- {!isOpenShift && }
diff --git a/frontend/public/components/graphs/base.jsx b/frontend/public/components/graphs/base.jsx index 0a62e62cf60..6c401051c7e 100644 --- a/frontend/public/components/graphs/base.jsx +++ b/frontend/public/components/graphs/base.jsx @@ -7,7 +7,7 @@ import * as classNames from 'classnames'; import { coFetchJSON } from '../../co-fetch'; import { SafetyFirst } from '../safety-first'; -import { prometheusBasePath } from './index'; +import { prometheusBasePath, prometheusTenancyBasePath } from './index'; import { MonitoringRoutes } from '../../monitoring'; export class BaseGraph extends SafetyFirst { @@ -44,13 +44,14 @@ export class BaseGraph extends SafetyFirst { }]; } - const basePath = this.props.basePath || prometheusBasePath; + const basePath = this.props.basePath || (this.props.namespace ? prometheusTenancyBasePath : prometheusBasePath); const pollInterval = timeSpan / 120 || 15000; const stepSize = pollInterval / 1000; const promises = queries.map(q => { + const nsParam = this.props.namespace ? `&namespace=${encodeURIComponent(this.props.namespace)}` : ''; const url = this.timeSpan - ? `${basePath}/api/v1/query_range?query=${encodeURIComponent(q.query)}&start=${start / 1000}&end=${end / 1000}&step=${stepSize}` - : `${basePath}/api/v1/query?query=${encodeURIComponent(q.query)}`; + ? `${basePath}/api/v1/query_range?query=${encodeURIComponent(q.query)}&start=${start / 1000}&end=${end / 1000}&step=${stepSize}${nsParam}` + : `${basePath}/api/v1/query?query=${encodeURIComponent(q.query)}${nsParam}`; return coFetchJSON(url); }); Promise.all(promises) @@ -142,6 +143,7 @@ export class BaseGraph extends SafetyFirst { } BaseGraph.propTypes = { + namespace: PropTypes.string, query: PropTypes.oneOfType([ PropTypes.string, PropTypes.arrayOf( diff --git a/frontend/public/components/graphs/index.jsx b/frontend/public/components/graphs/index.jsx index f7451765440..6ace8d1623d 100644 --- a/frontend/public/components/graphs/index.jsx +++ b/frontend/public/components/graphs/index.jsx @@ -2,10 +2,11 @@ import * as React from 'react'; import { AsyncComponent } from '../utils/async'; -import { FLAGS, connectToFlags, flagPending } from '../../features'; +import { FLAGS, connectToFlags } from '../../features'; export { Status, errorStatus } from './status'; export const prometheusBasePath = window.SERVER_FLAGS.prometheusBaseURL; +export const prometheusTenancyBasePath = window.SERVER_FLAGS.prometheusTenancyBaseURL; export const alertManagerBasePath = window.SERVER_FLAGS.alertManagerBaseURL; export const Bar = props => import('./graph-loader').then(c => c.Bar)} {...props} />; @@ -14,39 +15,16 @@ export const Line = props => import('./graph-loade export const Scalar = props => import('./graph-loader').then(c => c.Scalar)} {...props} />; export const Donut = props => import('./graph-loader').then(c => c.Donut)} {...props} />; -const canAccessPrometheus = (openshiftFlag, prometheusFlag, canListNS) => { - if (flagPending(prometheusFlag) || flagPending(openshiftFlag)) { - // Wait for feature detection to complete before showing graphs so we don't show them, then hide them. - return false; - } - - if (!prometheusFlag) { - return false; - } - - if (!window.SERVER_FLAGS.prometheusBaseURL) { - // Proxy has not been set up. - return false; - } - - if (!openshiftFlag) { - return true; - } - - // In OpenShift, the user must be able to list namespaces to query Prometheus. - return canListNS; -}; +const canAccessPrometheus = (prometheusFlag) => prometheusFlag && !!prometheusBasePath && !!prometheusTenancyBasePath; // HOC that will hide WrappedComponent when Prometheus isn't configured or the user doesn't have permission to query Prometheus. -/** @type {(WrappedComponent: React.SFC

) => React.ComponentType

} */ -export const requirePrometheus = WrappedComponent => connectToFlags(FLAGS.OPENSHIFT, FLAGS.PROMETHEUS, FLAGS.CAN_LIST_NS)(props => { +/** @type {(WrappedComponent: React.SFC

) => React.ComponentType

} */ +export const requirePrometheus = WrappedComponent => connectToFlags(FLAGS.PROMETHEUS)(props => { const { flags } = props; - const openshiftFlag = flags[FLAGS.OPENSHIFT]; const prometheusFlag = flags[FLAGS.PROMETHEUS]; - const canListNS = flags[FLAGS.CAN_LIST_NS]; - if (!canAccessPrometheus(openshiftFlag, prometheusFlag, canListNS)) { + if (!canAccessPrometheus(prometheusFlag)) { return null; } - return ; + return ; }); diff --git a/frontend/public/components/namespace.jsx b/frontend/public/components/namespace.jsx index 6371b783bee..05df4984a34 100644 --- a/frontend/public/components/namespace.jsx +++ b/frontend/public/components/namespace.jsx @@ -164,7 +164,7 @@ class PullSecret extends SafetyFirst { export const NamespaceLineCharts = ({ns}) =>

-
]} />
-
export const TopPodsBarChart = ({ns}) => ( diff --git a/frontend/public/components/overview/index.tsx b/frontend/public/components/overview/index.tsx index 17b79e748a7..386d4f80631 100644 --- a/frontend/public/components/overview/index.tsx +++ b/frontend/public/components/overview/index.tsx @@ -12,7 +12,7 @@ import { Toolbar, EmptyState } from 'patternfly-react'; import { coFetchJSON } from '../../co-fetch'; import { getBuildNumber } from '../../module/k8s/builds'; -import { prometheusBasePath } from '../graphs'; +import { prometheusTenancyBasePath } from '../graphs'; import { TextFilter } from '../factory'; import { UIActions, formatNamespacedRouteForResource } from '../../ui/ui-actions'; import { @@ -504,8 +504,7 @@ class OverviewMainContent_ extends React.Component { - if (!prometheusBasePath) { - // Component is not mounted or proxy has not been set up. + if (!prometheusTenancyBasePath) { return; } @@ -516,7 +515,7 @@ class OverviewMainContent_ extends React.Component { - const url = `${prometheusBasePath}/api/v1/query?query=${encodeURIComponent(query)}`; + const url = `${prometheusTenancyBasePath}/api/v1/query?namespace=${namespace}&query=${encodeURIComponent(query)}`; return coFetchJSON(url).then(({ data: {result} }) => { const byPod: MetricValuesByPod = result.reduce((acc, { metric, value }) => { acc[metric.pod_name] = Number(value[1]); diff --git a/frontend/public/components/pod.jsx b/frontend/public/components/pod.jsx index 4c6431e69d2..1f56d74f65c 100644 --- a/frontend/public/components/pod.jsx +++ b/frontend/public/components/pod.jsx @@ -133,13 +133,13 @@ export const PodContainerTable = ({heading, containers, pod}) =>
- +
- +
- +
diff --git a/server/server.go b/server/server.go index 4c26ee1a5fc..24a3d7d7229 100644 --- a/server/server.go +++ b/server/server.go @@ -23,14 +23,15 @@ const ( indexPageTemplateName = "index.html" tokenizerPageTemplateName = "tokener.html" - authLoginEndpoint = "/auth/login" - AuthLoginCallbackEndpoint = "/auth/callback" - AuthLoginSuccessEndpoint = "/" - AuthLoginErrorEndpoint = "/error" - authLogoutEndpoint = "/auth/logout" - k8sProxyEndpoint = "/api/kubernetes/" - prometheusProxyEndpoint = "/api/prometheus" - alertManagerProxyEndpoint = "/api/alertmanager" + authLoginEndpoint = "/auth/login" + AuthLoginCallbackEndpoint = "/auth/callback" + AuthLoginSuccessEndpoint = "/" + AuthLoginErrorEndpoint = "/error" + authLogoutEndpoint = "/auth/logout" + k8sProxyEndpoint = "/api/kubernetes/" + prometheusProxyEndpoint = "/api/prometheus" + prometheusTenancyProxyEndpoint = "/api/prometheus-tenancy" + alertManagerProxyEndpoint = "/api/alertmanager" ) var ( @@ -38,23 +39,24 @@ var ( ) type jsGlobals struct { - ConsoleVersion string `json:"consoleVersion"` - AuthDisabled bool `json:"authDisabled"` - KubectlClientID string `json:"kubectlClientID"` - BasePath string `json:"basePath"` - LoginURL string `json:"loginURL"` - LoginSuccessURL string `json:"loginSuccessURL"` - LoginErrorURL string `json:"loginErrorURL"` - LogoutURL string `json:"logoutURL"` - LogoutRedirect string `json:"logoutRedirect"` - KubeAPIServerURL string `json:"kubeAPIServerURL"` - PrometheusBaseURL string `json:"prometheusBaseURL"` - AlertManagerBaseURL string `json:"alertManagerBaseURL"` - Branding string `json:"branding"` - DocumentationBaseURL string `json:"documentationBaseURL"` - ClusterName string `json:"clusterName"` - GoogleTagManagerID string `json:"googleTagManagerID"` - LoadTestFactor int `json:"loadTestFactor"` + ConsoleVersion string `json:"consoleVersion"` + AuthDisabled bool `json:"authDisabled"` + KubectlClientID string `json:"kubectlClientID"` + BasePath string `json:"basePath"` + LoginURL string `json:"loginURL"` + LoginSuccessURL string `json:"loginSuccessURL"` + LoginErrorURL string `json:"loginErrorURL"` + LogoutURL string `json:"logoutURL"` + LogoutRedirect string `json:"logoutRedirect"` + KubeAPIServerURL string `json:"kubeAPIServerURL"` + PrometheusBaseURL string `json:"prometheusBaseURL"` + PrometheusTenancyBaseURL string `json:"prometheusTenancyBaseURL"` + AlertManagerBaseURL string `json:"alertManagerBaseURL"` + Branding string `json:"branding"` + DocumentationBaseURL string `json:"documentationBaseURL"` + ClusterName string `json:"clusterName"` + GoogleTagManagerID string `json:"googleTagManagerID"` + LoadTestFactor int `json:"loadTestFactor"` } type Server struct { @@ -75,9 +77,10 @@ type Server struct { LoadTestFactor int DexClient api.DexClient // A client with the correct TLS setup for communicating with the API server. - K8sClient *http.Client - PrometheusProxyConfig *proxy.Config - AlertManagerProxyConfig *proxy.Config + K8sClient *http.Client + PrometheusProxyConfig *proxy.Config + PrometheusTenancyProxyConfig *proxy.Config + AlertManagerProxyConfig *proxy.Config } func (s *Server) authDisabled() bool { @@ -85,7 +88,7 @@ func (s *Server) authDisabled() bool { } func (s *Server) prometheusProxyEnabled() bool { - return s.PrometheusProxyConfig != nil + return s.PrometheusProxyConfig != nil && s.PrometheusTenancyProxyConfig != nil } func (s *Server) alertManagerProxyEnabled() bool { @@ -189,6 +192,15 @@ func (s *Server) HTTPHandler() http.Handler { prometheusProxy.ServeHTTP(w, r) })), ) + prometheusTenancyProxyAPIPath := prometheusTenancyProxyEndpoint + "/api/" + prometheusTenancyProxy := proxy.NewProxy(s.PrometheusTenancyProxyConfig) + handle(prometheusTenancyProxyAPIPath, http.StripPrefix( + proxy.SingleJoiningSlash(s.BaseURL.Path, prometheusTenancyProxyAPIPath), + authHandlerWithUser(func(user *auth.User, w http.ResponseWriter, r *http.Request) { + r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", user.Token)) + prometheusTenancyProxy.ServeHTTP(w, r) + })), + ) } if s.alertManagerProxyEnabled() { @@ -251,6 +263,7 @@ func (s *Server) indexHandler(w http.ResponseWriter, r *http.Request) { if s.prometheusProxyEnabled() { jsg.PrometheusBaseURL = proxy.SingleJoiningSlash(s.BaseURL.Path, prometheusProxyEndpoint) + jsg.PrometheusTenancyBaseURL = proxy.SingleJoiningSlash(s.BaseURL.Path, prometheusTenancyProxyEndpoint) } if s.alertManagerProxyEnabled() {