Skip to content

Commit fe512a6

Browse files
authored
Merge pull request #342 from devtron-labs/feat/config-drift
feat: config drift
2 parents 4f98578 + 387d6e4 commit fe512a6

File tree

9 files changed

+68
-12
lines changed

9 files changed

+68
-12
lines changed

package-lock.json

Lines changed: 2 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@devtron-labs/devtron-fe-common-lib",
3-
"version": "0.5.2",
3+
"version": "0.5.2-beta-4",
44
"description": "Supporting common component library",
55
"type": "module",
66
"main": "dist/index.js",
@@ -85,7 +85,6 @@
8585
"react-ga4": "^1.4.1",
8686
"react-keybind": "^0.9.4",
8787
"react-mde": "^11.5.0",
88-
"react-router": "^5.3.0",
8988
"react-router-dom": "^5.3.0",
9089
"react-select": "5.8.0",
9190
"rxjs": "^7.8.1",

src/Common/Constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export const URLS = {
6565
GLOBAL_CONFIG_SCOPED_VARIABLES: '/global-config/scoped-variables',
6666
GLOBAL_CONFIG_DEPLOYMENT_CHARTS_LIST: '/global-config/deployment-charts',
6767
NETWORK_STATUS_INTERFACE: '/network-status-interface',
68+
CONFIG_DRIFT: 'config-drift',
6869
}
6970

