Skip to content

Commit

Permalink
feat: allow cluster resources to be modified and applied without leav…
Browse files Browse the repository at this point in the history
…ing cluster preview
  • Loading branch information
devcatalin committed Aug 31, 2021
1 parent 55e9aba commit ec5a752
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 16 deletions.
3 changes: 2 additions & 1 deletion src/components/molecules/Monaco/Monaco.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const Monaco = (props: {editorHeight: string}) => {
const selectedPath = useAppSelector(state => state.main.selectedPath);
const selectedResourceId = useAppSelector(state => state.main.selectedResourceId);
const resourceMap = useAppSelector(state => state.main.resourceMap);
const previewType = useAppSelector(state => state.main.previewType);
const [code, setCode] = useState('');
const [orgCode, setOrgCode] = useState<string>('');
const [containerRef, {width}] = useMeasure<HTMLDivElement>();
Expand Down Expand Up @@ -252,7 +253,7 @@ const Monaco = (props: {editorHeight: string}) => {

const options = {
selectOnLineNumbers: true,
readOnly: isInPreviewMode || (!selectedPath && !selectedResourceId),
readOnly: (isInPreviewMode && previewType !== 'cluster') || (!selectedPath && !selectedResourceId),
fontWeight: 'bold',
glyphMargin: true,
minimap: {
Expand Down
6 changes: 4 additions & 2 deletions src/components/organisms/ActionsPane/ActionsPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const ActionsPane = (props: {contentHeight: string}) => {
const uiState = useAppSelector(state => state.ui);
const currentSelectionHistoryIndex = useAppSelector(state => state.main.currentSelectionHistoryIndex);
const selectionHistory = useAppSelector(state => state.main.selectionHistory);
const previewType = useAppSelector(state => state.main.previewType);
const [key, setKey] = useState('source');
const dispatch = useAppDispatch();

Expand Down Expand Up @@ -124,11 +125,12 @@ const ActionsPane = (props: {contentHeight: string}) => {

const applySelection = useCallback(() => {
if (selectedResource) {
applyResourceWithConfirm(selectedResource, resourceMap, fileMap, dispatch, kubeconfig);
const isClusterPreview = previewType === 'cluster';
applyResourceWithConfirm(selectedResource, resourceMap, fileMap, dispatch, kubeconfig, {isClusterPreview});
} else if (selectedPath) {
applyFileWithConfirm(selectedPath, fileMap, dispatch, kubeconfig);
}
}, [selectedResource, resourceMap, fileMap, kubeconfig, selectedPath, dispatch]);
}, [selectedResource, resourceMap, fileMap, kubeconfig, selectedPath, dispatch, previewType]);

const diffSelectedResource = useCallback(() => {
if (selectedResourceId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ export function applyResourceWithConfirm(
resourceMap: ResourceMapType,
fileMap: FileMapType,
dispatch: ThunkDispatch<any, any, any>,
kubeconfig: string
kubeconfig: string,
options?: {
isClusterPreview?: boolean;
shouldPerformDiff?: boolean;
}
) {
const title = isKustomizationResource(selectedResource)
? `Apply ${selectedResource.name} kustomization your cluster?`
Expand All @@ -22,7 +26,7 @@ export function applyResourceWithConfirm(
icon: <ExclamationCircleOutlined />,
onOk() {
return new Promise(resolve => {
applyResource(selectedResource.id, resourceMap, fileMap, dispatch, kubeconfig);
applyResource(selectedResource.id, resourceMap, fileMap, dispatch, kubeconfig, options);
resolve({});
});
},
Expand Down
6 changes: 5 additions & 1 deletion src/components/organisms/DiffModal/DiffModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const DiffModal = () => {
const diffResourceId = useAppSelector(state => state.main.diffResourceId);
const [diffResource, setDiffResource] = useState<K8sResource>();
const [resourceContent, setResourceContent] = useState<string>();
const previewType = useAppSelector(state => state.main.previewType);
const fileMap = useAppSelector(state => state.main.fileMap);
const kubeconfig = useAppSelector(state => state.config.kubeconfigPath);
const [isVisible, setVisible] = useState(false);
Expand Down Expand Up @@ -75,7 +76,10 @@ const DiffModal = () => {
if (diffResourceId) {
const resource = resourceMap[diffResourceId];
if (resource) {
applyResourceWithConfirm(resource, resourceMap, fileMap, dispatch, kubeconfig);
applyResourceWithConfirm(resource, resourceMap, fileMap, dispatch, kubeconfig, {
isClusterPreview: previewType === 'cluster',
shouldPerformDiff: true,
});
}
}
};
Expand Down
28 changes: 26 additions & 2 deletions src/redux/thunks/applyResource.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import {K8sResource} from '@models/k8sresource';
import {spawn} from 'child_process';
import log from 'loglevel';
import {stringify} from 'yaml';
import {FileMapType, ResourceMapType} from '@models/appstate';
import {setAlert} from '@redux/reducers/alert';
import {AlertEnum, AlertType} from '@models/alert';
import {AppDispatch} from '@redux/store';
import {getAbsoluteResourceFolder} from '@redux/services/fileEntry';
import {isKustomizationResource} from '@redux/services/kustomize';
import {getShellPath} from '@utils/shell';
import {setApplyingResource} from '@redux/reducers/main';
import {setApplyingResource, updateResource} from '@redux/reducers/main';
import {getResourceFromCluster} from '@redux/thunks/utils';
import {performResourceDiff} from './diffResource';

/**
* Invokes kubectl for the content of the specified resource
Expand Down Expand Up @@ -56,7 +59,11 @@ export async function applyResource(
resourceMap: ResourceMapType,
fileMap: FileMapType,
dispatch: AppDispatch,
kubeconfig: string
kubeconfig: string,
options?: {
isClusterPreview?: boolean;
shouldPerformDiff?: boolean;
}
) {
try {
const resource = resourceMap[resourceId];
Expand All @@ -79,6 +86,23 @@ export async function applyResource(
title: 'Apply completed',
message: data.toString(),
};
if (options?.isClusterPreview) {
getResourceFromCluster(resource, kubeconfig).then(resourceFromCluster => {
delete resourceFromCluster.body.metadata?.managedFields;
const updatedResourceText = stringify(resourceFromCluster.body, {sortMapEntries: true});
dispatch(
updateResource({
resourceId: resource.id,
content: updatedResourceText,
})
);
if (options?.shouldPerformDiff) {
dispatch(performResourceDiff(resource.id));
}
});
} else if (options?.shouldPerformDiff) {
dispatch(performResourceDiff(resource.id));
}
dispatch(setAlert(alert));
dispatch(setApplyingResource(false));
});
Expand Down
24 changes: 16 additions & 8 deletions src/redux/thunks/diffResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import {AppDispatch, RootState} from '@redux/store';
import * as k8s from '@kubernetes/client-node';
import {stringify} from 'yaml';
import log from 'loglevel';
import {createPreviewRejection} from '@redux/thunks/utils';

import {createPreviewRejection, getResourceFromCluster} from '@redux/thunks/utils';
import {getResourceKindHandler} from '@src/kindhandlers';

/**
* Thunk to diff a resource against the configured cluster
*/
Expand All @@ -24,9 +22,16 @@ export const performResourceDiff = createAsyncThunk<
const kubeconfig = thunkAPI.getState().config.kubeconfigPath;
try {
const resource = resourceMap[diffResourceId];
const resourceKindHandler = getResourceKindHandler(resource.kind);

if (resource && resource.text && resourceKindHandler) {
if (resource && resource.text) {
const resourceKindHandler = getResourceKindHandler(resource.kind);
if (!resourceKindHandler) {
return createPreviewRejection(
thunkAPI,
'Diff Resource',
`Could not find Kind Handler for resoruce ${resource.id}`
);
}
const kc = new k8s.KubeConfig();
kc.loadFromFile(kubeconfig);

Expand All @@ -50,9 +55,12 @@ export const performResourceDiff = createAsyncThunk<
return createPreviewRejection(thunkAPI, title, message);
};

return resourceKindHandler
.getResourceFromCluster(kc, resource.content.metadata.name, resource.namespace ? resource.namespace : 'default')
.then(handleResource, handleRejection);
try {
const resourceFromCluster = await getResourceFromCluster(resource, kubeconfig);
return handleResource(resourceFromCluster);
} catch (err) {
return handleRejection(err);
}
}
} catch (e) {
createPreviewRejection(thunkAPI, 'Diff Resource', `Failed to diff resources; ${e.message}`);
Expand Down
19 changes: 19 additions & 0 deletions src/redux/thunks/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import * as k8s from '@kubernetes/client-node';
import {PREVIEW_PREFIX, YAML_DOCUMENT_DELIMITER} from '@constants/constants';
import {ResourceMapType} from '@models/appstate';
import {extractK8sResources, processParsedResources} from '@redux/services/resource';
import {stringify} from 'yaml';
import {AlertEnum} from '@models/alert';
import {K8sResource} from '@models/k8sresource';
import {getResourceKindHandler} from '@src/kindhandlers';

/**
* Utility to convert list of objects returned by k8s api to a single YAML document
Expand Down Expand Up @@ -53,3 +56,19 @@ export function createPreviewRejection(thunkAPI: any, title: string, message: st
},
});
}

export async function getResourceFromCluster(resource: K8sResource, kubeconfigPath: string) {
const resourceKindHandler = getResourceKindHandler(resource.kind);

if (resource && resource.text && resourceKindHandler) {
const kc = new k8s.KubeConfig();
kc.loadFromFile(kubeconfigPath);

const resourceFromCluster = await resourceKindHandler.getResourceFromCluster(
kc,
resource.content.metadata.name,
resource.namespace ? resource.namespace : 'default'
);
return resourceFromCluster;
}
}

0 comments on commit ec5a752

Please sign in to comment.