diff --git a/docs-internal/monitoring.md b/docs-internal/monitoring.md new file mode 100644 index 00000000000..d8c9adf1d70 --- /dev/null +++ b/docs-internal/monitoring.md @@ -0,0 +1,29 @@ +# Monitoring & Graphs + +## Prometheus + +Data is collected by a Prometheus deployment. The Prometheus API is exposed through a service, which Console queries for data and transforms into a graph. + +### Recording rules + +Most of the queries used for the graphs are captured by recording rules in the [monitoring configmap](https://github.com/coreos-inc/tectonic/blob/master/installer/assets/monitoring/prometheus-configmap.yaml). These rules are used by nodes, namespaces, and pods, and get aggregated as such. + +## Sparkline Graphs + +### Components + +All sparkline (graphing) components can be found in [components/sparkline-widget](https://github.com/coreos-inc/bridge/tree/master/frontend/public/components/sparkline-widget). Each file includes documentation describing its use. + +The rendering logic is split between React and D3.js, though React is used everywhere possible. D3 is primarily used for parsing the data, generating SVG points, attaching mouse events, and calculating statistics. + +### Feature detection / service discovery + +In order to retrieve data from Prometheus, the UI must determine if the Prometheus service is available and, if it is, the URL for this service. This discovery is done by [k8s/discover-service](https://github.com/coreos-inc/bridge/blob/master/frontend/public/module/k8s/discover-service.js). The sparkline searches for any service named `prometheus` in the `tectonic-system` namespace and, if it is successful, attempts a basic health check. If the service is found and the health check is successful, discover-service provides the sparkline with the appropriate URL to query. If something fails, the sparkline is notified and renders an appropriate error. + +Simultaneous requests to discover-service are combined such that only one HTTP request goes out. Furthermore, discover-service caches the response of the service discovery request with a TTL of 2 minutes. + +## Local Testing + +Testing UI changes locally is best done by spinning up a cluster via the installer, pointing your local kubectl at it, and running the console locally. This way the configuration of Prometheus and data labeling are consistent with those of production clusters. + +Alternatively, you can launch a [Minikube](https://github.com/kubernetes/minikube) or [Vagrant](https://github.com/coreos/coreos-kubernetes) cluster and mimic a real Tectonic deployment by `kubectl create`-ing the manifests in [tectonic/installer/assets](https://github.com/coreos-inc/tectonic/tree/master/installer/assets). **NOTE**: depending on what kind of system you created your cluster with, node metrics might not be queried correctly. Minikube usually works pretty well, however the Vagrant setup from coreos-kubernetes didn't always work for all nodes. diff --git a/docs-internal/operators.md b/docs-internal/operators.md new file mode 100644 index 00000000000..f2fc670e1be --- /dev/null +++ b/docs-internal/operators.md @@ -0,0 +1,25 @@ +# Update UI + +The UI detects if operators are installed in the cluster and conditionally enables and renders the update UI feature in the `Cluster Settings` page. + +## Operator feature detection + +We determine if the feature is enabled by first checking if the operator feature exists. For more information, see [k8s/k8s.js](https://github.com/coreos-inc/bridge/blob/master/frontend/public/module/k8s/k8s.js). + +## UI Components + +The update UI is divided into several key components and is abstracted such that additional channels could be added in the future e.g. Tectonic channel, Container Linux channel, etc. + +All components live in [components/cluster-updates](https://github.com/coreos-inc/bridge/blob/master/frontend/public/components/cluster-updates). Each major component has a description in the file of how it's intended to be used. + +## Data flow + +The UI reads data for the `tectonicversions`, `channeloperatorconfigs`, and `appversions` via a websocket. For tectonicversions and appversions, it parses the status and spec versions to determine the current state of the component and then passes the results to `ChannelOperator` to be displayed. + +Data mutations, such as clicking the "check for update"/"start upgrade" button or changing a config field, are simple k8s patches to the appropriate config. When you click a button or submit a config form, the UI shows a pending/loading state until the corresponding websocket receives new data. When new data is received, the mutation is considered "applied" and the UI displays the data, however it does not validate if the intended value was actually applied. + +At no point does interaction with the UI mutate the incoming data. If a mutation is made, the UI waits for that data to come in through the firehose. + +## Local Testing + +Refer to "Local Testing" in the [monitoring doc](./monitoring.md#local-testing). diff --git a/frontend/public/components/cluster-updates/channel-operator.jsx b/frontend/public/components/cluster-updates/channel-operator.jsx index c40b1a20b53..34262fd057b 100644 --- a/frontend/public/components/cluster-updates/channel-operator.jsx +++ b/frontend/public/components/cluster-updates/channel-operator.jsx @@ -62,6 +62,20 @@ Detail.propTypes = { children: React.PropTypes.node }; +// ChannelOperator is intended to be a generic UI component that +// takes in pre-parsed data and displays it in a consistent manner, +// regardless of which channel (Tectonic or Container Linux) is +// being displayed. +// +// Ideally, channels operate exactly the same - they all share +// the same updated/updating/paused/etc states and basic +// config parameters. ChannelOperator aggregates those states, +// versions, & configs into the UI. +// +// However, ChannelOperator and it's sub-components ended up being +// built a little less generic than originally intended, so there +// is a bit of refactoring to be done when the Container Linux +// channel gets built. export class ChannelOperator extends React.Component { constructor(props) { super(props); diff --git a/frontend/public/components/cluster-updates/detail-config.jsx b/frontend/public/components/cluster-updates/detail-config.jsx index 4fdf14b1b40..64f3484d282 100644 --- a/frontend/public/components/cluster-updates/detail-config.jsx +++ b/frontend/public/components/cluster-updates/detail-config.jsx @@ -4,6 +4,10 @@ import classNames from 'classnames'; import {LoadingInline} from '../utils'; import {angulars} from '../react-wrapper'; +// Displays a field of a config & enables the user to click +// on the value to edit in a modal. +// +// Eg, choosing an update channel or toggling automatic updates export class DetailConfig extends React.Component { constructor(props) { super(props);