1691 lines
65 KiB
React
1691 lines
65 KiB
React
import React, { memo, useContext, useEffect, useState } from 'react';
|
|
import { getTheme } from "../theme.jsx";
|
|
import {
|
|
Tooltip,
|
|
Typography,
|
|
Switch,
|
|
TextField,
|
|
Button,
|
|
ButtonGroup,
|
|
List,
|
|
ListItem,
|
|
ListItemText,
|
|
IconButton,
|
|
Dialog,
|
|
DialogTitle,
|
|
DialogActions,
|
|
DialogContent,
|
|
Checkbox,
|
|
Divider,
|
|
Tab,
|
|
Tabs,
|
|
Collapse,
|
|
Skeleton,
|
|
Grid,
|
|
Chip,
|
|
MenuItem,
|
|
} from "@mui/material";
|
|
import { CopyToClipboard } from "../views/Docs.jsx"
|
|
import {
|
|
FileCopy as FileCopyIcon,
|
|
CheckCircle as CheckCircleIcon,
|
|
Cached as CachedIcon,
|
|
Cloud as CloudIcon,
|
|
Cancel as CancelIcon,
|
|
Help as HelpIcon,
|
|
ExpandLess as ExpandLessIcon,
|
|
ExpandMore as ExpandMoreIcon,
|
|
} from "@mui/icons-material";
|
|
import { toast } from 'react-toastify';
|
|
import { Context } from '../context/ContextApi.jsx';
|
|
import { green, red } from '../views/AngularWorkflow.jsx'
|
|
import AppSearch from "../components/AppSearch1.jsx";
|
|
|
|
const EnvironmentTab = memo((props) => {
|
|
const { globalUrl, isCloud, userdata, selectedOrganization } = props;
|
|
const [environments, setEnvironments] = React.useState([]);
|
|
const [showArchived, setShowArchived] = React.useState(false);
|
|
const [modalUser, setModalUser] = React.useState({});
|
|
const [loginInfo, setLoginInfo] = React.useState("");
|
|
const [modalOpen, setModalOpen] = React.useState(false);
|
|
const [showLoader, setShowLoader] = useState(true)
|
|
const [commandController, setCommandController] = React.useState({
|
|
pipelines: false,
|
|
proxies: false,
|
|
})
|
|
const [installationTab, setInstallationTab] = React.useState(0);
|
|
const [isExpanded, setIsExpanded] = React.useState(false);
|
|
const [listItemExpanded, setListItemExpanded] = React.useState(-1);
|
|
const [, setUpdate] = React.useState(0);
|
|
const [showDistributionPopup, setShowDistributionPopup] = React.useState(false);
|
|
const [selectedEnvironment, setSelectedEnvironment] = React.useState(null);
|
|
const [selectedSubOrg, setSelectedSubOrg] = React.useState([]);
|
|
const [showLocationActionModal, setShowLocationActionModal] = React.useState(undefined)
|
|
|
|
const { themeMode, supportEmail, brandColor } = useContext(Context);
|
|
const theme = getTheme(themeMode, brandColor);
|
|
|
|
|
|
useEffect(() => {
|
|
getEnvironments();
|
|
setModalUser({});
|
|
}, []);
|
|
|
|
const changeModalData = (field, value) => {
|
|
modalUser[field] = value;
|
|
};
|
|
|
|
// Horrible frontend fix for environments
|
|
const setDefaultEnvironment = (environment) => {
|
|
// FIXME - add more checks to this
|
|
toast("Setting default location to " + environment.Name);
|
|
var newEnv = [];
|
|
for (var key in environments) {
|
|
if (environments[key].id == environment.id) {
|
|
if (environments[key].archived) {
|
|
toast("Can't set archived to default");
|
|
return;
|
|
}
|
|
|
|
environments[key].default = true;
|
|
} else if (
|
|
environments[key].default == true &&
|
|
environments[key].id !== environment.id
|
|
) {
|
|
environments[key].default = false;
|
|
}
|
|
|
|
newEnv.push(environments[key]);
|
|
}
|
|
|
|
// Just use this one?
|
|
const url = globalUrl + "/api/v1/setenvironments";
|
|
fetch(url, {
|
|
method: "PUT",
|
|
credentials: "include",
|
|
body: JSON.stringify(newEnv),
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
})
|
|
.then((response) =>
|
|
response.json().then((responseJson) => {
|
|
if (responseJson["success"] === false) {
|
|
toast(responseJson.reason);
|
|
setTimeout(() => {
|
|
getEnvironments();
|
|
}, 1500);
|
|
} else {
|
|
setLoginInfo("");
|
|
setModalOpen(false);
|
|
setTimeout(() => {
|
|
getEnvironments();
|
|
}, 1500);
|
|
}
|
|
}),
|
|
)
|
|
.catch((error) => {
|
|
console.log("Error in backend data: ", error);
|
|
});
|
|
};
|
|
|
|
const rerunCloudWorkflows = (environment) => {
|
|
toast("Starting execution reruns. This can run in the background.");
|
|
fetch(`${globalUrl}/api/v1/environments/${environment.id}/rerun`, {
|
|
method: "GET",
|
|
credentials: "include",
|
|
})
|
|
.then((response) => {
|
|
if (response.status !== 200) {
|
|
console.log("Status not 200 for apps :O!");
|
|
return;
|
|
} else {
|
|
toast(response.reason);
|
|
//toast("Aborted all dangling workflows");
|
|
}
|
|
|
|
return response.json();
|
|
})
|
|
.then((responseJson) => {
|
|
console.log("Got response for execution: ", responseJson);
|
|
//console.log("RESPONSE: ", responseJson)
|
|
//setFiles(responseJson)
|
|
})
|
|
.catch((error) => {
|
|
//toast(error.toString())
|
|
});
|
|
}
|
|
|
|
const getEnvironments = () => {
|
|
fetch(globalUrl + "/api/v1/getenvironments", {
|
|
method: "GET",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Accept: "application/json",
|
|
},
|
|
credentials: "include",
|
|
})
|
|
.then((response) => {
|
|
if (response.status !== 200) {
|
|
console.log("Status not 200 for apps :O!");
|
|
return;
|
|
}
|
|
|
|
return response.json();
|
|
})
|
|
.then((responseJson) => {
|
|
setEnvironments(responseJson);
|
|
setShowLoader(false)
|
|
// Helper info for users in case they have a large queue and don't know about queue flushing
|
|
if (responseJson !== undefined && responseJson !== null && responseJson.length > 0) {
|
|
if (responseJson.length === 1 && responseJson[0].Type !== "cloud") {
|
|
setListItemExpanded(0)
|
|
}
|
|
for (var i = 0; i < responseJson.length; i++) {
|
|
const env = responseJson[i];
|
|
|
|
// Check if queuesize is too large
|
|
if (env.queue !== undefined && env.queue !== null && env.queue > 100) {
|
|
toast("Queue size for " + env.name + " is very large. We recommend you to reduce it by flushing the queue before continuing.");
|
|
break
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
toast(error.toString());
|
|
});
|
|
};
|
|
|
|
const flushQueue = (name) => {
|
|
// Just use this one?
|
|
const url = globalUrl + "/api/v1/flush_queue";
|
|
fetch(url, {
|
|
method: "DELETE",
|
|
credentials: "include",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
})
|
|
.then((response) =>
|
|
response.json().then((responseJson) => {
|
|
if (responseJson["success"] === false) {
|
|
toast(responseJson.reason);
|
|
getEnvironments();
|
|
} else {
|
|
setLoginInfo("");
|
|
setModalOpen(false);
|
|
getEnvironments();
|
|
}
|
|
}),
|
|
)
|
|
.catch((error) => {
|
|
console.log("Error when deleting: ", error);
|
|
});
|
|
};
|
|
|
|
|
|
const deleteEnvironment = (environment) => {
|
|
// FIXME - add some check here ROFL
|
|
//const name = environment.name
|
|
|
|
//toast("Modifying environment " + name)
|
|
//var newEnv = []
|
|
//for (var key in environments) {
|
|
// if (environments[key].Name == name) {
|
|
// if (environments[key].default) {
|
|
// toast("Can't modify the default environment")
|
|
// return
|
|
// }
|
|
|
|
// if (environments[key].type === "cloud" && !environments[key].archived) {
|
|
// toast("Can't modify cloud environments")
|
|
// return
|
|
// }
|
|
|
|
// environments[key].archived = !environments[key].archived
|
|
// }
|
|
|
|
// newEnv.push(environments[key])
|
|
//}
|
|
const id = environment.id;
|
|
|
|
//toast("Modifying environment " + environment.Name)
|
|
var newEnv = [];
|
|
for (var key in environments) {
|
|
if (environments[key].id == id) {
|
|
if (environments[key].default) {
|
|
toast("Can't modify the default environment. Change the default environment first.");
|
|
return;
|
|
}
|
|
|
|
if (environments[key].type === "cloud" && !environments[key].archived) {
|
|
toast("Can't modify cloud environments");
|
|
return;
|
|
}
|
|
|
|
environments[key].archived = !environments[key].archived;
|
|
}
|
|
|
|
newEnv.push(environments[key]);
|
|
}
|
|
|
|
// Just use this one?
|
|
const url = globalUrl + "/api/v1/setenvironments";
|
|
fetch(url, {
|
|
method: "PUT",
|
|
credentials: "include",
|
|
body: JSON.stringify(newEnv),
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
})
|
|
.then((response) =>
|
|
response.json().then((responseJson) => {
|
|
if (responseJson["success"] === false) {
|
|
toast(responseJson.reason);
|
|
getEnvironments();
|
|
} else {
|
|
setLoginInfo("");
|
|
setModalOpen(false);
|
|
getEnvironments();
|
|
}
|
|
}),
|
|
)
|
|
.catch((error) => {
|
|
console.log("Error when deleting: ", error);
|
|
});
|
|
};
|
|
|
|
const abortEnvironmentWorkflows = (environment) => {
|
|
//console.log("Aborting all workflows started >10 minutes ago, not finished");
|
|
toast(
|
|
"Clearing the queue - this may take some time. A new will show up when finished.",
|
|
);
|
|
|
|
fetch(
|
|
`${globalUrl}/api/v1/environments/${environment.id}/stop?deleteall=true`,
|
|
{
|
|
method: "GET",
|
|
credentials: "include",
|
|
},
|
|
)
|
|
.then((response) => {
|
|
if (response.status !== 200) {
|
|
console.log("Status not 200 for apps :O!");
|
|
toast("Failed aborting dangling workflows");
|
|
return;
|
|
} else {
|
|
toast("Successfully cleared the queue");
|
|
|
|
getEnvironments();
|
|
}
|
|
|
|
return response.json();
|
|
})
|
|
.then((responseJson) => {
|
|
console.log("Got response for execution: ", responseJson);
|
|
//console.log("RESPONSE: ", responseJson)
|
|
//setFiles(responseJson)
|
|
})
|
|
.catch((error) => {
|
|
//toast(error.toString())
|
|
});
|
|
};
|
|
|
|
const changeRecommendation = (recommendation, action) => {
|
|
const data = {
|
|
action: action,
|
|
name: recommendation.name,
|
|
};
|
|
|
|
fetch(`${globalUrl}/api/v1/recommendations/modify`, {
|
|
mode: "cors",
|
|
method: "POST",
|
|
body: JSON.stringify(data),
|
|
credentials: "include",
|
|
crossDomain: true,
|
|
withCredentials: true,
|
|
headers: {
|
|
"Content-Type": "application/json; charset=utf-8",
|
|
},
|
|
})
|
|
.then((response) => {
|
|
if (response.status === 200) {
|
|
} else {
|
|
}
|
|
|
|
return response.json();
|
|
})
|
|
.then((responseJson) => {
|
|
if (responseJson.success === true) {
|
|
getEnvironments();
|
|
} else {
|
|
if (
|
|
responseJson.success === false &&
|
|
responseJson.reason !== undefined
|
|
) {
|
|
toast("Failed change recommendation: ", responseJson.reason);
|
|
} else {
|
|
toast("Failed change recommendation");
|
|
}
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
toast(
|
|
`Failed dismissing alert. Please contact ${supportEmail} if this persists.`,
|
|
);
|
|
});
|
|
};
|
|
|
|
const getOrborusCommand = (environment) => {
|
|
if (environment.Type === "cloud") {
|
|
//toast("No Orborus necessary for environment cloud. Create and use a different environment to run executions on-premises.",)
|
|
return
|
|
}
|
|
|
|
if (
|
|
props.userdata.active_org === undefined ||
|
|
props.userdata.active_org === null
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const elementName = "copy_element_shuffle";
|
|
var auth =
|
|
environment.auth === ""
|
|
? "cb5st3d3Z!3X3zaJ*Pc"
|
|
: environment.auth
|
|
|
|
// Escape exclamation marks for copying
|
|
auth = auth.replace("\\!", "!").replace(/!/g, "\\!")
|
|
|
|
const newUrl =
|
|
globalUrl === "https://shuffler.io"
|
|
? "https://shuffle-backend-stbuwivzoq-nw.a.run.app"
|
|
: globalUrl;
|
|
|
|
var skipPipeline = false
|
|
if (commandController.pipelines === true) {
|
|
skipPipeline = true
|
|
}
|
|
|
|
var addProxy = false
|
|
if (commandController.proxies === true) {
|
|
addProxy = true
|
|
}
|
|
|
|
if (installationTab === 1) {
|
|
return (`docker run -d \\
|
|
--restart=always \\
|
|
--name="shuffle-orborus" \\
|
|
--pull=always \\
|
|
--volume "/var/run/docker.sock:/var/run/docker.sock" \\
|
|
-e AUTH="${auth}" \\
|
|
-e ENVIRONMENT_NAME="${environment.Name}" \\
|
|
-e ORG="${environment.org_id}" \\
|
|
-e SHUFFLE_WORKER_IMAGE="ghcr.io/shuffle/shuffle-worker:latest" \\
|
|
-e SHUFFLE_SWARM_CONFIG=run \\
|
|
-e SHUFFLE_LOGS_DISABLED=true \\
|
|
-e BASE_URL="${newUrl}" \\${addProxy ? `
|
|
-e HTTPS_PROXY=IP:PORT \\` : ""}${skipPipeline ? `
|
|
-e SHUFFLE_SKIP_PIPELINES=true \\` : ""}
|
|
ghcr.io/shuffle/shuffle-orborus:latest
|
|
`)
|
|
} else if (installationTab === 2) {
|
|
return `https://shuffler.io/docs/configuration#kubernetes`
|
|
}
|
|
|
|
const commandData = `docker rm shuffle-orborus --force; \\\ndocker run -d \\
|
|
--restart=always \\
|
|
--name="shuffle-orborus" \\
|
|
--pull=always \\
|
|
--volume "/var/run/docker.sock:/var/run/docker.sock" \\
|
|
-e AUTH="${auth}" \\
|
|
-e ENVIRONMENT_NAME="${environment.Name}" \\
|
|
-e ORG="${props.userdata.active_org.id}" \\
|
|
-e BASE_URL="${newUrl}" \\${addProxy ? `
|
|
-e HTTPS_PROXY=IP:PORT \\` : ""}${skipPipeline ? `
|
|
-e SHUFFLE_SKIP_PIPELINES=true \\` : ""}
|
|
ghcr.io/shuffle/shuffle-orborus:latest`
|
|
|
|
return commandData
|
|
};
|
|
|
|
const submitEnvironment = (data) => {
|
|
// FIXME - add some check here ROFL
|
|
environments.push({
|
|
name: data.environment,
|
|
type: "onprem",
|
|
});
|
|
|
|
// Just use this one?
|
|
var baseurl = globalUrl;
|
|
const url = baseurl + "/api/v1/setenvironments";
|
|
fetch(url, {
|
|
method: "PUT",
|
|
credentials: "include",
|
|
body: JSON.stringify(environments),
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
})
|
|
.then((response) =>
|
|
response.json().then((responseJson) => {
|
|
if (responseJson["success"] === false) {
|
|
setLoginInfo("Error in input: " + responseJson.reason);
|
|
getEnvironments();
|
|
} else {
|
|
setLoginInfo("");
|
|
setModalOpen(false);
|
|
getEnvironments();
|
|
}
|
|
}),
|
|
)
|
|
.catch((error) => {
|
|
console.log("Error in userdata: ", error);
|
|
});
|
|
};
|
|
|
|
const modalView = (
|
|
<Dialog
|
|
open={modalOpen}
|
|
onClose={() => {
|
|
setModalOpen(false);
|
|
}}
|
|
PaperProps={{
|
|
sx: {
|
|
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
|
|
border: theme?.palette?.DialogStyle?.border,
|
|
minWidth: "800px",
|
|
minHeight: "320px",
|
|
fontFamily: theme?.typography?.fontFamily,
|
|
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
|
zIndex: 1000,
|
|
'& .MuiDialogContent-root': {
|
|
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
|
},
|
|
'& .MuiDialogTitle-root': {
|
|
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
|
},
|
|
'& .MuiDialogActions-root': {
|
|
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
|
},
|
|
},
|
|
}}
|
|
>
|
|
<DialogTitle>
|
|
<Typography variant='h5' color="textPrimary" >Add Location</Typography>
|
|
</DialogTitle>
|
|
<DialogContent>
|
|
<div>
|
|
<Typography variant='body2' color="textPrimary">Location Name</Typography>
|
|
<TextField
|
|
color="primary"
|
|
style={{ backgroundColor: theme.palette.textFieldStyle.backgroundColor,}}
|
|
autoFocus
|
|
InputProps={{
|
|
style: {
|
|
height: "50px",
|
|
color: theme.palette.textFieldStyle.color,
|
|
fontSize: "1em",
|
|
},
|
|
}}
|
|
required
|
|
fullWidth={true}
|
|
placeholder="datacenter froglantern"
|
|
id="environment_name"
|
|
margin="normal"
|
|
variant="outlined"
|
|
onChange={(event) =>
|
|
changeModalData("environment", event.target.value)
|
|
}
|
|
/>
|
|
</div>
|
|
{loginInfo}
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<Button
|
|
style={{ borderRadius: "2px", fontSize: 16, textTransform: "none", color: theme.palette.primary.main, }}
|
|
onClick={() => setModalOpen(false)}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
variant="contained"
|
|
style={{ borderRadius: "2px", fontSize: 16, textTransform: "none", }}
|
|
onClick={() => {
|
|
submitEnvironment(modalUser); // Assuming modalUser is available
|
|
}}
|
|
color="primary"
|
|
>
|
|
Submit
|
|
</Button>
|
|
</DialogActions>
|
|
</Dialog>
|
|
);
|
|
|
|
|
|
const textColor = "#9E9E9E !important";
|
|
|
|
const handleSelectSubOrg = (id, action) => {
|
|
if (action === "all") {
|
|
const childOrgs = userdata.orgs.filter(
|
|
(data) => data.creator_org === userdata.active_org.id
|
|
);
|
|
setSelectedSubOrg((prev) => {
|
|
if (prev.length === childOrgs.length) {
|
|
// If all child orgs are already selected, clear the selection
|
|
return [];
|
|
} else {
|
|
// Otherwise, select all child org IDs
|
|
return childOrgs.map((data) => data.id);
|
|
}
|
|
});
|
|
} else if (action === "none") {
|
|
setSelectedSubOrg([]);
|
|
} else {
|
|
setSelectedSubOrg((prev) => {
|
|
if (prev.includes(id)) {
|
|
return prev.filter((data) => data !== id);
|
|
} else {
|
|
return [...prev, id];
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
const queueSizeText = (queue) => {
|
|
if (queue === undefined || queue === null) return 0;
|
|
if (queue < 0) return 0;
|
|
if (queue > 1000) return ">1000";
|
|
return queue;
|
|
};
|
|
|
|
const LocationActionModal = (props) => {
|
|
const { showLocationActionModal } = props
|
|
|
|
const [searchQuery, setSearchQuery] = React.useState("");
|
|
|
|
if (showLocationActionModal === undefined || showLocationActionModal === null) {
|
|
return null
|
|
}
|
|
|
|
if (showLocationActionModal?.open !== true) {
|
|
return null
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
<Dialog
|
|
open={true}
|
|
onClose={() => {
|
|
setShowLocationActionModal(undefined)
|
|
}}
|
|
PaperProps={{
|
|
sx: {
|
|
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
|
|
border: theme?.palette?.DialogStyle?.border,
|
|
fontFamily: theme?.typography?.fontFamily,
|
|
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
|
zIndex: 1000,
|
|
minWidth: 600,
|
|
minHeight: 500,
|
|
overflow: "auto",
|
|
'& .MuiDialogContent-root': {
|
|
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
|
},
|
|
'& .MuiDialogTitle-root': {
|
|
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
|
},
|
|
'& .MuiDialogActions-root': {
|
|
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
|
},
|
|
},
|
|
}}
|
|
>
|
|
<DialogTitle>
|
|
<div style={{ color: "rgba(255,255,255,0.9)" }}>
|
|
Select a job to send
|
|
</div>
|
|
</DialogTitle>
|
|
<DialogContent style={{ backgroundColor: theme.palette.backgroundColor }}>
|
|
<Checkbox
|
|
checked={true}
|
|
disabled={true}
|
|
/>
|
|
<span style={{ marginLeft: 8 }}>
|
|
Find app to re-download
|
|
</span>
|
|
|
|
<AppSearch
|
|
/>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|
|
|
|
const editEnvironmentConfig = (id, selectedSubOrg, cacheKey) => {
|
|
const data = {
|
|
action: "suborg_distribute",
|
|
selected_suborgs: selectedSubOrg,
|
|
}
|
|
|
|
const url = `${globalUrl}/api/v1/environments/${id}/config`;
|
|
|
|
fetch(url, {
|
|
mode: "cors",
|
|
method: "POST",
|
|
body: JSON.stringify(data),
|
|
credentials: "include",
|
|
crossDomain: true,
|
|
withCredentials: true,
|
|
headers: {
|
|
"Content-Type": "application/json; charset=utf-8",
|
|
},
|
|
})
|
|
.then((response) =>
|
|
response.json().then((responseJson) => {
|
|
if (responseJson["success"] === false) {
|
|
toast("Failed overwriting environments");
|
|
} else {
|
|
toast("Successfully updated environments!");
|
|
setTimeout(() => {
|
|
getEnvironments();
|
|
setShowDistributionPopup(false);
|
|
}, 1000);
|
|
}
|
|
})
|
|
)
|
|
.catch((error) => {
|
|
toast("Err: " + error.toString());
|
|
});
|
|
};
|
|
|
|
const changeDistribution = (id, selectedSubOrg) => {
|
|
|
|
editEnvironmentConfig(id, [...new Set(selectedSubOrg)])
|
|
}
|
|
|
|
const EnvironmentDistributionModal = showDistributionPopup ? (
|
|
<Dialog
|
|
open={showDistributionPopup}
|
|
onClose={() => setShowDistributionPopup(false)}
|
|
PaperProps={{
|
|
sx: {
|
|
borderRadius: theme?.palette?.DialogStyle?.borderRadius,
|
|
border: theme?.palette?.DialogStyle?.border,
|
|
fontFamily: theme?.typography?.fontFamily,
|
|
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
|
zIndex: 1000,
|
|
minWidth: "600px",
|
|
minHeight: "320px",
|
|
overflow: "auto",
|
|
'& .MuiDialogContent-root': {
|
|
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
|
},
|
|
'& .MuiDialogTitle-root': {
|
|
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
|
},
|
|
'& .MuiDialogActions-root': {
|
|
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
|
|
},
|
|
},
|
|
}}
|
|
>
|
|
<DialogTitle>
|
|
<Typography variant='h5' color="textPrimary" >
|
|
Select sub-org to distribute Environments
|
|
</Typography>
|
|
</DialogTitle>
|
|
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
|
|
<MenuItem value="none" onClick={()=> {handleSelectSubOrg(null, "none")}}>None</MenuItem>
|
|
<MenuItem value="all" onClick={()=> {handleSelectSubOrg(null, "all")}}>All</MenuItem>
|
|
{userdata.orgs.map((data, index) => {
|
|
if (data.creator_org !== userdata.active_org.id) {
|
|
return null;
|
|
}
|
|
|
|
const imagesize = 22;
|
|
const imageStyle = {
|
|
width: imagesize,
|
|
height: imagesize,
|
|
pointerEvents: "none",
|
|
marginRight: 10,
|
|
marginLeft: data.id === userdata.active_org.id ? 0 : 20,
|
|
};
|
|
|
|
const image = data.image === "" ? (
|
|
<img alt={data.name} src={theme.palette.defaultImage} style={imageStyle} />
|
|
) : (
|
|
<img alt={data.name} src={data.image} style={imageStyle} />
|
|
);
|
|
|
|
return (
|
|
<MenuItem
|
|
key={index}
|
|
value={data.id}
|
|
onClick={() => handleSelectSubOrg(data.id)}
|
|
style={{ display: "flex", alignItems: "center" }}
|
|
>
|
|
<Checkbox
|
|
checked={selectedSubOrg.includes(data.id)}
|
|
/>
|
|
{image}
|
|
<span style={{ marginLeft: 8 }}>{data.name}</span>
|
|
</MenuItem>
|
|
);
|
|
})}
|
|
|
|
<div style={{ display: "flex", marginTop: 20 }}>
|
|
<Button
|
|
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, color: theme.palette.primary.main }}
|
|
onClick={() => setShowDistributionPopup(false)}
|
|
color="primary"
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
variant="contained"
|
|
style={{ borderRadius: "2px", textTransform: 'none', fontSize:16, marginLeft: 10 }}
|
|
onClick={() => {
|
|
changeDistribution(selectedEnvironment, selectedSubOrg);
|
|
}}
|
|
color="primary"
|
|
>
|
|
Submit
|
|
</Button>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
) : null;
|
|
|
|
return (
|
|
<div style={{ width: "100%", minHeight: 1100, boxSizing: 'border-box', padding: "27px 10px 19px 27px", height:"100%", backgroundColor: theme.palette.platformColor,borderTopRightRadius: '8px', borderBottomRightRadius: 8, borderLeft: theme.palette.defaultBorder, }}>
|
|
{modalView}
|
|
{EnvironmentDistributionModal}
|
|
<div style={{ height: "100%", maxHeight: 1700, overflowY: "auto", scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin' }}>
|
|
<div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: theme.palette.scrollbarColorTransparent, scrollbarWidth: 'thin'}}>
|
|
<div style={{ marginBottom: 20 }}>
|
|
<Typography variant='h5' color="textPrimary" style={{ marginBottom: 8, marginTop: 0,}}>Runtime Locations</Typography>
|
|
<Typography variant='body2' color="textSecondary">
|
|
Decides which Orborus <b>runtime location</b> to run your workflows in. Previously called Environments. <br /> If you have scale problems, <a href="https://shuffler.io/docs/configuration#high-availability" target="_blank" rel="noopener noreferrer" style={{ color: theme.palette.linkColor }}>check the docs</a> or talk to our team: {supportEmail}.
|
|
<a
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
href="/docs/organizations#locations"
|
|
style={{ color: theme.palette.linkColor, }}
|
|
>
|
|
Learn more
|
|
</a>
|
|
</Typography>
|
|
</div>
|
|
<Button
|
|
style={{ borderRadius: 4, textTransform: "capitalize", fontSize: 16, }}
|
|
variant="contained"
|
|
color="primary"
|
|
onClick={() => setModalOpen(true)}
|
|
>
|
|
Add Location
|
|
</Button>
|
|
<Button
|
|
style={{ borderRadius: 4, width: 81, height: 40, marginLeft: 16, marginRight: 15 }}
|
|
variant="contained"
|
|
color="secondary"
|
|
onClick={getEnvironments}
|
|
>
|
|
<CachedIcon />
|
|
</Button>
|
|
<Switch
|
|
checked={showArchived}
|
|
onChange={() => setShowArchived(!showArchived)}
|
|
/>{" "}
|
|
Show disabled
|
|
{/* <Divider
|
|
style={{
|
|
marginTop: 20,
|
|
marginBottom: 20,
|
|
backgroundColor: theme.palette.inputColor,
|
|
}}
|
|
/> */}
|
|
<div
|
|
style={{
|
|
borderRadius: 4,
|
|
marginTop: 24,
|
|
border: theme.palette.defaultBorder,
|
|
width: "100%",
|
|
overflowX: "auto",
|
|
paddingBottom: 0,
|
|
}}
|
|
>
|
|
<List
|
|
style={{
|
|
width: '100%',
|
|
tableLayout: "auto",
|
|
display: "grid",
|
|
minWidth: 800,
|
|
paddingBottom: 0,
|
|
}}
|
|
>
|
|
<ListItem
|
|
style={{
|
|
display: "grid",
|
|
gridTemplateColumns: "80px 80px 80px 120px 120px 120px 120px 350px 150px",
|
|
width: "100%",
|
|
minWidth: 800,
|
|
paddingBottom: 0,
|
|
borderBottom: theme.palette.defaultBorder,
|
|
}}
|
|
>
|
|
{["Type", "Status", "Scale", "Pipeline", "Name", "Type", "Queue", "Actions", "Distribution"].map((header, index) => {
|
|
|
|
return (
|
|
<ListItemText
|
|
key={index}
|
|
primary={header}
|
|
style={{
|
|
padding: "0px 8px 8px 8px",
|
|
whiteSpace: "nowrap",
|
|
textOverflow: "ellipsis",
|
|
overflow: "hidden",
|
|
fontWeight: "bold",
|
|
textAlign: header === "Actions" ? "left" : header === "Distribution" ? "right" : "center",
|
|
}}
|
|
/>
|
|
)
|
|
})}
|
|
</ListItem>
|
|
{showLoader
|
|
? [...Array(6)].map((_, rowIndex) => (
|
|
<ListItem
|
|
key={rowIndex}
|
|
style={{
|
|
display: "grid",
|
|
gridTemplateColumns: "80px 80px 80px 120px 120px 120px 120px 350px 150px",
|
|
backgroundColor: theme.palette.platformColor,
|
|
height: 40,
|
|
width: "100%",
|
|
boxSizing: "border-box",
|
|
}}
|
|
>
|
|
{Array(9).fill(null).map((_, colIndex) => (
|
|
<ListItemText
|
|
key={colIndex}
|
|
style={{
|
|
padding: "0px 8px",
|
|
textAlign: "center", // Optional
|
|
}}
|
|
>
|
|
<Skeleton
|
|
variant="text"
|
|
animation="wave"
|
|
sx={{
|
|
backgroundColor: theme.palette.loaderColor,
|
|
borderRadius: "4px",
|
|
}}
|
|
/>
|
|
</ListItemText>
|
|
))}
|
|
</ListItem>
|
|
))
|
|
: environments?.length === 0 ? (
|
|
<Typography style={{padding: 20, textAlign: 'center', fontSize: 16}}>
|
|
No Locations Found
|
|
</Typography>
|
|
):(
|
|
environments?.map((environment, index) => {
|
|
if (!showArchived && environment.archived) {
|
|
return null;
|
|
}
|
|
|
|
if (environment.archived === undefined) {
|
|
return null;
|
|
}
|
|
|
|
var bgColor = themeMode === "dark" ? "#212121" : "#FFFFFF";
|
|
if (index % 2 === 0) {
|
|
bgColor = themeMode === "dark" ? "#1A1A1A" : "#EAEAEA";
|
|
}
|
|
|
|
// Check if there's a notification for it in userdata.priorities
|
|
var showCPUAlert = false;
|
|
var foundIndex = -1;
|
|
if (
|
|
userdata !== undefined &&
|
|
userdata !== null &&
|
|
userdata.priorities !== undefined &&
|
|
userdata.priorities !== null &&
|
|
userdata.priorities.length > 0
|
|
) {
|
|
foundIndex = userdata.priorities.findIndex(
|
|
(prio) => prio.name.includes("CPU") && prio.active === true,
|
|
);
|
|
|
|
if (
|
|
foundIndex >= 0 &&
|
|
userdata.priorities[foundIndex].name.endsWith(
|
|
environment.Name,
|
|
)
|
|
) {
|
|
showCPUAlert = true;
|
|
}
|
|
}
|
|
|
|
const queueSize =
|
|
environment.queue !== undefined && environment.queue !== null
|
|
? environment.queue < 0
|
|
? 0
|
|
: environment.queue > 1000
|
|
? ">1000"
|
|
: environment.queue
|
|
: 0;
|
|
|
|
|
|
const orborusCommandWrapper = () => {
|
|
// Check the current text
|
|
const orborusCommand = document.getElementById("orborus_command")
|
|
if (orborusCommand === undefined || orborusCommand === null) {
|
|
return getOrborusCommand(environment)
|
|
}
|
|
|
|
return orborusCommand.textContent
|
|
}
|
|
|
|
const isDistributed = environment?.suborg_distribution?.length > 0 ? true : false;
|
|
|
|
return (
|
|
<>
|
|
|
|
<ListItem
|
|
key={index}
|
|
style={{ cursor: "pointer", backgroundColor: bgColor, marginLeft: 0, borderBottomLeftRadius: environments?.length - 1 === index ? 8 : 0, borderBottomRightRadius: environments?.length - 1 === index ? 8 : 0, display: 'grid', gridTemplateColumns: "80px 80px 80px 120px 120px 120px 120px 405px 125px", }}
|
|
onClick={() => {
|
|
if (environment.Type === "cloud") {
|
|
toast("Cloud environments are not configurable. To see what is possible, create a new environment.")
|
|
return
|
|
}
|
|
|
|
setListItemExpanded(listItemExpanded === index ? -1 : index)
|
|
}}
|
|
>
|
|
<ListItemText
|
|
primary={
|
|
environment.run_type === "cloud" ||
|
|
environment.name === "Cloud" ? (
|
|
<Tooltip title="Cloud" placement="top">
|
|
<CloudIcon
|
|
style={{
|
|
color: themeMode === "dark" ? "#CCCCCC" : "#333333",
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
) : environment.run_type === "docker" ? (
|
|
<Tooltip title="Docker" placement="top">
|
|
<img
|
|
src="/icons/docker.svg"
|
|
style={{ width: 30, height: 30 }}
|
|
/>
|
|
</Tooltip>
|
|
) : environment.run_type === "k8s" ? (
|
|
<Tooltip title="Kubernetes" placement="top">
|
|
<img
|
|
src="/icons/k8s.svg"
|
|
style={{ width: 30, height: 30 }}
|
|
/>
|
|
</Tooltip>
|
|
) : (
|
|
<Tooltip title="Unknown" placement="top">
|
|
<HelpIcon
|
|
style={{ color: "rgba(255,255,255,0.8)" }}
|
|
/>
|
|
</Tooltip>
|
|
)
|
|
}
|
|
style={{
|
|
minWidth: 80,
|
|
padding: "8px 8px 8px 0",
|
|
overflow: "hidden",
|
|
whiteSpace: "normal",
|
|
wordWrap: "break-word",
|
|
textAlign: "center",
|
|
display: "table-cell",
|
|
}}
|
|
/>
|
|
|
|
<ListItemText
|
|
primaryTypographyProps={{
|
|
style: {
|
|
marginLeft: -10,
|
|
maxWidth: 100,
|
|
overflow: "hidden",
|
|
whiteSpace: "nowrap",
|
|
textOverflow: "ellipsis",
|
|
padding: "8px 0px 8px 0px",
|
|
transition: "all 0.3s ease",
|
|
textAlign: "center",
|
|
},
|
|
}}
|
|
style={{minWidth:120, display: "table-cell",}}
|
|
primary={
|
|
<Tooltip title={
|
|
<Typography variant="body1" style={{margin: 10, }}>
|
|
{environment.Type !== "cloud"
|
|
? environment.running_ip === undefined ||
|
|
environment.running_ip === null ||
|
|
environment.running_ip.length === 0
|
|
?
|
|
"Not running. Click to get the start command that can be ran on your server."
|
|
:
|
|
<span>IP / label: {environment?.running_ip?.split(":")[0]}. May stay running up to a minute after stopping Orborus.</span>
|
|
:
|
|
`Cloud is automatically configured. Reachout to ${supportEmail} if you have any questions.`
|
|
}
|
|
|
|
<br />
|
|
<br />
|
|
|
|
Last checkin: {environment?.checkin !== undefined && environment.checkin !== null && environment?.checkin > 0 ? new Date(environment?.checkin * 1000).toLocaleString() : "Never"}
|
|
</Typography>
|
|
} placement="top">
|
|
<Typography
|
|
style={{
|
|
minWidth: 100,
|
|
overflow: "hidden",
|
|
whiteSpace: "nowrap",
|
|
textOverflow: "ellipsis",
|
|
wordWrap: "break-word",
|
|
fontSize: 16,
|
|
transition: "all 0.3s ease",
|
|
}}
|
|
variant="body2"
|
|
>
|
|
{environment.Type !== "cloud" &&
|
|
(environment.running_ip === undefined ||
|
|
environment.running_ip === null ||
|
|
environment.running_ip.length === 0)
|
|
?
|
|
<Chip
|
|
key={index}
|
|
style={{
|
|
color: red,
|
|
borderColor: red,
|
|
}}
|
|
label={"Stopped"}
|
|
onClick={() => {
|
|
//handleChipClick
|
|
}}
|
|
variant="outlined"
|
|
color="primary"
|
|
/>
|
|
:
|
|
<Chip
|
|
key={index}
|
|
style={{
|
|
color: green,
|
|
borderColor: green,
|
|
}}
|
|
label={"Running"}
|
|
onClick={() => {
|
|
//handleChipClick
|
|
}}
|
|
variant="outlined"
|
|
color="primary"
|
|
/>
|
|
}
|
|
</Typography>
|
|
</Tooltip>
|
|
}
|
|
/>
|
|
|
|
<ListItemText
|
|
primary={
|
|
environment.licensed ? (
|
|
<Tooltip title="Scale configured (auto on cloud)" placement="top">
|
|
<CheckCircleIcon style={{ color: "#4caf50" }} />
|
|
</Tooltip>
|
|
) : (
|
|
<Tooltip
|
|
title="In Verbose mode. Set SHUFFLE_SWARM_CONFIG=run to Scale. This will not be as verbose. Details: https://shuffler.io/docs/configuration#scaling-shuffle"
|
|
placement="top"
|
|
>
|
|
<a
|
|
href="/docs/configuration#scaling-shuffle"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
<CancelIcon style={{ color: "#f85a3e" }} />
|
|
</a>
|
|
</Tooltip>
|
|
)
|
|
}
|
|
style={{
|
|
minWidth: 60,
|
|
marginLeft: 20,
|
|
overflow: "hidden",
|
|
whiteSpace: "normal",
|
|
wordWrap: "break-word",
|
|
padding: 8,
|
|
display: "table-cell",
|
|
}}
|
|
/>
|
|
|
|
<ListItemText
|
|
primary={
|
|
environment.Type === "cloud" ?
|
|
<Tooltip title={`Make a new environment to set up a Datalake node. Please contact ${supportEmail} if this is something you want to see on Cloud directly.`} placement="top">
|
|
<CancelIcon style={{ color: "rgba(255,255,255,0.3)" }} />
|
|
</Tooltip>
|
|
:
|
|
environment?.data_lake?.enabled && environment?.archived !== true ? (
|
|
<a
|
|
href="/detections/Sigma"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
|
|
<Tooltip title={"Data Lake node enabled. Check /detections/Sigma to learn more"} placement="top">
|
|
<CheckCircleIcon style={{ color: "#4caf50" }} />
|
|
</Tooltip>
|
|
</a>
|
|
) : (
|
|
<Tooltip
|
|
title="Data Lake node disabled. Click to enable."
|
|
placement="top"
|
|
onClick={(e) => {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
|
|
window.open("/detections/Sigma", "_blank")
|
|
}}
|
|
>
|
|
<a
|
|
href="/detections/Sigma"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
<CancelIcon style={{ color: "#f85a3e" }} />
|
|
</a>
|
|
</Tooltip>
|
|
)
|
|
}
|
|
style={{
|
|
minWidth: 60,
|
|
marginLeft: 40,
|
|
overflow: "hidden",
|
|
whiteSpace: "normal",
|
|
wordWrap: "break-word",
|
|
display: "table-cell",
|
|
}}
|
|
/>
|
|
|
|
<ListItemText
|
|
primary={(
|
|
<Tooltip title={environment.Name} placement="top">
|
|
{environment.Name}
|
|
</Tooltip>
|
|
)}
|
|
primaryTypographyProps={{
|
|
style:{
|
|
maxWidth: 150,
|
|
whiteSpace: 'nowrap',
|
|
overflow: "hidden",
|
|
textOverflow: 'ellipsis',
|
|
wordWrap: "break-word",
|
|
transition: "all 0.3s ease",
|
|
}}}
|
|
style={{
|
|
minWidth: 120,
|
|
maxWidth: 150,
|
|
display: "table-cell",
|
|
}}
|
|
/>
|
|
|
|
<ListItemText
|
|
primary={environment.Type}
|
|
primaryTypographyProps={{
|
|
style:{
|
|
minWidth: 70,
|
|
overflow: "hidden",
|
|
whiteSpace: 'nowrap',
|
|
textOverflow: 'ellipsis',
|
|
wordWrap: "break-word",
|
|
transition: "all 0.3s ease",
|
|
textAlign: "center",
|
|
}}}
|
|
style={{display: "table-cell",}}
|
|
/>
|
|
<ListItemText
|
|
primaryTypographyProps={{
|
|
style:{
|
|
minWidth: 82,
|
|
overflow: "hidden",
|
|
whiteSpace: 'nowrap',
|
|
textOverflow: 'ellipsis',
|
|
wordWrap: "break-word",
|
|
transition: "all 0.3s ease",
|
|
textAlign: "center",
|
|
}}}
|
|
primary={queueSize}
|
|
style={{display: "table-cell",}}
|
|
/>
|
|
<ListItemText
|
|
style={{
|
|
minWidth: 350,
|
|
}}
|
|
>
|
|
<div style={{ display: "flex", flexWrap: "nowrap" }}>
|
|
<ButtonGroup
|
|
style={{ borderRadius: "5px 5px 5px 5px", flexWrap: "nowrap", width: "100%" }}
|
|
>
|
|
<Button
|
|
variant="outlined"
|
|
disabled={environment.default || selectedOrganization.id !== environment.org_id}
|
|
style={{
|
|
marginLeft: 0,
|
|
marginRight: 0,
|
|
maxWidth: 150,
|
|
fontSize: 16,
|
|
textTransform: 'none',
|
|
display: "table-cell",
|
|
whiteSpace: "nowrap", // Prevent text wrapping
|
|
}}
|
|
onClick={(e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
setDefaultEnvironment(environment)
|
|
}}
|
|
color="primary"
|
|
>
|
|
Make Default
|
|
</Button>
|
|
<Button
|
|
variant={environment.archived ? "contained" : "outlined"}
|
|
disabled={selectedOrganization.id !== environment.org_id}
|
|
style={{
|
|
fontSize: 16,
|
|
textTransform: 'none',
|
|
whiteSpace: "nowrap", // Prevent text wrapping
|
|
}}
|
|
onClick={(e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
deleteEnvironment(environment)
|
|
}}
|
|
color="primary"
|
|
>
|
|
{environment.archived ? "Activate" : "Disable"}
|
|
</Button>
|
|
|
|
<Button
|
|
variant={"outlined"}
|
|
disabled={selectedOrganization.id !== environment.org_id}
|
|
style={{
|
|
fontSize: 16,
|
|
textTransform: 'none',
|
|
whiteSpace: "nowrap", // Prevent text wrapping
|
|
}}
|
|
onClick={(e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
console.log("Should clear executions for: ", environment);
|
|
|
|
if (isCloud && environment.Name.toLowerCase() === "cloud") {
|
|
rerunCloudWorkflows(environment);
|
|
} else {
|
|
abortEnvironmentWorkflows(environment);
|
|
}
|
|
}}
|
|
color="primary"
|
|
>
|
|
{isCloud && environment.Name.toLowerCase() === "cloud"
|
|
? "Rerun"
|
|
: "Clear"}
|
|
</Button>
|
|
|
|
{environment.Type === "cloud" ? null :
|
|
<Button
|
|
variant={"outlined"}
|
|
style={{
|
|
fontSize: 16,
|
|
textTransform: 'none',
|
|
whiteSpace: "nowrap", // Prevent text wrapping
|
|
}}
|
|
onClick={(e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
toast.info("Please choose an app you want to re-distribute to this environment")
|
|
setShowLocationActionModal({
|
|
environment: environment,
|
|
open: true,
|
|
})
|
|
setUpdate(Math.random())
|
|
}}
|
|
color="primary"
|
|
>
|
|
Send Job
|
|
</Button>
|
|
}
|
|
|
|
</ButtonGroup>
|
|
|
|
<IconButton disabled={environment.Type === "cloud"} onClick={()=> {setIsExpanded(prev => !prev)}}>
|
|
{listItemExpanded === index ? <ExpandLessIcon sx={{color: theme.palette.text.primary}} /> : <ExpandMoreIcon sx={{color: theme.palette.text.primary}}/>}
|
|
</IconButton>
|
|
</div>
|
|
</ListItemText>
|
|
{selectedOrganization.id !== undefined && environment?.org_id !== selectedOrganization.id ?
|
|
<ListItemText
|
|
primary={
|
|
<Tooltip
|
|
title="Parent organization controlled environments. You can use, but not modify this environments. Contact an admin of your parent organization if you need changes to this."
|
|
placement="top"
|
|
>
|
|
<Chip
|
|
label={"Parent"}
|
|
variant="contained"
|
|
color="secondary"
|
|
/>
|
|
</Tooltip>
|
|
}
|
|
style={{ textAlign: 'center', verticalAlign: 'middle', }}
|
|
/>
|
|
:
|
|
<Tooltip
|
|
title={environment.Name === "Cloud" ? "Cloud environments cannot be distributed" : "Distributed to sub-organizations. This means the sub organizations can use this environment, but can not modify it."}
|
|
placement="top"
|
|
>
|
|
<IconButton
|
|
sx={{":hover": {backgroundColor: "transparent"}}}
|
|
disabled={ environment.Name === "Cloud" || userdata?.active_org?.role !== "admin" || (selectedOrganization.creator_org !== undefined && selectedOrganization.creator_org !== null && selectedOrganization.creator_org !== "" )? true : false}
|
|
onClick={(e) => {
|
|
e.stopPropagation()
|
|
setShowDistributionPopup(true)
|
|
if(environment?.suborg_distribution?.length > 0){
|
|
setSelectedSubOrg(environment.suborg_distribution)
|
|
}else{
|
|
setSelectedSubOrg([])
|
|
}
|
|
setSelectedEnvironment(environment.id)
|
|
}}>
|
|
<Checkbox
|
|
disabled={ environment.Name === "Cloud" || userdata?.active_org?.role !== "admin" || (selectedOrganization.creator_org !== undefined && selectedOrganization.creator_org !== null && selectedOrganization.creator_org !== "" )? true : false}
|
|
checked={isDistributed}
|
|
style={{ }}
|
|
color="secondary"
|
|
/>
|
|
</IconButton>
|
|
</Tooltip>
|
|
}
|
|
</ListItem>
|
|
<Collapse in={listItemExpanded === index} timeout="auto" unmountOnExit>
|
|
<Grid container justifyContent="center" style={{minWidth: 850, maxWidth: 850, }}>
|
|
<Grid item xs={12} sm={8} md={6}>
|
|
<div style={{minWidth: 750, maxWidth: 750, minHeight: 350, display: 'flex', justifyContent: "center", backgroundColor: "transparent", }}>
|
|
<div style={{ paddingTop: 50, paddingBottom: 100, }}>
|
|
<Typography variant="h6">
|
|
Self-Hosted Orborus instance
|
|
</Typography>
|
|
<Typography variant="body2" color="textSecondary">
|
|
Orborus is the Shuffle queue handler that runs your hybrid workflows and manages pipelines. It can be run in Docker/k8s container on your server or in your cluster. Follow the steps below, and configure as need be.
|
|
</Typography>
|
|
|
|
<Tabs
|
|
value={installationTab}
|
|
indicatorColor="primary"
|
|
textColor="secondary"
|
|
onChange={(e, inputValue) => {
|
|
setInstallationTab(inputValue)
|
|
}}
|
|
aria-label="disabled tabs example"
|
|
variant="scrollable"
|
|
scrollButtons="auto"
|
|
style={{textAlign: "center", marginTop: 25, }}
|
|
>
|
|
<Tab
|
|
value={0}
|
|
label=<span style={{color: theme.palette.text.secondary, }}>
|
|
<img
|
|
src="/icons/docker.svg"
|
|
style={{ width: 20, height: 20, marginRight: 10, }}
|
|
/> Verbose (default)
|
|
</span>
|
|
/>
|
|
<Tab
|
|
value={1}
|
|
label=<span style={{color: theme.palette.text.secondary, }}>
|
|
<img
|
|
src="/icons/docker.svg"
|
|
style={{ width: 20, height: 20, marginRight: 10,}}
|
|
/> Scale
|
|
</span>
|
|
/>
|
|
<Tab
|
|
value={2}
|
|
label=<span style={{color: theme.palette.text.secondary, }}>
|
|
<img
|
|
src="/icons/k8s.svg"
|
|
style={{ width: 20, height: 20, marginRight: 10 }}
|
|
/> k8s
|
|
</span>
|
|
/>
|
|
</Tabs>
|
|
<Typography variant="body1" color="textSecondary" style={{marginTop: 15, }}>
|
|
{installationTab === 2 ?
|
|
<Typography variant='body2' color="textSecondary">
|
|
Check our <a href="https://docs.docker.com/get-started/get-docker/" target="_blank" rel="noopener noreferrer" style={{textDecoration: "none", color: "#f85a3e",}}>Kubernetes documentation</a> for more information on how to run Shuffle on Kubernetes. The status of the node will change when connected.
|
|
</Typography>
|
|
:
|
|
<Typography variant='body2' color="textSecondary">
|
|
1. <a href="https://docs.docker.com/get-started/get-docker/" target="_blank" rel="noopener noreferrer" style={{textDecoration: "none", color: "#f85a3e",}}>Ensure Docker is installed</a> and the target server can reach '{globalUrl}'
|
|
</Typography>
|
|
}
|
|
|
|
</Typography>
|
|
<Typography variant="body1" color="textSecondary">
|
|
{installationTab === 2 ? null :
|
|
"2. Run this command on the server you want to run workflows or store Pipeline data on"}
|
|
</Typography>
|
|
|
|
{installationTab === 2 ? null :
|
|
<div
|
|
style={{
|
|
marginTop: 10,
|
|
padding: 15,
|
|
minWidth: "50%",
|
|
maxWidth: "100%",
|
|
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
|
|
overflowY: "auto",
|
|
// Have it inline
|
|
borderRadius: theme.palette?.borderRadius,
|
|
}}
|
|
>
|
|
<div style={{ display: "flex", position: "relative", }}>
|
|
<code
|
|
contentEditable="true"
|
|
id="orborus_command"
|
|
style={{
|
|
whiteSpace: "pre-wrap",
|
|
overflow: "auto",
|
|
marginRight: 30,
|
|
backgroundColor: themeMode === "dark" ? "#1e1e1e" : "#f5f5f5",
|
|
color: themeMode === "dark" ? "#f8f8f2" : "#333",
|
|
padding: "8px",
|
|
borderRadius: "4px",
|
|
fontFamily: "monospace",
|
|
fontSize: 18,
|
|
border: themeMode === "dark" ? "1px solid #555" : "1px solid #ddd",
|
|
}}
|
|
>
|
|
{getOrborusCommand(environment)}
|
|
</code>
|
|
|
|
<div
|
|
style={{
|
|
position: "absolute",
|
|
right: 0,
|
|
top: -10,
|
|
}}
|
|
>
|
|
<IconButton
|
|
onClick={() => {
|
|
navigator.clipboard.writeText(orborusCommandWrapper())
|
|
toast("Copied to clipboard")
|
|
}}
|
|
>
|
|
<FileCopyIcon />
|
|
</IconButton>
|
|
</div>
|
|
</div>
|
|
|
|
<Divider style={{marginTop: 25, marginBottom: 10, }}/>
|
|
<div style={{display: 'flex', alignItems: 'center', }}>
|
|
<Typography variant='body2' color="textSecondary">Configure HTTP Proxies:</Typography> <Checkbox
|
|
id="shuffle_skip_proxies"
|
|
onClick={() => {
|
|
if (commandController.proxies === undefined) {
|
|
commandController.proxies = true
|
|
} else {
|
|
commandController.proxies = !commandController.proxies
|
|
}
|
|
|
|
setCommandController(commandController)
|
|
setUpdate(Math.random())
|
|
}}
|
|
/>
|
|
</div>
|
|
<div />
|
|
<div style={{display: 'flex', alignItems: 'center', }}>
|
|
<Typography variant='body2' color="textSecondary">Disable Pipelines & Data Lake:</Typography> <Checkbox
|
|
id="shuffle_skip_pipelines"
|
|
onClick={() => {
|
|
if (commandController.pipelines === undefined) {
|
|
commandController.pipelines = true
|
|
} else {
|
|
commandController.pipelines = !commandController.pipelines
|
|
}
|
|
setCommandController(commandController)
|
|
setUpdate(Math.random())
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<Typography variant="body1" color="textSecondary" style={{marginTop: 15, }}>
|
|
{installationTab === 2 ? null :
|
|
<span>
|
|
3. Verify if the node is running. Try to refresh the page a little while after running the command.
|
|
</span>
|
|
}
|
|
</Typography>
|
|
</div>
|
|
</div>
|
|
</Grid>
|
|
</Grid>
|
|
</Collapse>
|
|
|
|
{showCPUAlert === false ? null : (
|
|
<ListItem
|
|
key={index + "_cpu"}
|
|
style={{ backgroundColor: bgColor }}
|
|
>
|
|
<div
|
|
style={{
|
|
border: "1px solid #f85a3e",
|
|
borderRadius: theme.palette?.borderRadius,
|
|
marginTop: 10,
|
|
marginBottom: 10,
|
|
padding: 15,
|
|
textAlign: "center",
|
|
height: 70,
|
|
backgroundColor: theme.palette.surfaceColor,
|
|
display: "flex",
|
|
}}
|
|
>
|
|
<div style={{ flex: 2, overflow: "hidden" }}>
|
|
<Typography variant="body1">
|
|
90% CPU the server(s) hosting the Shuffle App
|
|
Runner (Orborus) was found.
|
|
</Typography>
|
|
<Typography variant="body2" color="textSecondary">
|
|
Need help with High Availability and Scale?{" "}
|
|
<a
|
|
href="/docs/configuration#scale"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
style={{
|
|
textDecoration: "none",
|
|
color: "#f85a3e",
|
|
}}
|
|
>
|
|
Read documentation
|
|
</a>{" "}
|
|
and{" "}
|
|
<a
|
|
href="https://shuffler.io/contact"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
style={{
|
|
textDecoration: "none",
|
|
color: "#f85a3e",
|
|
}}
|
|
>
|
|
Get in touch
|
|
</a>
|
|
.
|
|
</Typography>
|
|
</div>
|
|
<div
|
|
style={{ flex: 1, display: "flex", marginLeft: 30 }}
|
|
>
|
|
<Button
|
|
style={{
|
|
borderRadius: 25,
|
|
width: 200,
|
|
height: 50,
|
|
marginTop: 8,
|
|
}}
|
|
variant="outlined"
|
|
color="secondary"
|
|
onClick={() => {
|
|
// dismiss -> get envs
|
|
changeRecommendation(
|
|
userdata.priorities[foundIndex],
|
|
"dismiss",
|
|
);
|
|
}}
|
|
>
|
|
Dismiss
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</ListItem>
|
|
)}
|
|
</>
|
|
);
|
|
})
|
|
) }
|
|
</List>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{showLocationActionModal !== undefined && showLocationActionModal !== null && showLocationActionModal?.open === true ?
|
|
<div>
|
|
<LocationActionModal
|
|
showLocationActionModal={showLocationActionModal}
|
|
/>
|
|
</div>
|
|
|
|
: null }
|
|
</div>
|
|
|
|
|
|
)
|
|
});
|
|
|
|
export default EnvironmentTab;
|