7071
export const ROUTES = {

src/Shared/Components/CICDHistory/AppStatusDetailsChart.tsx

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,34 @@
1616

1717
import { useMemo, useState } from 'react'
1818
import Tippy from '@tippyjs/react'
19+
import { useHistory } from 'react-router-dom'
20+
import { URLS } from '@Common/Constants'
1921
import { ReactComponent as InfoIcon } from '../../../Assets/Icon/ic-info-filled.svg'
2022
import { ReactComponent as Chat } from '../../../Assets/Icon/ic-chat-circle-dots.svg'
21-
import { AppStatusDetailsChartType, AggregatedNodes, STATUS_SORTING_ORDER } from './types'
23+
import { AppStatusDetailsChartType, AggregatedNodes, STATUS_SORTING_ORDER, NodeFilters } from './types'
2224
import { StatusFilterButtonComponent } from './StatusFilterButtonComponent'
23-
import { DEPLOYMENT_STATUS, APP_STATUS_HEADERS } from '../../constants'
25+
import { DEPLOYMENT_STATUS, APP_STATUS_HEADERS, ComponentSizeType } from '../../constants'
2426
import { IndexStore } from '../../Store'
2527
import { aggregateNodes } from '../../Helpers'
28+
import { Button, ButtonStyleType, ButtonVariantType } from '../Button'
2629

27-
const AppStatusDetailsChart = ({ filterRemoveHealth = false, showFooter }: AppStatusDetailsChartType) => {
30+
const AppStatusDetailsChart = ({
31+
filterRemoveHealth = false,
32+
showFooter,
33+
showConfigDriftInfo = false,
34+
onClose,
35+
}: AppStatusDetailsChartType) => {
36+
const history = useHistory()
2837
const _appDetails = IndexStore.getAppDetails()
2938
const [currentFilter, setCurrentFilter] = useState('')
3039

40+
const { appId, environmentId: envId } = _appDetails
41+
42+
const handleCompareDesiredManifest = () => {
43+
onClose()
44+
history.push(`${URLS.APP}/${appId}${URLS.DETAILS}/${envId}/${URLS.APP_DETAILS_K8}/${URLS.CONFIG_DRIFT}`)
45+
}
46+
3147
const nodes: AggregatedNodes = useMemo(
3248
() => aggregateNodes(_appDetails.resourceTree?.nodes || [], _appDetails.resourceTree?.podMetadata || []),
3349
[_appDetails],
@@ -100,6 +116,7 @@ const AppStatusDetailsChart = ({ filterRemoveHealth = false, showFooter }: AppSt
100116
.filter(
101117
(nodeDetails) =>
102118
currentFilter === 'all' ||
119+
(currentFilter === NodeFilters.drifted && nodeDetails.hasDrift) ||
103120
nodeDetails.health.status?.toLowerCase() === currentFilter,
104121
)
105122
.map((nodeDetails) => (
@@ -123,7 +140,24 @@ const AppStatusDetailsChart = ({ filterRemoveHealth = false, showFooter }: AppSt
123140
>
124141
{nodeDetails.status ? nodeDetails.status : nodeDetails.health.status}
125142
</div>
126-
<div>{getNodeMessage(nodeDetails.kind, nodeDetails.name)}</div>
143+
<div className="flexbox-col dc__gap-4">
144+
{showConfigDriftInfo && nodeDetails.hasDrift && (
145+
<div className="flexbox dc__gap-8 dc__align-items-center">
146+
<span className="fs-13 fw-4 lh-20 cy-7">Config drift detected</span>
147+
{onClose && appId && envId && (
148+
<Button
149+
dataTestId="show-config-drift"
150+
text="Compare with desired"
151+
variant={ButtonVariantType.text}
152+
style={ButtonStyleType.default}
153+
onClick={handleCompareDesiredManifest}
154+
size={ComponentSizeType.small}
155+
/>
156+
)}
157+
</div>
158+
)}
159+
<div>{getNodeMessage(nodeDetails.kind, nodeDetails.name)}</div>
160+
</div>
127161
</div>
128162
))
129163
) : (

src/Shared/Components/CICDHistory/StatusFilterButtonComponent.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@
1818
import { useEffect, useState } from 'react'
1919
import { ReactComponent as ICCaretDown } from '@Icons/ic-caret-down.svg'
2020
import { PopupMenu, StyledRadioGroup as RadioGroup } from '../../../Common'
21-
import { NodeStatus, StatusFilterButtonType } from './types'
21+
import { NodeFilters, NodeStatus, StatusFilterButtonType } from './types'
2222
import { IndexStore } from '../../Store'
23-
2423
import './StatusFilterButtonComponent.scss'
2524

2625
export const StatusFilterButtonComponent = ({ nodes, handleFilterClick }: StatusFilterButtonType) => {
@@ -32,10 +31,15 @@ export const StatusFilterButtonComponent = ({ nodes, handleFilterClick }: Status
3231
let progressingNodeCount: number = 0
3332
let failedNodeCount: number = 0
3433
let missingNodeCount: number = 0
34+
let driftedNodeCount: number = 0
3535

3636
nodes?.forEach((_node) => {
3737
const _nodeHealth = _node.health?.status
3838

39+
if (_node.hasDrift) {
40+
driftedNodeCount += 1
41+
}
42+
3943
if (_nodeHealth?.toLowerCase() === NodeStatus.Healthy) {
4044
healthyNodeCount += 1
4145
} else if (_nodeHealth?.toLowerCase() === NodeStatus.Degraded) {
@@ -58,6 +62,11 @@ export const StatusFilterButtonComponent = ({ nodes, handleFilterClick }: Status
5862
isSelected: NodeStatus.Progressing == selectedTab,
5963
},
6064
{ status: NodeStatus.Healthy, count: healthyNodeCount, isSelected: NodeStatus.Healthy == selectedTab },
65+
window._env_.FEATURE_CONFIG_DRIFT_ENABLE && {
66+
status: NodeFilters.drifted,
67+
count: driftedNodeCount,
68+
isSelected: selectedTab === NodeFilters.drifted,
69+
},
6170
]
6271
const validFilterOptions = filterOptions.filter(({ count }) => count > 0)
6372
const displayedInlineFilters = validFilterOptions.slice(
@@ -72,7 +81,8 @@ export const StatusFilterButtonComponent = ({ nodes, handleFilterClick }: Status
7281
(selectedTab === NodeStatus.Healthy && healthyNodeCount === 0) ||
7382
(selectedTab === NodeStatus.Degraded && failedNodeCount === 0) ||
7483
(selectedTab === NodeStatus.Progressing && progressingNodeCount === 0) ||
75-
(selectedTab === NodeStatus.Missing && missingNodeCount === 0)
84+
(selectedTab === NodeStatus.Missing && missingNodeCount === 0) ||
85+
(selectedTab === NodeFilters.drifted && driftedNodeCount === 0)
7686
) {
7787
setSelectedTab('all')
7888
} else if (handleFilterClick) {

src/Shared/Components/CICDHistory/types.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,8 @@ export interface DeploymentHistorySidebarType {
519519
export interface AppStatusDetailsChartType {
520520
filterRemoveHealth?: boolean
521521
showFooter: boolean
522+
showConfigDriftInfo?: boolean
523+
onClose?: () => void
522524
}
523525

524526
export interface StatusFilterButtonType {
@@ -535,6 +537,10 @@ export enum NodeStatus {
535537
Unknown = 'unknown',
536538
}
537539

540+
export enum NodeFilters {
541+
drifted = 'drifted',
542+
}
543+
538544
type NodesMap = {
539545
[key in NodeType]?: Map<string, any>
540546
}

src/Shared/Store/IndexStore.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
/* eslint-disable eqeqeq */
1818
/* eslint-disable array-callback-return */
1919
import { BehaviorSubject } from 'rxjs'
20+
import { NodeFilters } from '@Shared/Components'
2021
import { AppDetails, AppType, EnvDetails, EnvType, Node, Nodes, PodMetaData, iNode } from '../types'
2122

2223
const _appDetailsSubject: BehaviorSubject<AppDetails> = new BehaviorSubject({} as AppDetails)
@@ -43,6 +44,10 @@ const publishFilteredNodes = () => {
4344
return true
4445
}
4546

47+
if (_nodeFilter.filterType.toLowerCase() === NodeFilters.drifted && _node.hasDrift) {
48+
return true
49+
}
50+
4651
return false
4752
})
4853

src/Shared/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ export interface Node {
140140
port: number
141141
canBeHibernated: boolean
142142
isHibernated: boolean
143+
hasDrift?: boolean
143144
}
144145

145146
// eslint-disable-next-line no-use-before-define

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export interface customEnv {
7676
SYSTEM_CONTROLLER_LISTING_TIMEOUT?: number
7777
FEATURE_STEP_WISE_LOGS_ENABLE?: boolean
7878
FEATURE_IMAGE_PROMOTION_ENABLE?: boolean
79+
FEATURE_CONFIG_DRIFT_ENABLE: boolean
7980
}
8081
declare global {
8182
interface Window {

0 commit comments

Comments
 (0)