Skip to content

Commit

Permalink
Merge pull request #909 from spadgett/update-monitoring-proxy
Browse files Browse the repository at this point in the history
Update console for monitoring changes
  • Loading branch information
openshift-merge-robot authored Jan 28, 2019
2 parents d86dcf4 + 7ccd37e commit 21d0a9d
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 82 deletions.
8 changes: 8 additions & 0 deletions cmd/bridge/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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"},
Expand Down
8 changes: 5 additions & 3 deletions frontend/public/components/build.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,18 @@ const BuildGraphs = requirePrometheus(({build}) => {
return null;
}

const namespace = build.metadata.namespace;

return <React.Fragment>
<div className="row">
<div className="col-md-4">
<Line title="Memory Usage" query={`pod_name:container_memory_usage_bytes:sum{pod_name='${podName}',container_name='',namespace='${build.metadata.namespace}'}`} />
<Line title="Memory Usage" namespace={namespace} query={`pod_name:container_memory_usage_bytes:sum{pod_name='${podName}',container_name='',namespace='${namespace}'}`} />
</div>
<div className="col-md-4">
<Line title="CPU Usage" query={`pod_name:container_cpu_usage:sum{pod_name='${podName}',container_name='',namespace='${build.metadata.namespace}'} * 1000`} />
<Line title="CPU Usage" namespace={namespace} query={`pod_name:container_cpu_usage:sum{pod_name='${podName}',container_name='',namespace='${namespace}'} * 1000`} />
</div>
<div className="col-md-4">
<Line title="Filesystem (bytes)" query={`pod_name:container_fs_usage_bytes:sum{pod_name='${podName}',container_name='',namespace='${build.metadata.namespace}'}`} />
<Line title="Filesystem (bytes)" namespace={namespace} query={`pod_name:container_fs_usage_bytes:sum{pod_name='${podName}',container_name='',namespace='${namespace}'}`} />
</div>
</div>

Expand Down
9 changes: 1 addition & 8 deletions frontend/public/components/cluster-overview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -20,15 +19,11 @@ import {
StatusBox,
} from './utils';


const DashboardLink = ({to, id}) => <Link id={id} className="co-external-link" target="_blank" to={to}>View Grafana Dashboard</Link>;

const Graphs = requirePrometheus(({namespace, isOpenShift}) => {
const Graphs = requirePrometheus(({namespace}) => {
return <React.Fragment>
<div className="group">
<div className="group__title">
<h2 className="h3">Health</h2>
{!isOpenShift && <DashboardLink id="qa_dashboard_k8s_health" to="/grafana/dashboard/db/kubernetes-cluster-health?orgId=1" />}
</div>
<div className="container-fluid group__body">
<Health namespace={namespace} />
Expand All @@ -38,7 +33,6 @@ const Graphs = requirePrometheus(({namespace, isOpenShift}) => {
<div className="group">
<div className="group__title">
<h2 className="h3">Control Plane Status</h2>
{!isOpenShift && <DashboardLink to="/grafana/dashboard/db/kubernetes-control-plane-status?orgId=1" />}
</div>
<div className="container-fluid group__body group__graphs">
<div className="row">
Expand All @@ -62,7 +56,6 @@ const Graphs = requirePrometheus(({namespace, isOpenShift}) => {
<div className="group">
<div className="group__title">
<h2 className="h3">Capacity Planning</h2>
{!isOpenShift && <DashboardLink to="/grafana/dashboard/db/kubernetes-capacity-planning?orgId=1" />}
</div>
<div className="container-fluid group__body group__graphs">
<div className="row">
Expand Down
10 changes: 6 additions & 4 deletions frontend/public/components/graphs/base.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -142,6 +143,7 @@ export class BaseGraph extends SafetyFirst {
}

BaseGraph.propTypes = {
namespace: PropTypes.string,
query: PropTypes.oneOfType([
PropTypes.string,
PropTypes.arrayOf(
Expand Down
36 changes: 7 additions & 29 deletions frontend/public/components/graphs/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 => <AsyncComponent loader={() => import('./graph-loader').then(c => c.Bar)} {...props} />;
Expand All @@ -14,39 +15,16 @@ export const Line = props => <AsyncComponent loader={() => import('./graph-loade
export const Scalar = props => <AsyncComponent loader={() => import('./graph-loader').then(c => c.Scalar)} {...props} />;
export const Donut = props => <AsyncComponent loader={() => 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<P>) => React.ComponentType<P & {isOpenShift: boolean}>} */
export const requirePrometheus = WrappedComponent => connectToFlags(FLAGS.OPENSHIFT, FLAGS.PROMETHEUS, FLAGS.CAN_LIST_NS)(props => {
/** @type {(WrappedComponent: React.SFC<P>) => React.ComponentType<P>} */
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 <WrappedComponent isOpenShift={openshiftFlag} {...props} />;
return <WrappedComponent {...props} />;
});
5 changes: 3 additions & 2 deletions frontend/public/components/namespace.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,15 +164,15 @@ class PullSecret extends SafetyFirst {

export const NamespaceLineCharts = ({ns}) => <div className="row">
<div className="col-sm-6 col-xs-12">
<Line title="CPU Usage" query={[
<Line title="CPU Usage" namespace={ns.metadata.name} query={[
{
name: 'Used',
query: `namespace:container_cpu_usage:sum{namespace='${ns.metadata.name}'}`,
},
]} />
</div>
<div className="col-sm-6 col-xs-12">
<Line title="Memory Usage" query={[
<Line title="Memory Usage" namespace={ns.metadata.name} query={[
{
name: 'Used',
query: `namespace:container_memory_usage_bytes:sum{namespace='${ns.metadata.name}'}`,
Expand All @@ -184,6 +184,7 @@ export const NamespaceLineCharts = ({ns}) => <div className="row">
export const TopPodsBarChart = ({ns}) => (
<Bar
title="Memory Usage by Pod (Top 10)"
namespace={ns.metadata.name}
query={`sort(topk(10, sum by (pod_name)(container_memory_usage_bytes{pod_name!="", namespace="${ns.metadata.name}"})))`}
humanize={humanizeMem}
metric="pod_name" />
Expand Down
7 changes: 3 additions & 4 deletions frontend/public/components/overview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -504,8 +504,7 @@ class OverviewMainContent_ extends React.Component<OverviewMainContentProps, Ove
}

fetchMetrics = (): void => {
if (!prometheusBasePath) {
// Component is not mounted or proxy has not been set up.
if (!prometheusTenancyBasePath) {
return;
}

Expand All @@ -516,7 +515,7 @@ class OverviewMainContent_ extends React.Component<OverviewMainContentProps, Ove
};

const promises = _.map(queries, (query, name) => {
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]);
Expand Down
6 changes: 3 additions & 3 deletions frontend/public/components/pod.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,13 @@ export const PodContainerTable = ({heading, containers, pod}) => <React.Fragment
const PodGraphs = requirePrometheus(({pod}) => <React.Fragment>
<div className="row">
<div className="col-md-4">
<Line title="Memory Usage" query={`pod_name:container_memory_usage_bytes:sum{pod_name='${pod.metadata.name}',namespace='${pod.metadata.namespace}'}`} />
<Line title="Memory Usage" namespace={pod.metadata.namespace} query={`pod_name:container_memory_usage_bytes:sum{pod_name='${pod.metadata.name}',namespace='${pod.metadata.namespace}'}`} />
</div>
<div className="col-md-4">
<Line title="CPU Usage" query={`pod_name:container_cpu_usage:sum{pod_name='${pod.metadata.name}',namespace='${pod.metadata.namespace}'} * 1000`} />
<Line title="CPU Usage" namespace={pod.metadata.namespace} query={`pod_name:container_cpu_usage:sum{pod_name='${pod.metadata.name}',namespace='${pod.metadata.namespace}'} * 1000`} />
</div>
<div className="col-md-4">
<Line title="Filesystem (bytes)" query={`pod_name:container_fs_usage_bytes:sum{pod_name='${pod.metadata.name}',namespace='${pod.metadata.namespace}'}`} />
<Line title="Filesystem (bytes)" namespace={pod.metadata.namespace} query={`pod_name:container_fs_usage_bytes:sum{pod_name='${pod.metadata.name}',namespace='${pod.metadata.namespace}'}`} />
</div>
</div>

Expand Down
71 changes: 42 additions & 29 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,38 +23,40 @@ 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 (
plog = capnslog.NewPackageLogger("github.com/openshift/console", "server")
)

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 {
Expand All @@ -75,17 +77,18 @@ 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 {
return s.Auther == nil
}

func (s *Server) prometheusProxyEnabled() bool {
return s.PrometheusProxyConfig != nil
return s.PrometheusProxyConfig != nil && s.PrometheusTenancyProxyConfig != nil
}

func (s *Server) alertManagerProxyEnabled() bool {
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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() {
Expand Down

0 comments on commit 21d0a9d

Please sign in to comment.