Skip to content

Commit

Permalink
feat(managed): allow pausing/resuming managed resources via indicator (
Browse files Browse the repository at this point in the history
  • Loading branch information
anotherchrisberry authored and mergify[bot] committed Dec 19, 2019
1 parent ed779e6 commit b1ced5a
Show file tree
Hide file tree
Showing 11 changed files with 229 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ const ManagedResourceConfig = ({ application }: IManagedResourceConfigProps) =>
setPauseFailed(false);
logClick('Pause Management', application.name);

ManagedWriter.pauseResourceManagement(application.name)
ManagedWriter.pauseApplicationManagement(application.name)
.then(() => {
setPaused(true);
application.managedResources.refresh();
application.managedResources.refresh(true);
})
.catch(() => setPauseFailed(true))
.finally(() => setPausePending(false));
Expand All @@ -83,10 +83,10 @@ const ManagedResourceConfig = ({ application }: IManagedResourceConfigProps) =>
setPauseFailed(false);
logClick('Resume Management', application.name);

ManagedWriter.resumeResourceManagement(application.name)
ManagedWriter.resumeApplicationManagement(application.name)
.then(() => {
setPaused(false);
application.managedResources.refresh();
application.managedResources.refresh(true);
})
.catch(() => setPauseFailed(true))
.finally(() => setPausePending(false));
Expand Down
6 changes: 5 additions & 1 deletion app/scripts/modules/core/src/cluster/ClusterPod.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ export class ClusterPod extends React.Component<IClusterPodProps, IClusterPodSta
<div>{subgroup.heading}</div>
<div className={classNames('flex-container-h middle', { 'sp-margin-xs-left': showManagedIndicator })}>
{showManagedIndicator && (
<ManagedResourceStatusIndicator shape="circle" resourceSummary={subgroup.managedResourceSummary} />
<ManagedResourceStatusIndicator
shape="circle"
resourceSummary={subgroup.managedResourceSummary}
application={application}
/>
)}
<EntityNotifications
entity={subgroup}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ export class DefaultClusterPodTitle extends React.Component<IClusterPodTitleProp
/>

{grouping.isManaged && (
<ManagedResourceStatusIndicator shape="square" resourceSummary={grouping.managedResourceSummary} />
<ManagedResourceStatusIndicator
shape="square"
resourceSummary={grouping.managedResourceSummary}
application={application}
/>
)}
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions app/scripts/modules/core/src/loadBalancer/LoadBalancer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export class LoadBalancer extends React.Component<ILoadBalancerProps> {
<ManagedResourceStatusIndicator
shape="circle"
resourceSummary={loadBalancer.managedResourceSummary}
application={application}
/>
)}
<EntityNotifications
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ export class LoadBalancerPod extends React.Component<ILoadBalancerPodProps> {
<div className="pod-center horizontal space-between flex-1 no-right-padding">
<div>{grouping.heading}</div>
{grouping.isManaged && (
<ManagedResourceStatusIndicator shape="square" resourceSummary={grouping.managedResourceSummary} />
<ManagedResourceStatusIndicator
shape="square"
resourceSummary={grouping.managedResourceSummary}
application={application}
/>
)}
</div>
</div>
Expand Down
164 changes: 102 additions & 62 deletions app/scripts/modules/core/src/managed/ManagedResourceStatusIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,25 @@ import ReactGA from 'react-ga';
import classNames from 'classnames';
import { UISref } from '@uirouter/react';

import { HoverablePopover } from 'core/presentation';
import { HoverablePopover, IHoverablePopoverContentsProps } from 'core/presentation';
import { IManagedResourceSummary, ManagedResourceStatus } from 'core/domain';
import { Application } from 'core/application';

import { toggleResourcePause } from './toggleResourceManagement';

import './ManagedResourceStatusIndicator.less';

const viewConfigurationByStatus = {
interface IViewConfiguration {
iconClass: string;
colorClass: string;
popoverContents: (resourceSummary: IManagedResourceSummary, application?: Application) => JSX.Element;
}

const viewConfigurationByStatus: { [status in ManagedResourceStatus]: IViewConfiguration } = {
ACTUATING: {
iconClass: 'icon-md-actuating',
colorClass: 'info',
popoverContents: (id: string) => (
popoverContents: (resourceSummary: IManagedResourceSummary) => (
<>
<p>
<b>Action is being taken to resolve a drift from the declarative configuration.</b>
Expand All @@ -22,149 +31,139 @@ const viewConfigurationByStatus = {
<UISref to="home.applications.application.tasks">
<a>tasks view</a>
</UISref>{' '}
to see work that's in progress.
to see work that's in progress. <LearnMoreLink resourceSummary={resourceSummary} />
</p>
<LearnMoreLink id={id} status={ManagedResourceStatus.ACTUATING} />
</>
),
},
CREATED: {
iconClass: 'icon-md-created',
colorClass: 'info',
popoverContents: (id: string) => (
popoverContents: (resourceSummary: IManagedResourceSummary) => (
<>
<p>
<b>Spinnaker has started continuously managing this resource.</b>
</p>
<p>
If its actual configuration drifts from the declarative configuration, Spinnaker will automatically correct
it. Changes made in the UI will be stomped in favor of the declarative configuration.
it. Changes made in the UI will be stomped in favor of the declarative configuration.{' '}
<LearnMoreLink resourceSummary={resourceSummary} />
</p>
<LearnMoreLink id={id} status={ManagedResourceStatus.CREATED} />
</>
),
},
DIFF: {
iconClass: 'icon-md-diff',
colorClass: 'info',
popoverContents: (id: string) => (
popoverContents: (resourceSummary: IManagedResourceSummary) => (
<>
<p>
<b>A drift from the declarative configuration was detected.</b>
</p>
<p>Spinnaker will automatically take action to bring this resource back to its desired state.</p>
<LearnMoreLink id={id} status={ManagedResourceStatus.DIFF} />
<p>
Spinnaker will automatically take action to bring this resource back to its desired state.{' '}
<LearnMoreLink resourceSummary={resourceSummary} />
</p>
</>
),
},
ERROR: {
iconClass: 'icon-md-error',
colorClass: 'error',
popoverContents: (id: string) => (
popoverContents: (resourceSummary: IManagedResourceSummary) => (
<>
<p>
<b>Something went wrong.</b>
</p>
<p>
Spinnaker is configured to continuously manage this resource, but something went wrong trying to check its
current state. Automatic action can't be taken right now, and manual intervention might be required.
current state. Automatic action can't be taken right now, and manual intervention might be required.{' '}
<LearnMoreLink resourceSummary={resourceSummary} />
</p>
<LearnMoreLink id={id} status={ManagedResourceStatus.ERROR} />
</>
),
},
HAPPY: {
iconClass: 'icon-md',
colorClass: 'info',
popoverContents: (id: string) => (
popoverContents: (resourceSummary: IManagedResourceSummary) => (
<>
<p>
<b>Spinnaker is continuously managing this resource.</b>
</p>
<p>Changes made in the UI will be stomped in favor of the declarative configuration.</p>
<LearnMoreLink id={id} status={ManagedResourceStatus.HAPPY} />
<p>
Changes made in the UI will be stomped in favor of the declarative configuration.{' '}
<LearnMoreLink resourceSummary={resourceSummary} />
</p>
</>
),
},
PAUSED: {
iconClass: 'icon-md-paused',
colorClass: 'warning',
popoverContents: (id: string) => (
popoverContents: (resourceSummary: IManagedResourceSummary, application: Application) => (
<>
<p>
<b>Continuous management is paused.</b>
</p>
<p>
Spinnaker is configured to continuously manage this resource, but management is temporarily paused. You can
resume management on the{' '}
<UISref to="home.applications.application.config" params={{ section: 'managed-resources' }}>
<a>config view</a>
</UISref>
.
</p>
<LearnMoreLink id={id} status={ManagedResourceStatus.PAUSED} />
{application.isManagementPaused && (
<p>
Spinnaker is configured to continuously manage this resource, but management for the entire application is
temporarily paused. <LearnMoreLink resourceSummary={resourceSummary} />
</p>
)}
{!application.isManagementPaused && (
<p>
Spinnaker is configured to continuously manage this resource, but management has been temporarily paused.{' '}
<LearnMoreLink resourceSummary={resourceSummary} />
</p>
)}
</>
),
},
RESUMED: {
iconClass: 'icon-md-resumed',
colorClass: 'info',
popoverContents: (id: string) => (
popoverContents: (resourceSummary: IManagedResourceSummary) => (
<>
<p>
<b>Continuous management was just resumed.</b>
</p>
<p>
Management was resumed after being temporarily paused. If Spinnaker detects that a drift from the declarative
configuration occurred while paused, it will take automatic action to resolve the drift.
configuration occurred while paused, it will take automatic action to resolve the drift.{' '}
<LearnMoreLink resourceSummary={resourceSummary} />
</p>
<p>
You can pause and resume management on the{' '}
<UISref to="home.applications.application.config" params={{ section: 'managed-resources' }}>
<a>config view</a>
</UISref>
.
</p>
<LearnMoreLink id={id} status={ManagedResourceStatus.RESUMED} />
</>
),
},
UNHAPPY: {
iconClass: 'icon-md-flapping',
colorClass: 'error',
popoverContents: (id: string) => (
popoverContents: (resourceSummary: IManagedResourceSummary) => (
<>
<p>
<b>A drift from the declarative configuration was detected, but Spinnaker hasn't been able to correct it.</b>
</p>
<p>
Spinnaker has been trying to correct a detected drift, but taking automatic action hasn't helped. Manual
intervention might be required.
</p>
<p>
You can temporarily pause management on the{' '}
<UISref to="home.applications.application.config" params={{ section: 'managed-resources' }}>
<a>config view</a>
</UISref>{' '}
to troubleshoot or make manual changes.
intervention might be required. <LearnMoreLink resourceSummary={resourceSummary} />
</p>
<LearnMoreLink id={id} status={ManagedResourceStatus.UNHAPPY} />
</>
),
},
UNKNOWN: {
iconClass: 'icon-md-unknown',
colorClass: 'warning',
popoverContents: (id: string) => (
popoverContents: (resourceSummary: IManagedResourceSummary) => (
<>
<p>
<b>Unable to determine resource status.</b>
</p>
<p>
Spinnaker is configured to continuously manage this resource, but its current status can't be calculated right
now.
now. <LearnMoreLink resourceSummary={resourceSummary} />
</p>
<LearnMoreLink id={id} status={ManagedResourceStatus.UNKNOWN} />
</>
),
},
Expand All @@ -177,30 +176,71 @@ const logClick = (label: string, resourceId: string, status: ManagedResourceStat
label: `${resourceId},${status}`,
});

const LearnMoreLink = ({ id, status }: { id: string; status: ManagedResourceStatus }) => (
<p className="sp-margin-m-top sp-margin-xs-bottom">
<a
target="_blank"
onClick={() => logClick('Status docs link', id, status)}
href={`https://www.spinnaker.io/reference/managed-delivery/resource-status/#${status.toLowerCase()}`}
>
Learn more about this
</a>
</p>
const LearnMoreLink = ({ resourceSummary }: { resourceSummary: IManagedResourceSummary }) => (
<a
target="_blank"
onClick={() => logClick('Status docs link', resourceSummary.id, resourceSummary.status)}
href={`https://www.spinnaker.io/reference/managed-delivery/resource-status/#${resourceSummary.status.toLowerCase()}`}
>
Learn more
</a>
);

const PopoverLinks = ({
resourceSummary,
application,
hidePopover,
}: {
resourceSummary: IManagedResourceSummary;
application: Application;
hidePopover: () => void;
}) => (
<div className="horizontal right">
{!resourceSummary.isPaused && (
<p className="sp-margin-m-top sp-margin-xs-bottom">
<button className="passive" onClick={() => toggleResourcePause(resourceSummary, application, hidePopover)}>
<i className="fa fa-pause" /> Pause management of this resource
</button>
</p>
)}
{resourceSummary.isPaused && !application.isManagementPaused && (
<p className="sp-margin-m-top sp-margin-xs-bottom">
<button className="passive" onClick={() => toggleResourcePause(resourceSummary, application, hidePopover)}>
<i className="fa fa-play" /> Resume management of this resource
</button>
</p>
)}
{application.isManagementPaused && (
<p>
<UISref to="home.applications.application.config" params={{ section: 'managed-resources' }}>
<a>Resume application management</a>
</UISref>
</p>
)}
</div>
);

export interface IManagedResourceStatusIndicatorProps {
shape: 'square' | 'circle';
resourceSummary: IManagedResourceSummary;
application: Application;
}

export const ManagedResourceStatusIndicator = ({
shape,
resourceSummary: { id, status },
resourceSummary,
application,
}: IManagedResourceStatusIndicatorProps) => {
const { status } = resourceSummary;
const PopoverContents = ({ hidePopover }: IHoverablePopoverContentsProps) => (
<>
{viewConfigurationByStatus[status].popoverContents(resourceSummary, application)}
<PopoverLinks resourceSummary={resourceSummary} application={application} hidePopover={hidePopover} />
</>
);
return (
<div className="flex-container-h stretch ManagedResourceStatusIndicator">
<HoverablePopover template={viewConfigurationByStatus[status].popoverContents(id)} placement="left">
<HoverablePopover Component={PopoverContents} placement="left">
<div className={classNames('flex-container-h middle', shape, viewConfigurationByStatus[status].colorClass)}>
<i className={classNames('fa', viewConfigurationByStatus[status].iconClass)} />
</div>
Expand Down
Loading

0 comments on commit b1ced5a

Please sign in to comment.