Skip to content

Commit

Permalink
Merge pull request #1100 from spadgett/cluster-version
Browse files Browse the repository at this point in the history
Bug 1666231 - Update Cluster Settings page for ClusterVersion API changes
  • Loading branch information
openshift-merge-robot committed Feb 5, 2019
2 parents dfed75a + bfad801 commit 6165533
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 61 deletions.
147 changes: 91 additions & 56 deletions frontend/public/components/cluster-settings/cluster-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,20 @@ import { Helmet } from 'react-helmet';
import { Button } from 'patternfly-react';
import { Link } from 'react-router-dom';

import { Firehose, HorizontalNav, ResourceLink, resourcePathFromModel } from '../utils';
import { K8sResourceKind, referenceForModel } from '../../module/k8s';
import { ClusterVersionKind, K8sResourceKind, referenceForModel } from '../../module/k8s';
import { ClusterAutoscalerModel, ClusterVersionModel } from '../../models';
import { ClusterOperatorPage } from './cluster-operator';
import { clusterUpdateModal } from '../modals';
import { GlobalConfigPage } from './global-config';
import {
EmptyBox,
Firehose,
HorizontalNav,
ResourceLink,
resourcePathFromModel,
SectionHeading,
Timestamp,
} from '../utils';

enum ClusterUpdateStatus {
UpToDate = 'Up to Date',
Expand All @@ -26,20 +34,19 @@ enum ClusterUpdateStatus {
const clusterAutoscalerReference = referenceForModel(ClusterAutoscalerModel);
const clusterVersionReference = referenceForModel(ClusterVersionModel);

export const getAvailableClusterUpdates = (cv) => {
export const getAvailableClusterUpdates = (cv: ClusterVersionKind) => {
return _.get(cv, 'status.availableUpdates');
};

export const getCurrentClusterVersion = (cv) => {
export const getDesiredClusterVersion = (cv: ClusterVersionKind) => {
return _.get(cv, 'status.desired.version');
};


const launchUpdateModal = (cv) => {
const launchUpdateModal = (cv: ClusterVersionKind) => {
clusterUpdateModal({cv});
};

const getClusterUpdateStatus = (cv: K8sResourceKind): ClusterUpdateStatus => {
const getClusterUpdateStatus = (cv: ClusterVersionKind): ClusterUpdateStatus => {
const conditions = _.get(cv, 'status.conditions', []);
const isFailingCondition = _.find(conditions, { type: 'Failing', status: 'True' });
if (isFailingCondition) {
Expand Down Expand Up @@ -111,63 +118,91 @@ const UpdateStatus: React.SFC<UpdateStatusProps> = ({cv}) => {
</React.Fragment>;
};

const CurrentVersion: React.SFC<CurrentVersionProps> = ({cv}) => {
const currentVersion = getCurrentClusterVersion(cv);
return currentVersion || <React.Fragment><i className="pficon pficon-warning-triangle-o" aria-hidden="true" />&nbsp;Unknown</React.Fragment>;
const DesiredVersion: React.SFC<DesiredVersionProps> = ({cv}) => {
const version = getDesiredClusterVersion(cv);
return version
? <React.Fragment>{version}</React.Fragment>
: <React.Fragment><i className="pficon pficon-warning-triangle-o" aria-hidden="true" />&nbsp;Unknown</React.Fragment>;
};

const ClusterVersionDetailsTable: React.SFC<ClusterVersionDetailsTableProps> = ({obj: cv, autoscalers}) => {
const conditions = _.get(cv, 'status.conditions', []);
const { history, conditions } = cv.status;
const status = getClusterUpdateStatus(cv);
const retrievedUpdatesFailedCondition = _.find(conditions, { type: 'RetrievedUpdates', status: 'False' });
const isFailingCondition = _.find(conditions, { type: 'Failing', status: 'True' });

return <div className="co-m-pane__body">
<div className="co-m-pane__body-group">
{ status === ClusterUpdateStatus.Updating && <UpdateInProgressAlert /> }
{ status === ClusterUpdateStatus.UpdatesAvailable && <UpdatesAvailableAlert cv={cv} /> }
{ isFailingCondition && <FailedConditionAlert message="Update is failing." condition={isFailingCondition} /> }
{ retrievedUpdatesFailedCondition && <FailedConditionAlert message="Could not retrieve updates." condition={retrievedUpdatesFailedCondition} /> }
<div className="co-detail-table">
<div className="co-detail-table__row row">
<div className="co-detail-table__section">
<dl className="co-m-pane__details">
<dt className="co-detail-table__section-header">Channel</dt>
<dd>{cv.spec.channel}</dd>
</dl>
</div>
<div className="co-detail-table__section">
<dl className="co-m-pane__details">
<dt className="co-detail-table__section-header">Update Status</dt>
<dd><UpdateStatus cv={cv} /></dd>
</dl>
</div>
<div className="co-detail-table__section">
<dl className="co-m-pane__details">
<dt className="co-detail-table__section-header">Current Version</dt>
<dd><CurrentVersion cv={cv} /></dd>
</dl>
return <React.Fragment>
<div className="co-m-pane__body">
<div className="co-m-pane__body-group">
{ status === ClusterUpdateStatus.Updating && <UpdateInProgressAlert /> }
{ status === ClusterUpdateStatus.UpdatesAvailable && <UpdatesAvailableAlert cv={cv} /> }
{ isFailingCondition && <FailedConditionAlert message="Update is failing." condition={isFailingCondition} /> }
{ retrievedUpdatesFailedCondition && <FailedConditionAlert message="Could not retrieve updates." condition={retrievedUpdatesFailedCondition} /> }
<div className="co-detail-table">
<div className="co-detail-table__row row">
<div className="co-detail-table__section">
<dl className="co-m-pane__details">
<dt className="co-detail-table__section-header">Channel</dt>
<dd>{cv.spec.channel}</dd>
</dl>
</div>
<div className="co-detail-table__section">
<dl className="co-m-pane__details">
<dt className="co-detail-table__section-header">Update Status</dt>
<dd><UpdateStatus cv={cv} /></dd>
</dl>
</div>
<div className="co-detail-table__section">
<dl className="co-m-pane__details">
<dt className="co-detail-table__section-header">Desired Version</dt>
<dd><DesiredVersion cv={cv} /></dd>
</dl>
</div>
</div>
</div>
</div>
<div className="co-m-pane__body-group">
<dl className="co-m-pane__details">
<dt>Cluster ID</dt>
<dd className="co-break-all">{cv.spec.clusterID}</dd>
<dt>Desired Release Image</dt>
<dd className="co-break-all">{_.get(cv, 'status.desired.image') || '-'}</dd>
<dt>Cluster Autoscaler</dt>
<dd>
{_.isEmpty(autoscalers)
? <Link to={`${resourcePathFromModel(ClusterAutoscalerModel)}/new`}>
<i className="pficon pficon-add-circle-o" aria-hidden="true" /> Create Autoscaler
</Link>
: autoscalers.map(autoscaler => <div key={autoscaler.metadata.uid}><ResourceLink kind={clusterAutoscalerReference} name={autoscaler.metadata.name} /></div>)}
</dd>
</dl>
</div>
</div>
<div className="co-m-pane__body-group">
<dl className="co-m-pane__details">
<dt>Cluster ID</dt>
<dd className="co-break-all">{cv.spec.clusterID}</dd>
<dt>Current Payload</dt>
<dd className="co-break-all">{_.get(cv, 'status.desired.payload') || '-'}</dd>
<dt>Cluster Autoscaler</dt>
<dd>
{_.isEmpty(autoscalers)
? <Link to={`${resourcePathFromModel(ClusterAutoscalerModel)}/new`}>
<i className="pficon pficon-add-circle-o" aria-hidden="true" /> Create Autoscaler
</Link>
: autoscalers.map(autoscaler => <div key={autoscaler.metadata.uid}><ResourceLink kind={clusterAutoscalerReference} name={autoscaler.metadata.name} /></div>)}
</dd>
</dl>
<div className="co-m-pane__body">
<SectionHeading text="Update History" />
<div className="co-table-container">
<table className="table">
<thead>
<tr>
<th>Version</th>
<th>State</th>
<th>Started</th>
<th>Completed</th>
</tr>
</thead>
<tbody>
{_.isEmpty(history) && <EmptyBox label="History" />}
{history.map((update, i) => <tr key={i}>
<td className="co-break-all">{update.version || '-'}</td>
<td>{update.state || '-'}</td>
<td><Timestamp timestamp={update.startedTime} /></td>
<td>{update.completionTime ? <Timestamp timestamp={update.completionTime} /> : '-'}</td>
</tr>)}
</tbody>
</table>
</div>
</div>
</div>;
</React.Fragment>;
};

const ClusterOperatorTabPage: React.SFC = () => <ClusterOperatorPage autoFocus={false} showTitle={false} />;
Expand Down Expand Up @@ -207,15 +242,15 @@ export const ClusterSettingsPage: React.SFC<ClusterSettingsPageProps> = ({match}
};

type UpdateStatusProps = {
cv: K8sResourceKind;
cv: ClusterVersionKind;
};

type CurrentVersionProps = {
cv: K8sResourceKind;
type DesiredVersionProps = {
cv: ClusterVersionKind;
};

type ClusterVersionDetailsTableProps = {
obj: K8sResourceKind;
obj: ClusterVersionKind;
autoscalers: K8sResourceKind[];
};

Expand Down
8 changes: 4 additions & 4 deletions frontend/public/components/modals/cluster-update-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import * as React from 'react';

import { createModalLauncher, ModalTitle, ModalBody, ModalSubmitFooter, ModalComponentProps } from '../factory/modal';
import { Dropdown, PromiseComponent, ExternalLink } from '../utils';
import { k8sPatch, K8sResourceKind } from '../../module/k8s';
import { ClusterVersionKind, k8sPatch } from '../../module/k8s';
import { ClusterVersionModel } from '../../models';
import { getAvailableClusterUpdates, getCurrentClusterVersion } from '../cluster-settings/cluster-settings';
import { getAvailableClusterUpdates, getDesiredClusterVersion } from '../cluster-settings/cluster-settings';

class ClusterUpdateModal extends PromiseComponent {
readonly state: ClusterUpdateModalState;
Expand Down Expand Up @@ -38,7 +38,7 @@ class ClusterUpdateModal extends PromiseComponent {
const {cv} = this.props;
const {selectedVersion} = this.state;
const availableUpdates = getAvailableClusterUpdates(cv);
const currentVersion = getCurrentClusterVersion(cv);
const currentVersion = getDesiredClusterVersion(cv);
const dropdownItems = _.map(availableUpdates, 'version');
const dropdownTitle = _.get(availableUpdates[selectedVersion], 'version');
return <form onSubmit={this._submit} name="form" className="modal-content">
Expand Down Expand Up @@ -71,7 +71,7 @@ class ClusterUpdateModal extends PromiseComponent {
export const clusterUpdateModal = createModalLauncher(ClusterUpdateModal);

type ClusterUpdateModalProps = {
cv: K8sResourceKind;
cv: ClusterVersionKind;
} & ModalComponentProps;

type ClusterUpdateModalState = {
Expand Down
3 changes: 2 additions & 1 deletion frontend/public/features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
PrometheusModel,
SelfSubjectAccessReviewModel,
} from './models';
import { ClusterVersionKind } from './module/k8s';
import { k8sBasePath, referenceForModel } from './module/k8s/k8s';
import { k8sCreate } from './module/k8s/resource';
import { types } from './module/k8s/k8s-actions';
Expand Down Expand Up @@ -105,7 +106,7 @@ const detectOpenShift = dispatch => coFetchJSON(openshiftPath)
const clusterVersionPath = `${k8sBasePath}/apis/config.openshift.io/v1/clusterversions/version`;
const detectClusterVersion = dispatch => coFetchJSON(clusterVersionPath)
.then(
clusterVersion => {
(clusterVersion: ClusterVersionKind) => {
const hasClusterVersion = !_.isEmpty(clusterVersion);
setFlag(dispatch, FLAGS.CLUSTER_VERSION, hasClusterVersion);

Expand Down
28 changes: 28 additions & 0 deletions frontend/public/module/k8s/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,34 @@ export type MachineConfigPoolKind = {
status: MachineConfigPoolStatus;
} & K8sResourceKind;

type ClusterUpdate = {
image: string;
version: string;
};

type UpdateHistory = {
state: 'Completed' | 'Partial';
startedTime: string;
completionTime: string;
version: string;
image: string;
};

export type ClusterVersionKind = {
spec: {
channel: string;
clusterID: string;
desiredUpdate: ClusterUpdate;
upstream: string;
};
status: {
availableUpdates: ClusterUpdate[];
conditions: any[];
desired: ClusterUpdate;
history: UpdateHistory[];
};
} & K8sResourceKind;

export type K8sKind = {
abbr: string;
kind: string;
Expand Down

0 comments on commit 6165533

Please sign in to comment.