Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support flyte decks #504

Merged
merged 7 commits into from
Jun 13, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/zapp/console/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"@commitlint/cli": "^8.3.5",
"@commitlint/config-conventional": "^8.3.4",
"@date-io/moment": "1.3.9",
"@flyteorg/flyteidl": "1.1.0",
"@flyteorg/flyteidl": "1.1.4",
"@material-ui/core": "^4.0.0",
"@material-ui/icons": "^4.0.0",
"@material-ui/pickers": "^3.2.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,78 @@
import { Button } from '@material-ui/core';
import { Button, Dialog, IconButton } from '@material-ui/core';
import * as React from 'react';
import { ResourceIdentifier, Identifier, Variable } from 'models/Common/types';
import { makeStyles, Theme } from '@material-ui/core/styles';
import { getTask } from 'models/Task/api';
import { LaunchFormDialog } from 'components/Launch/LaunchForm/LaunchFormDialog';
import { NodeExecutionIdentifier } from 'models/Execution/types';
import { useNodeExecutionData } from 'components/hooks/useNodeExecution';
import { useNodeExecution, useNodeExecutionData } from 'components/hooks/useNodeExecution';
import { literalsToLiteralValueMap } from 'components/Launch/LaunchForm/utils';
import { TaskInitialLaunchParameters } from 'components/Launch/LaunchForm/types';
import { NodeExecutionPhase } from 'models/Execution/enums';
import Close from '@material-ui/icons/Close';
import { NodeExecutionDetails } from '../types';
import t from './strings';
import { ExecutionNodeDeck } from './ExecutionNodeDeck';

const useStyles = makeStyles((theme: Theme) => {
return {
actionsContainer: {
borderTop: `1px solid ${theme.palette.divider}`,
marginTop: theme.spacing(2),
paddingTop: theme.spacing(2),
'& button': {
marginRight: theme.spacing(1),
},
},
dialog: {
maxWidth: `calc(100% - ${theme.spacing(12)}px)`,
maxHeight: `calc(100% - ${theme.spacing(12)}px)`,
height: theme.spacing(90),
width: theme.spacing(110),
'& iframe': {
border: 'none',
},
},
dialogTitle: {
display: 'flex',
alignItems: 'center',
padding: theme.spacing(2),
paddingBottom: theme.spacing(0),
fontFamily: 'Open sans',
},
deckTitle: {
flexGrow: 1,
textAlign: 'center',
fontSize: '24px',
lineHeight: '32px',
marginBlock: 0,
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
},
close: {
position: 'absolute',
right: theme.spacing(2),
},
};
});

interface ExecutionDetailsActionsProps {
className?: string;
details: NodeExecutionDetails;
nodeExecutionId: NodeExecutionIdentifier;
phase?: NodeExecutionPhase;
}

export const ExecutionDetailsActions = (props: ExecutionDetailsActionsProps): JSX.Element => {
const { className, details, nodeExecutionId } = props;
const { details, nodeExecutionId, phase } = props;
const styles = useStyles();

const [showLaunchForm, setShowLaunchForm] = React.useState<boolean>(false);
const [taskInputsTypes, setTaskInputsTypes] = React.useState<
Record<string, Variable> | undefined
>();

const executionData = useNodeExecutionData(nodeExecutionId);
const execution = useNodeExecution(nodeExecutionId);

const id = details.taskTemplate?.id as ResourceIdentifier | undefined;

Expand All @@ -36,6 +84,9 @@ export const ExecutionDetailsActions = (props: ExecutionDetailsActionsProps): JS
if (id) fetchTask();
}, [id]);

const [showDeck, setShowDeck] = React.useState(false);
const onCloseDeck = () => setShowDeck(false);

if (!id) {
return <></>;
}
Expand All @@ -54,7 +105,17 @@ export const ExecutionDetailsActions = (props: ExecutionDetailsActionsProps): JS

