Skip to content

Commit c03e0fa

Browse files
Merge pull request #1568 from devtron-labs/feat-secret-decode
feat: checkbox that decodes the manifest for secrets.
2 parents 1a8b52d + 14f8f68 commit c03e0fa

File tree

5 files changed

+115
-18
lines changed

5 files changed

+115
-18
lines changed

src/components/v2/appDetails/k8Resource/nodeDetail/NodeDetail.component.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,20 +117,20 @@ function NodeDetailComponent({
117117
},
118118
)
119119
const _resourceContainers = []
120-
if (result?.manifest?.spec) {
121-
if (Array.isArray(result.manifest.spec.containers)) {
120+
if (result?.manifestResponse?.manifest?.spec) {
121+
if (Array.isArray(result.manifestResponse.manifest.spec.containers)) {
122122
_resourceContainers.push(
123-
...result.manifest.spec.containers.map((_container) => ({
123+
...result.manifestResponse.manifest.spec.containers.map((_container) => ({
124124
name: _container.name,
125125
isInitContainer: false,
126126
isEphemeralContainer: false,
127127
})),
128128
)
129129
}
130130

131-
if (Array.isArray(result.manifest.spec.initContainers)) {
131+
if (Array.isArray(result.manifestResponse.manifest.spec.initContainers)) {
132132
_resourceContainers.push(
133-
...result.manifest.spec.initContainers.map((_container) => ({
133+
...result.manifestResponse.manifest.spec.initContainers.map((_container) => ({
134134
name: _container.name,
135135
isInitContainer: true,
136136
isEphemeralContainer: false,

src/components/v2/appDetails/k8Resource/nodeDetail/NodeDetailTabs/Manifest.component.tsx

Lines changed: 86 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,27 @@ import MessageUI, { MsgUIType } from '../../../../common/message.ui'
1717
import { AppType, ManifestActionPropsType, NodeType } from '../../../appDetails.type'
1818
import YAML from 'yaml'
1919
import { toast } from 'react-toastify'
20-
import { DeploymentAppTypes, showError, ToastBody } from '@devtron-labs/devtron-fe-common-lib'
20+
import {
21+
Checkbox,
22+
CHECKBOX_VALUE,
23+
ConditionalWrap,
24+
DeploymentAppTypes,
25+
noop,
26+
showError,
27+
ToastBody,
28+
} from '@devtron-labs/devtron-fe-common-lib'
2129
import { appendRefetchDataToUrl } from '../../../../../util/URLUtil'
2230
import {
2331
EA_MANIFEST_SECRET_EDIT_MODE_INFO_TEXT,
2432
EA_MANIFEST_SECRET_INFO_TEXT,
2533
} from '../../../../../../config/constantMessaging'
2634
import { MODES } from '../../../../../../config'
27-
import { EMPTY_YAML_ERROR, SAVE_DATA_VALIDATION_ERROR_MSG } from '../../../../values/chartValuesDiff/ChartValuesView.constants'
28-
import { getTrimmedManifestData } from '../nodeDetail.util'
35+
import {
36+
EMPTY_YAML_ERROR,
37+
SAVE_DATA_VALIDATION_ERROR_MSG,
38+
} from '../../../../values/chartValuesDiff/ChartValuesView.constants'
39+
import { getDecodedEncodedSecretManifestData, getTrimmedManifestData } from '../nodeDetail.util'
40+
import Tippy from '@tippyjs/react'
2941

3042
function ManifestComponent({
3143
selectedTab,
@@ -39,7 +51,14 @@ function ManifestComponent({
3951
const history = useHistory()
4052
const [{ tabs, activeTab }, dispatch] = useTab(ManifestTabJSON)
4153
const { url } = useRouteMatch()
42-
const params = useParams<{ actionName: string; podName: string; nodeType: string; node: string; group: string, namespace: string }>()
54+
const params = useParams<{
55+
actionName: string
56+
podName: string
57+
nodeType: string
58+
node: string
59+
group: string
60+
namespace: string
61+
}>()
4362
const [manifest, setManifest] = useState('')
4463
const [modifiedManifest, setModifiedManifest] = useState('')
4564
const [activeManifestEditorData, setActiveManifestEditorData] = useState('')
@@ -54,6 +73,8 @@ function ManifestComponent({
5473
const [showDesiredAndCompareManifest, setShowDesiredAndCompareManifest] = useState(false)
5574
const [isResourceMissing, setIsResourceMissing] = useState(false)
5675
const [showInfoText, setShowInfoText] = useState(false)
76+
const [showDecodedData, setShowDecodedData] = useState(false)
77+
const [secretViewAccess, setSecretViewAccess] = useState(false)
5778

5879
useEffect(() => {
5980
selectedTab(NodeDetailTab.MANIFEST, url)
@@ -84,8 +105,7 @@ function ManifestComponent({
84105
if (
85106
isResourceBrowserView ||
86107
appDetails.appType === AppType.EXTERNAL_HELM_CHART ||
87-
(appDetails.deploymentAppType === DeploymentAppTypes.GITOPS &&
88-
appDetails.deploymentAppDeleteRequest)
108+
(appDetails.deploymentAppType === DeploymentAppTypes.GITOPS && appDetails.deploymentAppDeleteRequest)
89109
) {
90110
markActiveTab('Live manifest')
91111
}
@@ -104,8 +124,8 @@ function ManifestComponent({
104124
])
105125
.then((response) => {
106126
let _manifest: string
107-
108-
_manifest = JSON.stringify(response[0]?.result?.manifest)
127+
setSecretViewAccess(response[0]?.result?.secretViewAccess || false)
128+
_manifest = JSON.stringify(response[0]?.result?.manifestResponse?.manifest || '')
109129
setDesiredManifest(response[1]?.result?.manifest || '')
110130

111131
if (_manifest) {
@@ -177,13 +197,14 @@ function ManifestComponent({
177197
const handleApplyChanges = () => {
178198
setLoading(true)
179199
setLoadingMsg('Applying changes')
200+
setShowDecodedData(false)
180201

181202
let manifestString
182203
try {
183204
if (!modifiedManifest) {
184205
setErrorText(`${SAVE_DATA_VALIDATION_ERROR_MSG} "${EMPTY_YAML_ERROR}"`)
185206
// Handled for blocking API call
186-
manifestString = ""
207+
manifestString = ''
187208
} else {
188209
manifestString = JSON.stringify(YAML.parse(modifiedManifest))
189210
}
@@ -224,7 +245,7 @@ function ManifestComponent({
224245
className: 'devtron-toast unauthorized',
225246
},
226247
)
227-
} else if (err.code === 400 || err.code === 409 || err.code === 422 ) {
248+
} else if (err.code === 400 || err.code === 409 || err.code === 422) {
228249
const error = err['errors'] && err['errors'][0]
229250
if (error && error.code && error.userMessage) {
230251
setErrorText(`ERROR ${err.code} > Message: “${error.userMessage}”`)
@@ -264,6 +285,7 @@ function ManifestComponent({
264285
setModifiedManifest(manifest)
265286
setActiveManifestEditorData('')
266287
setErrorText('')
288+
setShowDecodedData(false)
267289
}
268290

269291
const markActiveTab = (_tabName: string) => {
@@ -299,6 +321,56 @@ function ManifestComponent({
299321
updateEditor(_tab.name)
300322
}
301323

324+
const onChangeToggleShowDecodedValue = (jsonManifestData) => {
325+
if (!jsonManifestData?.data) return
326+
setShowDecodedData(!showDecodedData)
327+
if (!showDecodedData) {
328+
setTrimedManifestEditorData(
329+
getDecodedEncodedSecretManifestData(jsonManifestData, true, showDecodedData) as string,
330+
)
331+
} else {
332+
setTrimedManifestEditorData(getDecodedEncodedSecretManifestData(jsonManifestData, true, true) as string)
333+
}
334+
}
335+
336+
const renderShowDecodedValueCheckbox = () => {
337+
const jsonManifestData = YAML.parse(trimedManifestEditorData)
338+
if (jsonManifestData?.kind === 'Secret' && !isEditmode && secretViewAccess) {
339+
return (
340+
<ConditionalWrap
341+
condition={!jsonManifestData?.data}
342+
wrap={(children) => (
343+
<Tippy
344+
className="default-tt w-200"
345+
arrow={false}
346+
placement="top-start"
347+
content="Nothing to decode, data field not found."
348+
>
349+
{children}
350+
</Tippy>
351+
)}
352+
>
353+
<div
354+
className={`${
355+
!jsonManifestData?.data ? 'dc__opacity-0_5 cursor-not-allowed' : ''
356+
} flex left ml-8`}
357+
>
358+
<Checkbox
359+
rootClassName={`${
360+
!jsonManifestData?.data ? 'dc__opacity-0_5 cursor-not-allowed' : 'cursor'
361+
} mb-0-imp h-18`}
362+
id="showDecodedValue"
363+
isChecked={showDecodedData}
364+
onChange={() => onChangeToggleShowDecodedValue(jsonManifestData)}
365+
value={CHECKBOX_VALUE.CHECKED}
366+
/>
367+
Show decoded Value
368+
</div>
369+
</ConditionalWrap>
370+
)
371+
}
372+
}
373+
302374
return isDeleted ? (
303375
<div>
304376
<MessageUI
@@ -415,7 +487,10 @@ function ManifestComponent({
415487
? EA_MANIFEST_SECRET_EDIT_MODE_INFO_TEXT
416488
: EA_MANIFEST_SECRET_INFO_TEXT
417489
}
418-
/>
490+
className="flex left"
491+
>
492+
{renderShowDecodedValueCheckbox()}
493+
</CodeEditor.Information>
419494
)}
420495
{activeTab === 'Compare' && (
421496
<CodeEditor.Header hideDefaultSplitHeader={true}>

src/components/v2/appDetails/k8Resource/nodeDetail/nodeDetail.util.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { ManifestData, NodeDetailTab } from './nodeDetail.type'
1313
import { multiSelectStyles } from '../../../common/ReactSelectCustomization'
1414
import { sortOptionsByLabel } from '../../../../common'
1515
import { MANIFEST_KEY_FIELDS } from '../../../../../config'
16+
import { decode } from '../../../../../util/Util'
1617

1718
export const getNodeDetailTabs = (nodeType: NodeType, isResourceBrowserTab?: boolean) => {
1819
if (nodeType.toLowerCase() === NodeType.Pod.toLowerCase()) {
@@ -405,3 +406,15 @@ export const getTrimmedManifestData = (
405406

406407
return returnAsString ? JSON.stringify(manifestData) : manifestData
407408
}
409+
410+
export const getDecodedEncodedSecretManifestData = (
411+
manifestData: ManifestData,
412+
returnAsString: boolean = false,
413+
isEncoded?: boolean,
414+
): ManifestData | string => {
415+
const encodedData = {
416+
...manifestData,
417+
[MANIFEST_KEY_FIELDS.DATA]: decode(manifestData[MANIFEST_KEY_FIELDS.DATA], isEncoded),
418+
}
419+
return returnAsString ? JSON.stringify(encodedData) : manifestData
420+
}

src/config/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,7 @@ export const NO_COMMIT_SELECTED = 'No commit is selected'
766766
export enum MANIFEST_KEY_FIELDS {
767767
METADATA = 'metadata',
768768
MANAGED_FIELDS = 'managedFields',
769+
DATA = 'data',
769770
}
770771

771772
export enum KEY_VALUE {

src/util/Util.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,17 @@ export function cleanKubeManifest(manifestJsonString: string): string {
3030
}
3131
}
3232

33-
export const decode = (data) => {
33+
const getDecodedEncodedData = (data, isEncoded: boolean = false) => {
34+
if (isEncoded) {
35+
return btoa(data)
36+
} else {
37+
return atob(data)
38+
}
39+
}
40+
41+
export const decode = (data, isEncoded: boolean = false) => {
3442
return Object.keys(data)
35-
.map((m) => ({ key: m, value: data[m] ? atob(data[m]) : data[m] }))
43+
.map((m) => ({ key: m, value: data[m] ? getDecodedEncodedData(data[m], isEncoded) : data[m] }))
3644
.reduce((agg, curr) => {
3745
agg[curr.key] = curr.value
3846
return agg

0 commit comments

Comments
 (0)