Skip to content

Commit

Permalink
feat: added functionaly to apply files when they are selected
Browse files Browse the repository at this point in the history
  • Loading branch information
olensmar committed Aug 24, 2021
1 parent 598de88 commit 087904c
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 4 deletions.
39 changes: 35 additions & 4 deletions src/components/organisms/ActionsPane/ActionsPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ import {K8sResource} from '@models/k8sresource';
import {isKustomizationResource} from '@redux/services/kustomize';
import {FileMapType, ResourceMapType} from '@models/appstate';
import {ThunkDispatch} from 'redux-thunk';
import {ApplyTooltip, DiffTooltip} from '@constants/tooltips';
import {ApplyFileTooltip, ApplyTooltip, DiffTooltip} from '@constants/tooltips';
import {performResourceDiff} from '@redux/thunks/diffResource';
import {selectFromHistory} from '@redux/thunks/selectionHistory';
import {TOOLTIP_DELAY} from '@constants/constants';
import {applyFile} from '@redux/thunks/applyFile';

const {TabPane} = Tabs;

Expand Down Expand Up @@ -96,13 +97,35 @@ export function applyWithConfirm(
});
}

function applyFileWithConfirm(
selectedPath: string,
fileMap: FileMapType,
dispatch: ThunkDispatch<any, any, any>,
kubeconfig: string
) {
const title = `Apply ${fileMap[selectedPath].name} to your cluster?`;

Modal.confirm({
title,
icon: <ExclamationCircleOutlined />,
onOk() {
return new Promise(resolve => {
applyFile(selectedPath, fileMap, dispatch, kubeconfig);
resolve({});
});
},
onCancel() {},
});
}