return (
<>
<div className={className}>
<div className={styles.actionsContainer}>
{execution?.value?.closure?.deckUri && (
<Button
variant="outlined"
color="primary"
onClick={() => setShowDeck(true)}
disabled={phase !== NodeExecutionPhase.SUCCEEDED}
>
{t('flyteDeck')}
</Button>
)}
<Button variant="outlined" color="primary" onClick={rerunOnClick}>
{t('rerun')}
</Button>
Expand All @@ -65,6 +126,17 @@ export const ExecutionDetailsActions = (props: ExecutionDetailsActionsProps): JS
showLaunchForm={showLaunchForm}
setShowLaunchForm={setShowLaunchForm}
/>
{execution?.value?.closure?.deckUri ? (
<Dialog PaperProps={{ className: styles.dialog }} maxWidth={false} open={showDeck}>
<div className={styles.dialogTitle}>
<h2 className={styles.deckTitle}>{t('flyteDeck')}</h2>
<IconButton aria-label="close" onClick={onCloseDeck} className={styles.close}>
<Close />
</IconButton>
</div>
<ExecutionNodeDeck deckUri={execution.value.closure.deckUri} />
</Dialog>
) : null}
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useDownloadLocation } from 'components/hooks/useDataProxy';
import { WaitForData } from 'components/common/WaitForData';
import * as React from 'react';

