Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
38 changes: 38 additions & 0 deletions src/@types/Instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,49 @@ export type InstanceBase = {
id: string;
} & Partial<Instance>;

export type UserScriptPhase =
| "osmorphing_pre_os_mount"
| "osmorphing_post_os_mount"
| "replica_first_boot";

export const USER_SCRIPT_PHASES: UserScriptPhase[] = [
"osmorphing_pre_os_mount",
"osmorphing_post_os_mount",
"replica_first_boot",
];

export const DEFAULT_USER_SCRIPT_PHASE: UserScriptPhase =
"osmorphing_post_os_mount";

export const USER_SCRIPT_PHASE_OPTIONS: {
label: string;
value: UserScriptPhase;
}[] = [
{ label: "OS morphing: before mount", value: "osmorphing_pre_os_mount" },
{ label: "OS morphing: after mount", value: "osmorphing_post_os_mount" },
{ label: "VM first boot script", value: "replica_first_boot" },
];

export const USER_SCRIPT_PHASE_DESCRIPTIONS: Record<UserScriptPhase, string> = {
osmorphing_pre_os_mount:
"Runs before the OS partition is mounted during OS morphing, e.g. to unlock encrypted disks.",
osmorphing_post_os_mount:
"Runs after the OS partition is mounted during OS morphing (the default).",
replica_first_boot:
"Injected during OS morphing and executed when the VM boots for the first time.",
};

export type InstanceScript = {
global?: "windows" | "linux" | null;
instanceId?: string | null;
scriptContent: string | null;
fileName: string | null;
phase?: UserScriptPhase;
};

export type UserScriptTarget = {
global: "windows" | "linux" | null;
instanceId: string | null;
};