const ActionsPane = (props: {contentHeight: string}) => {
const {contentHeight} = props;

const selectedResourceId = useAppSelector(state => state.main.selectedResourceId);
const applyingResource = useAppSelector(state => state.main.isApplyingResource);
const [selectedResource, setSelectedResource] = useState<K8sResource>();
const resourceMap = useAppSelector(state => state.main.resourceMap);
const selectedPath = useAppSelector(state => state.main.selectedPath);
const fileMap = useAppSelector(state => state.main.fileMap);
const kubeconfig = useAppSelector(state => state.config.kubeconfigPath);
const previewLoader = useAppSelector(state => state.main.previewLoader);
Expand Down Expand Up @@ -131,6 +154,8 @@ const ActionsPane = (props: {contentHeight: string}) => {
const applySelectedResource = useCallback(() => {
if (selectedResource) {
applyWithConfirm(selectedResource, resourceMap, fileMap, dispatch, kubeconfig);
} else if (selectedPath) {
applyFileWithConfirm(selectedPath, fileMap, dispatch, kubeconfig);
}
}, [selectedResource, resourceMap, fileMap, kubeconfig]);

Expand All @@ -141,8 +166,10 @@ const ActionsPane = (props: {contentHeight: string}) => {
}, [selectedResourceId]);

useEffect(() => {
if (selectedResourceId && resourceMap) {
if (selectedResourceId && resourceMap[selectedResourceId]) {
setSelectedResource(resourceMap[selectedResourceId]);
} else {
setSelectedResource(undefined);
}
}, [selectedResourceId, resourceMap]);

Expand Down Expand Up @@ -175,14 +202,18 @@ const ActionsPane = (props: {contentHeight: string}) => {
icon={<ArrowRightOutlined />}
/>

<Tooltip mouseEnterDelay={TOOLTIP_DELAY} title={ApplyTooltip} placement="bottomLeft">
<Tooltip
mouseEnterDelay={TOOLTIP_DELAY}
title={selectedPath ? ApplyFileTooltip : ApplyTooltip}
placement="bottomLeft"
>
<Button
loading={Boolean(applyingResource)}
type="primary"
size="small"
ghost
onClick={applySelectedResource}
disabled={!selectedResourceId}
disabled={!selectedResourceId && !selectedPath}
>
Apply
</Button>
Expand Down
1 change: 1 addition & 0 deletions src/constants/tooltips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const KustomizationPreviewTooltip = 'Preview the output of this Kustomize
export const ExitKustomizationPreviewTooltip = 'Exit Kustomize preview (Escape)';
export const HelmPreviewTooltip = 'Preview the Helm chart with this values file';
export const ExitHelmPreviewTooltip = 'Exit Helm chart preview (Escape)';
export const ApplyFileTooltip = 'Apply this file to your configured cluster';
export const ApplyTooltip = 'Apply this resource to your configured cluster';
export const DiffTooltip = 'Diff this resource against your configured cluster';
export const NamespacesFilterTooltip = 'Filter visible resources on selected namespace';
Expand Down
8 changes: 8 additions & 0 deletions src/redux/services/fileEntry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,14 @@ export function getAbsoluteResourcePath(resource: K8sResource, fileMap: FileMapT
return path.join(fileMap[ROOT_FILE_ENTRY].filePath, resource.filePath);
}

/**
* Returns the absolute path to the file that containing specified resource
*/

export function getAbsoluteFileEntryPath(fileEntry: FileEntry, fileMap: FileMapType) {
return path.join(fileMap[ROOT_FILE_ENTRY].filePath, fileEntry.filePath);
}

/**
* Extracts all resources from the file at the specified path
*/
Expand Down
78 changes: 78 additions & 0 deletions src/redux/thunks/applyFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {spawn} from 'child_process';
import log from 'loglevel';
import {FileMapType} from '@models/appstate';
import {setAlert} from '@redux/reducers/alert';
import {AlertEnum, AlertType} from '@models/alert';
import {AppDispatch} from '@redux/store';
import {getShellPath} from '@utils/shell';
import {setApplyingResource} from '@redux/reducers/main';
import fs from 'fs';
import {getAbsoluteFileEntryPath} from '@redux/services/fileEntry';

/**
* Invokes kubectl for the content of the specified resource
*/

function applyFileToCluster(filePath: string, kubeconfig: string) {
const child = spawn('kubectl', ['apply', '-f', '-'], {
env: {
NODE_ENV: process.env.NODE_ENV,
PUBLIC_URL: process.env.PUBLIC_URL,
PATH: getShellPath(),
KUBECONFIG: kubeconfig,
},
});
child.stdin.write(fs.readFileSync(filePath, 'utf8'));
child.stdin.end();
return child;
}

/**
* applies the specified file and creates corresponding alert
*
* this isn't actually a Thunk - but should be in the future!
*/

export async function applyFile(filePath: string, fileMap: FileMapType, dispatch: AppDispatch, kubeconfig: string) {
try {
const fileEntry = fileMap[filePath];
if (fileEntry && !fileEntry.children) {
dispatch(setApplyingResource(true));

try {
const child = applyFileToCluster(getAbsoluteFileEntryPath(fileEntry, fileMap), kubeconfig);

child.on('exit', (code, signal) => {
log.info(`kubectl exited with code ${code} and signal ${signal}`);
dispatch(setApplyingResource(false));
});

child.stdout.on('data', data => {
const alert: AlertType = {
type: AlertEnum.Success,
title: 'Apply completed',
message: data.toString(),
};
dispatch(setAlert(alert));
dispatch(setApplyingResource(false));
});

child.stderr.on('data', data => {
const alert: AlertType = {
type: AlertEnum.Error,
title: 'Apply failed',
message: data.toString(),
};
dispatch(setAlert(alert));
dispatch(setApplyingResource(false));
});
} catch (e) {
log.error(e.message);
dispatch(setApplyingResource(true));
}
}
} catch (e) {
log.error('Failed to apply file');
log.error(e);
}
}

0 comments on commit 087904c

Please sign in to comment.