/** Fetches and renders the deck data for a given `deckUri` */
export const ExecutionNodeDeck: React.FC<{ deckUri: string }> = ({ deckUri }) => {
const downloadLocation = useDownloadLocation(deckUri);

return (
<WaitForData {...downloadLocation}>
<iframe title="deck" height="100%" src={downloadLocation.value.signedUrl} />
</WaitForData>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,6 @@ const useStyles = makeStyles((theme: Theme) => {
marginTop: theme.spacing(2),
paddingTop: theme.spacing(2),
},
actionsContainer: {
borderTop: `1px solid ${theme.palette.divider}`,
marginTop: theme.spacing(2),
paddingTop: theme.spacing(2),
},
nodeTypeContent: {
minWidth: theme.spacing(9),
},
Expand Down Expand Up @@ -403,9 +398,9 @@ export const NodeExecutionDetailsPanelContent: React.FC<NodeExecutionDetailsProp
{!dag && detailsContent}
{details && (
<ExecutionDetailsActions
className={styles.actionsContainer}
details={details}
nodeExecutionId={nodeExecutionId}
phase={nodeExecution?.closure.phase}
/>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createLocalizedString } from '@flyteconsole/locale';

const str = {
rerun: 'RERUN',
flyteDeck: 'Flyte Deck',
};

export { patternKey } from '@flyteconsole/locale';
Expand Down
17 changes: 17 additions & 0 deletions packages/zapp/console/src/components/hooks/useDataProxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useAPIContext } from 'components/data/apiContext';
import { DownloadLocation } from 'models/Execution/types';
import { FetchableData } from './types';
import { useFetchableData } from './useFetchableData';

/** A hook for fetching a NodeExecution */
export function useDownloadLocation(nativeUrl: string): FetchableData<DownloadLocation> {
const { getDownloadLocation } = useAPIContext();
return useFetchableData<DownloadLocation, string>(
{
debugName: 'CreateDownloadLocation',
defaultValue: {} as DownloadLocation,
doFetch: (nativeUrl) => getDownloadLocation(nativeUrl),
},
nativeUrl,
);
}
1 change: 1 addition & 0 deletions packages/zapp/console/src/models/Common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const endpointPrefixes = {
taskExecution: '/task_executions',
taskExecutionChildren: '/children/task_executions',
workflow: '/workflows',
dataProxyArtifactUrn: '/dataproxy/artifact_urn',
anrusina marked this conversation as resolved.
Show resolved Hide resolved
};

export const identifierPrefixes: { [k in ResourceType]: string } = {
Expand Down
14 changes: 13 additions & 1 deletion packages/zapp/console/src/models/Execution/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Admin, Core, Protobuf } from 'flyteidl';
import { Admin, Core, Protobuf, Service } from 'flyteidl';
import { getAdminEntity, postAdminEntity } from 'models/AdminEntity/AdminEntity';
import {
defaultListExecutionChildrenConfig,
Expand All @@ -11,6 +11,7 @@ import { makeIdentifierPath } from 'models/Common/utils';
import { defaultExecutionPrincipal } from './constants';
import { ExecutionState } from './enums';
import {
DownloadLocation,
Execution,
ExecutionData,
ExecutionMetadata,
Expand All @@ -22,6 +23,7 @@ import {
} from './types';
import {
executionListTransformer,
makeCreateDownloadLocationPath,
makeExecutionPath,
makeNodeExecutionListPath,
makeNodeExecutionPath,
Expand Down Expand Up @@ -56,6 +58,16 @@ export const getExecution = (id: WorkflowExecutionIdentifier, config?: RequestCo
config,
);

/** Fetches a signed url of the native url */
export const getDownloadLocation = (nativeUrl: string, config?: RequestConfig) =>
getAdminEntity<Service.CreateDownloadLocationResponse, DownloadLocation>(
{
path: makeCreateDownloadLocationPath(nativeUrl),
messageType: Service.CreateDownloadLocationResponse,
},
config,
);

const emptyExecutionData: ExecutionData = {
inputs: {},
outputs: {},
Expand Down
9 changes: 8 additions & 1 deletion packages/zapp/console/src/models/Execution/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Admin, Core, Event, Protobuf } from 'flyteidl';
import { Admin, Core, Event, Protobuf, Service } from 'flyteidl';
import { Identifier, LiteralMap, LiteralMapBlob, TaskLog, UrlBlob } from 'models/Common/types';
import { CompiledWorkflow } from 'models/Workflow/types';
import {
Expand Down Expand Up @@ -55,6 +55,11 @@ export interface Execution extends Admin.IExecution {
spec: ExecutionSpec;
}

export interface DownloadLocation extends Service.CreateDownloadLocationResponse {
signedUrl: string;
expiresAt: Protobuf.ITimestamp;
}

/** Node executions */
export interface WorkflowNodeMetadata {
executionId: WorkflowExecutionIdentifier;
Expand Down Expand Up @@ -102,6 +107,7 @@ export interface NodeExecutionClosure extends Admin.INodeExecutionClosure {
startedAt?: Protobuf.ITimestamp;
taskNodeMetadata?: TaskNodeMetadata;
workflowNodeMetadata?: WorkflowNodeMetadata;
deckUri?: string;
}

/** Task executions */
Expand Down Expand Up @@ -138,5 +144,6 @@ export interface ExecutionData {
outputs?: UrlBlob; // TODO FC#393: this field was deprecated use fullOutputs instead - check for usage and remove
fullInputs: LiteralMap | null;
fullOutputs: LiteralMap | null;
deckUri?: string;
dynamicWorkflow?: CompiledWorkflow;
}
4 changes: 4 additions & 0 deletions packages/zapp/console/src/models/Execution/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ export const makeTaskExecutionPath = ({
retryAttempt,
].join('/');

/** Generates the API endpoint for the CreateDownloadLocation request */
export const makeCreateDownloadLocationPath = (nativeUrl: string) =>
`${endpointPrefixes.dataProxyArtifactUrn}?native_url=${nativeUrl}`;

/** Transformer to coerce an `Admin.ExecutionList` into a standard shape */
export const executionListTransformer = createPaginationTransformer<Execution, Admin.ExecutionList>(
'executions',
Expand Down
4 changes: 2 additions & 2 deletions packages/zapp/console/webpack.dev.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as webpack from 'webpack';
import * as HTMLWebpackPlugin from 'html-webpack-plugin';
import * as path from 'path';
import chalk from 'chalk';
import { LOCAL_DEV_HOST, CERTIFICATE_PATH } from './env';
import { LOCAL_DEV_HOST, CERTIFICATE_PATH, ADMIN_API_USE_SSL } from './env';

const { merge } = require('webpack-merge');
const fs = require('fs');
Expand Down Expand Up @@ -42,7 +42,7 @@ export const clientConfig: webpack.Configuration = merge(common.default.clientCo
host: LOCAL_DEV_HOST,
historyApiFallback: true,
server: {
type: 'https',
type: ADMIN_API_USE_SSL,
options: {
key: fs.readFileSync(`${CERTIFICATE_PATH}/server.key`),
cert: fs.readFileSync(`${CERTIFICATE_PATH}/server.crt`),
Expand Down
Loading