export const InstanceUtils = {
Expand Down
15 changes: 11 additions & 4 deletions src/@types/MainItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import type { Execution } from "./Execution";
import type { Instance, InstanceScript } from "./Instance";
import type { Instance, InstanceScript, UserScriptPhase } from "./Instance";
import type { NetworkMap } from "./Network";
import type { StorageMap } from "./Endpoint";
import { Task } from "./Task";
Expand Down Expand Up @@ -100,13 +100,20 @@ export type TransferItem = BaseItem & {
scenario: string;
};

export type UserScriptItem = {
phase: UserScriptPhase;
payload: string;
};

export type UserScriptValue = string | UserScriptItem[] | null;

export type UserScriptData = {
global?: {
linux?: string | null;
windows?: string | null;
linux?: UserScriptValue;
windows?: UserScriptValue;
};
instances?: {
[instanceName: string]: string | null;
[instanceName: string]: UserScriptValue;
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ import deploymentImage from "./images/deployment.svg";
import deploymentFields from "./DeploymentFields";

import type { Field } from "@src/@types/Field";
import type { Instance, InstanceScript } from "@src/@types/Instance";
import type {
Instance,
InstanceScript,
UserScriptTarget,
} from "@src/@types/Instance";
import { applyUserScriptsChange } from "@src/utils/UserScriptUtils";
const Wrapper = styled.div<any>`
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -171,24 +176,14 @@ class DeploymentOptions extends React.Component<Props, State> {
});
}

handleCancelScript(global: string | null, instanceName: string | null) {
this.setState(prevState => ({
uploadedScripts: prevState.uploadedScripts.filter(s =>
global ? s.global !== global : s.instanceId !== instanceName,
),
}));
}

handleScriptUpload(script: InstanceScript) {
this.setState(prevState => ({
uploadedScripts: [...prevState.uploadedScripts, script],
}));
}

handleScriptRemove(script: InstanceScript) {
this.setState(prevState => ({
removedScripts: [...prevState.removedScripts, script],
}));
handleScriptsChange(
target: UserScriptTarget,
scripts: InstanceScript[],
hadExisting: boolean,
) {
this.setState(prevState =>
applyUserScriptsChange(prevState, target, scripts, hadExisting),
);
}

renderField(field: Field) {
Expand Down Expand Up @@ -270,14 +265,8 @@ class DeploymentOptions extends React.Component<Props, State> {
<WizardScripts
instances={this.props.instances}
loadingInstances={this.props.loadingInstances}
onScriptUpload={s => {
this.handleScriptUpload(s);
}}
onScriptDataRemove={s => {
this.handleScriptRemove(s);
}}
onCancelScript={(g, i) => {
this.handleCancelScript(g, i);
onScriptsChange={(target, scripts, hadExisting) => {
this.handleScriptsChange(target, scripts, hadExisting);
}}
uploadedScripts={this.state.uploadedScripts}
removedScripts={this.state.removedScripts}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,28 +34,43 @@ jest.mock("@src/components/modules/WizardModule/WizardScripts", () => ({
<div
data-testid="ScriptsRemove"
onClick={() => {
props.onScriptDataRemove(props.uploadedScripts[0]);
props.onScriptsChange(
{ global: "windows", instanceId: null },
[],
true,
);
}}
/>
<div data-testid="ScriptsRemoved">
{props.removedScripts.map(s => s.scriptContent).join(", ")}
{props.removedScripts.map(s => s.global || s.instanceId).join(", ")}
</div>
<div
data-testid="ScriptsCancel"
onClick={() => {
props.onCancelScript("windows", null);
props.onScriptsChange(
{ global: "windows", instanceId: null },
[],
false,
);
props.scrollableRef &&
props.scrollableRef(null as any as HTMLElement);
}}
/>
<div
data-testid="ScriptsUpload"
onClick={() => {
props.onScriptUpload({
scriptContent: `script-content-${Math.random()}`,
fileName: `script-name.ps1`,
global: "windows",
});
props.onScriptsChange(
{ global: "windows", instanceId: null },
[
{
scriptContent: `script-content-${Math.random()}`,
fileName: `script-name.ps1`,
global: "windows",
phase: "osmorphing_post_os_mount",
},
],
false,
);
}}
/>
</div>
Expand Down Expand Up @@ -124,9 +139,8 @@ describe("ReplicaDeploymentOptions", () => {
);
expect(getByTestId("ScriptsRemoved").textContent).toBe("");
fireEvent.click(getByTestId("ScriptsRemove"));
expect(getByTestId("ScriptsRemoved").textContent).toContain(
"script-content",
);
expect(getByTestId("ScriptsUploaded").textContent).toBe("");
expect(getByTestId("ScriptsRemoved").textContent).toContain("windows");
});

it("doesn't render minion pool mappings", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@ import {
StorageMap,
} from "@src/@types/Endpoint";
import type { Field } from "@src/@types/Field";
import type { Instance, InstanceScript } from "@src/@types/Instance";
import type {
Instance,
InstanceScript,
UserScriptTarget,
} from "@src/@types/Instance";
import { applyUserScriptsChange } from "@src/utils/UserScriptUtils";
import {
Network,
NetworkMap,
Expand Down Expand Up @@ -762,27 +767,14 @@ class TransferItemModal extends React.Component<Props, State> {
});
}

handleCancelScript(
global: "windows" | "linux" | null,
instanceName: string | null,
handleScriptsChange(
target: UserScriptTarget,
scripts: InstanceScript[],
hadExisting: boolean,
) {
this.setState(prevState => ({
uploadedScripts: prevState.uploadedScripts.filter(s =>
global ? s.global !== global : s.instanceId !== instanceName,
),
}));
}

handleScriptUpload(script: InstanceScript) {
this.setState(prevState => ({
uploadedScripts: [...prevState.uploadedScripts, script],
}));
}

handleScriptDataRemove(script: InstanceScript) {
this.setState(prevState => ({
removedScripts: [...prevState.removedScripts, script],
}));
this.setState(prevState =>
applyUserScriptsChange(prevState, target, scripts, hadExisting),
);
}

handleStorageChange(mapping: StorageMap) {
Expand Down Expand Up @@ -963,14 +955,8 @@ class TransferItemModal extends React.Component<Props, State> {
<WizardScripts
instances={this.props.instancesDetails}
loadingInstances={this.props.instancesDetailsLoading}
onScriptUpload={s => {
this.handleScriptUpload(s);
}}
onScriptDataRemove={s => {
this.handleScriptDataRemove(s);
}}
onCancelScript={(g, i) => {
this.handleCancelScript(g, i);
onScriptsChange={(target, scripts, hadExisting) => {
this.handleScriptsChange(target, scripts, hadExisting);
}}
uploadedScripts={this.state.uploadedScripts}
removedScripts={this.state.removedScripts}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ import configLoader from "@src/utils/Config";
import transferItemIcon from "./images/transferItemIcon";

import type { WizardData, WizardPage } from "@src/@types/WizardData";
import type { Instance, InstanceScript } from "@src/@types/Instance";
import type {
Instance,
InstanceScript,
UserScriptTarget,
} from "@src/@types/Instance";
import type { Field } from "@src/@types/Field";
import type { Schedule as ScheduleType } from "@src/@types/Schedule";

Expand Down Expand Up @@ -174,10 +178,9 @@ type Props = {
onContentRef: (ref: any) => void;
onReloadOptionsClick: () => void;
onReloadNetworksClick: () => void;
onUserScriptUpload: (instanceScript: InstanceScript) => void;
onCancelUploadedScript: (
global: string | null,
instanceName: string | null,
onUserScriptsChange: (
target: UserScriptTarget,
scripts: InstanceScript[],
) => void;
onTransferExecuteOptionsChange: (field: Field, value: any) => void;
};
Expand Down Expand Up @@ -604,12 +607,10 @@ class WizardPageContent extends React.Component<Props, State> {
body = (
<WizardScripts
instances={this.props.instanceStore.instancesDetails}
onScriptUpload={this.props.onUserScriptUpload}
onCancelScript={this.props.onCancelUploadedScript}
onScriptsChange={this.props.onUserScriptsChange}
uploadedScripts={this.props.uploadedUserScripts}
userScriptData={null}
removedScripts={[]}
onScriptDataRemove={() => {}}
/>
);
break;
Expand Down
Loading
Loading