[release] v0.12.0-unstable13
This commit is contained in:
parent
5c1b7e5d74
commit
af4ecbef41
|
@ -1,6 +1,7 @@
|
||||||
## Version 0.12.0
|
## Version 0.12.0
|
||||||
- New Dashboard
|
- New Dashboard
|
||||||
- New metrics gathering system
|
- New metrics gathering system
|
||||||
|
- Integrated a new docker-less mode of functioning for networking
|
||||||
- Added Button to force reset HTTPS cert in settings
|
- Added Button to force reset HTTPS cert in settings
|
||||||
|
|
||||||
## Version 0.11.3
|
## Version 0.11.3
|
||||||
|
|
|
@ -46,12 +46,19 @@ function toUTC(date, time) {
|
||||||
return formatDate(now, time);
|
return formatDate(now, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
const PlotComponent = ({ title, data, defaultSlot = 'latest' }) => {
|
const PlotComponent = ({ title, data, SimpleDesign, withSelector, defaultSlot = 'latest' }) => {
|
||||||
const [slot, setSlot] = useState(defaultSlot);
|
const [slot, setSlot] = useState(defaultSlot);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { primary, secondary } = theme.palette.text;
|
const { primary, secondary } = theme.palette.text;
|
||||||
const line = theme.palette.divider;
|
const line = theme.palette.divider;
|
||||||
const [series, setSeries] = useState([]);
|
const [series, setSeries] = useState([]);
|
||||||
|
const [selected, setSelected] = useState(withSelector ? data.findIndex((d) => d.Key === withSelector)
|
||||||
|
: 0);
|
||||||
|
|
||||||
|
const isDark = theme.palette.mode === 'dark';
|
||||||
|
|
||||||
|
const backColor = isDark ? '0,0,0' : '255,255,255';
|
||||||
|
const textColor = isDark ? 'white' : 'dark';
|
||||||
|
|
||||||
// chart options
|
// chart options
|
||||||
const areaChartOptions = {
|
const areaChartOptions = {
|
||||||
|
@ -99,8 +106,12 @@ const PlotComponent = ({ title, data, defaultSlot = 'latest' }) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let toProcess = data;
|
||||||
|
if(withSelector) {
|
||||||
|
toProcess = data.filter((d, id) => id === selected);
|
||||||
|
}
|
||||||
const dataSeries = [];
|
const dataSeries = [];
|
||||||
data.forEach((serie) => {
|
toProcess.forEach((serie) => {
|
||||||
dataSeries.push({
|
dataSeries.push({
|
||||||
name: serie.Label,
|
name: serie.Label,
|
||||||
dataAxis: xAxis.map((date) => {
|
dataAxis: xAxis.map((date) => {
|
||||||
|
@ -131,26 +142,27 @@ const PlotComponent = ({ title, data, defaultSlot = 'latest' }) => {
|
||||||
: xAxis,
|
: xAxis,
|
||||||
labels: {
|
labels: {
|
||||||
style: {
|
style: {
|
||||||
fontSize: slot === 'latest' ? '0' : '11px',
|
fontSize: (slot === 'latest' || SimpleDesign) ? '0' : '11px',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
axisBorder: {
|
axisBorder: {
|
||||||
show: true,
|
show: !SimpleDesign,
|
||||||
color: line
|
color: line
|
||||||
},
|
},
|
||||||
tickAmount: xAxis.length,
|
tickAmount: xAxis.length,
|
||||||
},
|
},
|
||||||
yaxis: data.map((thisdata, ida) => ({
|
yaxis: toProcess.map((thisdata, ida) => ({
|
||||||
opposite: ida === 1,
|
opposite: ida === 1,
|
||||||
labels: {
|
labels: {
|
||||||
style: {
|
style: {
|
||||||
colors: [secondary]
|
colors: [secondary],
|
||||||
|
fontSize: '11px',
|
||||||
},
|
},
|
||||||
|
|
||||||
formatter: FormaterForMetric(thisdata)
|
formatter: FormaterForMetric(thisdata)
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
text: thisdata.Label,
|
text: SimpleDesign ? '' : thisdata.Label,
|
||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
grid: {
|
grid: {
|
||||||
|
@ -167,16 +179,34 @@ const PlotComponent = ({ title, data, defaultSlot = 'latest' }) => {
|
||||||
data: serie.dataAxis
|
data: serie.dataAxis
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}, [slot, data]);
|
}, [slot, data, selected]);
|
||||||
|
|
||||||
|
|
||||||
return <Grid item xs={12} md={7} lg={8}>
|
return <Grid item xs={12} md={7} lg={8} >
|
||||||
<Grid container alignItems="center" justifyContent="space-between">
|
<Grid container alignItems="center" justifyContent="space-between">
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<Typography variant="h5">{title}</Typography>
|
<Typography variant="h5">{title}</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item >
|
||||||
<Stack direction="row" alignItems="center" spacing={0}>
|
<Stack direction="row" alignItems="center" spacing={0}>
|
||||||
|
|
||||||
|
{withSelector &&
|
||||||
|
<div style={{ marginRight: 15 }}>
|
||||||
|
<TextField
|
||||||
|
id="standard-select-currency"
|
||||||
|
size="small"
|
||||||
|
select
|
||||||
|
value={selected}
|
||||||
|
onChange={(e) => setSelected(e.target.value)}
|
||||||
|
sx={{ '& .MuiInputBase-input': { py: 0.5, fontSize: '0.875rem', } }}
|
||||||
|
>
|
||||||
|
{data.map((option, i) => (
|
||||||
|
<MenuItem key={i} value={i}>
|
||||||
|
{option.Label}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</TextField></div>}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => setSlot('latest')}
|
onClick={() => setSlot('latest')}
|
||||||
|
@ -204,7 +234,7 @@ const PlotComponent = ({ title, data, defaultSlot = 'latest' }) => {
|
||||||
</Stack>
|
</Stack>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<MainCard content={false} sx={{ mt: 1.5 }}>
|
<MainCard content={false} sx={{ mt: 1.5 }} >
|
||||||
<Box sx={{ pt: 1, pr: 2 }}>
|
<Box sx={{ pt: 1, pr: 2 }}>
|
||||||
<ReactApexChart options={options} series={series} type="area" height={450} />
|
<ReactApexChart options={options} series={series} type="area" height={450} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -98,7 +98,7 @@ function OrderTableHead({ order, orderBy, headCells }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const TableComponent = ({ title, data, defaultSlot = 'latest' }) => {
|
const TableComponent = ({ title, data, displayMax, render, defaultSlot = 'latest' }) => {
|
||||||
const [slot, setSlot] = useState(defaultSlot);
|
const [slot, setSlot] = useState(defaultSlot);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { primary, secondary } = theme.palette.text;
|
const { primary, secondary } = theme.palette.text;
|
||||||
|
@ -121,15 +121,17 @@ const TableComponent = ({ title, data, defaultSlot = 'latest' }) => {
|
||||||
|
|
||||||
heads[cat] = true;
|
heads[cat] = true;
|
||||||
|
|
||||||
let formatter = FormaterForMetric(item);
|
let formatter = FormaterForMetric(item, displayMax);
|
||||||
|
|
||||||
if(!fnrows.find((row) => row.name === name)) {
|
if(!fnrows.find((row) => row.name === name)) {
|
||||||
fnrows.push({
|
fnrows.push({
|
||||||
name,
|
name,
|
||||||
[cat]: formatter(v.Value)
|
[cat]: render ? render(item, v.Value, formatter(v.Value)) : formatter(v.Value),
|
||||||
|
["__" + cat]: v.Value
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
fnrows.find((row) => row.name === name)[cat] = formatter(v.Value);
|
fnrows.find((row) => row.name === name)[cat] = render ? render(item, v.Value, formatter(v.Value)) : formatter(v.Value)
|
||||||
|
fnrows.find((row) => row.name === name)["__" + cat] = v.Value
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -137,7 +139,8 @@ const TableComponent = ({ title, data, defaultSlot = 'latest' }) => {
|
||||||
fnrows = fnrows.filter((row) => {
|
fnrows = fnrows.filter((row) => {
|
||||||
let flag = false;
|
let flag = false;
|
||||||
Object.keys(row).forEach((key) => {
|
Object.keys(row).forEach((key) => {
|
||||||
if(key !== 'name' && row[key] != 0) {
|
if(key !== 'name' && row["__" + key]) {
|
||||||
|
console.log(key, row["__" + key])
|
||||||
flag = true;
|
flag = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -212,7 +215,8 @@ const TableComponent = ({ title, data, defaultSlot = 'latest' }) => {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
display: 'block',
|
display: 'block',
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
'& td, & th': { whiteSpace: 'nowrap' }
|
'& td, & th': { whiteSpace: 'nowrap' },
|
||||||
|
maxHeight: '474px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Table
|
<Table
|
||||||
|
@ -227,7 +231,7 @@ const TableComponent = ({ title, data, defaultSlot = 'latest' }) => {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<OrderTableHead headCells={headCells} order={order} orderBy={orderBy} />
|
<OrderTableHead headCells={headCells} order={order} orderBy={orderBy} />
|
||||||
<TableBody>
|
<TableBody style={{height:'409px', overflow: 'auto'}}>
|
||||||
{stableSort(rows, getComparator(order, orderBy)).map((row, index) => {
|
{stableSort(rows, getComparator(order, orderBy)).map((row, index) => {
|
||||||
const isItemSelected = false // isSelected(row.trackingNo);
|
const isItemSelected = false // isSelected(row.trackingNo);
|
||||||
const labelId = `enhanced-table-checkbox-${index}`;
|
const labelId = `enhanced-table-checkbox-${index}`;
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
export const FormaterForMetric = (metric) => {
|
export const simplifyNumber = (num) => {
|
||||||
console.log(metric)
|
|
||||||
|
|
||||||
return (num) => {
|
|
||||||
if(!num) return 0;
|
if(!num) return 0;
|
||||||
|
|
||||||
if(metric.Scale)
|
|
||||||
num /= metric.Scale;
|
|
||||||
|
|
||||||
if (Math.abs(num) >= 1e12) {
|
if (Math.abs(num) >= 1e12) {
|
||||||
return (num / 1e12).toFixed(1) + 'T'; // Convert to Millions
|
return (num / 1e12).toFixed(1) + 'T'; // Convert to Millions
|
||||||
} else if (Math.abs(num) >= 1e9) {
|
} else if (Math.abs(num) >= 1e9) {
|
||||||
|
@ -18,5 +12,23 @@ export const FormaterForMetric = (metric) => {
|
||||||
} else {
|
} else {
|
||||||
return num.toString();
|
return num.toString();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FormaterForMetric = (metric, displayMax) => {
|
||||||
|
console.log(metric)
|
||||||
|
|
||||||
|
return (num) => {
|
||||||
|
if(!num) return 0;
|
||||||
|
|
||||||
|
if(metric.Scale)
|
||||||
|
num /= metric.Scale;
|
||||||
|
|
||||||
|
num = simplifyNumber(num);
|
||||||
|
|
||||||
|
if(displayMax && metric.Max) {
|
||||||
|
num += ` / ${simplifyNumber(metric.Max)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return num;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,7 +16,8 @@ import {
|
||||||
Stack,
|
Stack,
|
||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
Alert
|
Alert,
|
||||||
|
LinearProgress
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
|
||||||
// project import
|
// project import
|
||||||
|
@ -40,6 +41,7 @@ import * as API from '../../api';
|
||||||
import AnimateButton from '../../components/@extended/AnimateButton';
|
import AnimateButton from '../../components/@extended/AnimateButton';
|
||||||
import PlotComponent from './components/plot';
|
import PlotComponent from './components/plot';
|
||||||
import TableComponent from './components/table';
|
import TableComponent from './components/table';
|
||||||
|
import { HomeBackground, TransparentHeader } from '../home';
|
||||||
|
|
||||||
// avatar style
|
// avatar style
|
||||||
const avatarSX = {
|
const avatarSX = {
|
||||||
|
@ -108,54 +110,12 @@ const DashboardDefault = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{/* <HomeBackground status={coStatus} />
|
||||||
|
<TransparentHeader /> */}
|
||||||
<IsLoggedIn />
|
<IsLoggedIn />
|
||||||
<div>
|
{metrics && <div style={{marginTop: '30px', zIndex:2, position: 'relative'}}>
|
||||||
<Stack spacing={1}>
|
<Grid container rowSpacing={4.5} columnSpacing={2.75} >
|
||||||
{coStatus && !coStatus.database && (
|
{/*
|
||||||
<Alert severity="error">
|
|
||||||
No Database is setup for Cosmos! User Management and Authentication will not work.<br />
|
|
||||||
You can either setup the database, or disable user management in the configuration panel.<br />
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{coStatus && coStatus.letsencrypt && (
|
|
||||||
<Alert severity="error">
|
|
||||||
You have enabled Let's Encrypt for automatic HTTPS Certificate. You need to provide the configuration with an email address to use for Let's Encrypt in the configs.
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{coStatus && coStatus.newVersionAvailable && (
|
|
||||||
<Alert severity="warning">
|
|
||||||
A new version of Cosmos is available! Please update to the latest version to get the latest features and bug fixes.
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{coStatus && coStatus.needsRestart && (
|
|
||||||
<Alert severity="warning">
|
|
||||||
You have made changes to the configuration that require a restart to take effect. Please restart Cosmos to apply the changes.
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{coStatus && coStatus.domain && (
|
|
||||||
<Alert severity="error">
|
|
||||||
You are using localhost or 0.0.0.0 as a hostname in the configuration. It is recommended that you use a domain name instead.
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{coStatus && !coStatus.docker && (
|
|
||||||
<Alert severity="error">
|
|
||||||
Docker is not connected! Please check your docker connection.<br/>
|
|
||||||
Did you forget to add <pre>-v /var/run/docker.sock:/var/run/docker.sock</pre> to your docker run command?<br />
|
|
||||||
if your docker daemon is running somewhere else, please add <pre>-e DOCKER_HOST=...</pre> to your docker run command.
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Alert severity="info">Dashboard implementation currently in progress! If you want to voice your opinion on where Cosmos is going, please join us on Discord!</Alert>
|
|
||||||
</Stack>
|
|
||||||
</div>
|
|
||||||
{metrics && <div style={{marginTop: '30px'}}>
|
|
||||||
<Grid container rowSpacing={4.5} columnSpacing={2.75}>
|
|
||||||
{/* row 1 */}
|
|
||||||
<Grid item xs={12} sx={{ mb: -2.25 }}>
|
<Grid item xs={12} sx={{ mb: -2.25 }}>
|
||||||
<Typography variant="h5">Dashboard</Typography>
|
<Typography variant="h5">Dashboard</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -173,8 +133,8 @@ const DashboardDefault = () => {
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item md={8} sx={{ display: { sm: 'none', md: 'block', lg: 'none' } }} />
|
<Grid item md={8} sx={{ display: { sm: 'none', md: 'block', lg: 'none' } }} />
|
||||||
|
*/}
|
||||||
|
|
||||||
{/* row 2 */}
|
|
||||||
|
|
||||||
<PlotComponent title={'Resources'} data={[metrics["cosmos.system.cpu.0"], metrics["cosmos.system.ram"]]}/>
|
<PlotComponent title={'Resources'} data={[metrics["cosmos.system.cpu.0"], metrics["cosmos.system.ram"]]}/>
|
||||||
|
|
||||||
|
@ -188,7 +148,25 @@ const DashboardDefault = () => {
|
||||||
Object.keys(metrics).filter((key) => key.startsWith("cosmos.system.docker.net")).map((key) => metrics[key])
|
Object.keys(metrics).filter((key) => key.startsWith("cosmos.system.docker.net")).map((key) => metrics[key])
|
||||||
}/>
|
}/>
|
||||||
|
|
||||||
{/* row 3 */}
|
<TableComponent title="Disk Usage" displayMax={true}
|
||||||
|
render={(metric, value, formattedValue) => {
|
||||||
|
return <span>
|
||||||
|
{formattedValue}
|
||||||
|
<LinearProgress variant="determinate" value={value / metric.Max * 100} />
|
||||||
|
</span>
|
||||||
|
}}
|
||||||
|
data={
|
||||||
|
Object.keys(metrics).filter((key) => key.startsWith("cosmos.system.disk")).map((key) => metrics[key])
|
||||||
|
}/>
|
||||||
|
|
||||||
|
<PlotComponent
|
||||||
|
title={'Temperature'}
|
||||||
|
withSelector={'cosmos.system.temp.all'}
|
||||||
|
SimpleDesign
|
||||||
|
data={Object.keys(metrics).filter((key) => key.startsWith("cosmos.system.temp")).map((key) => metrics[key])}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/*
|
||||||
<Grid item xs={12} md={7} lg={8}>
|
<Grid item xs={12} md={7} lg={8}>
|
||||||
<Grid container alignItems="center" justifyContent="space-between">
|
<Grid container alignItems="center" justifyContent="space-between">
|
||||||
<Grid item>
|
<Grid item>
|
||||||
|
@ -227,7 +205,6 @@ const DashboardDefault = () => {
|
||||||
</MainCard>
|
</MainCard>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* row 4 */}
|
|
||||||
<Grid item xs={12} md={7} lg={8}>
|
<Grid item xs={12} md={7} lg={8}>
|
||||||
<Grid container alignItems="center" justifyContent="space-between">
|
<Grid container alignItems="center" justifyContent="space-between">
|
||||||
<Grid item>
|
<Grid item>
|
||||||
|
@ -382,6 +359,7 @@ const DashboardDefault = () => {
|
||||||
</Stack>
|
</Stack>
|
||||||
</MainCard>
|
</MainCard>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
*/}
|
||||||
</Grid>
|
</Grid>
|
||||||
</div>}
|
</div>}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "cosmos-server",
|
"name": "cosmos-server",
|
||||||
"version": "0.12.0-unstable12",
|
"version": "0.12.0-unstable13",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "test-server.js",
|
"main": "test-server.js",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
|
|
|
@ -703,18 +703,23 @@ func StatsAll() ([]ContainerStats, error) {
|
||||||
cpuDelta := float64(stats.CPUStats.CPUUsage.TotalUsage - stats.PreCPUStats.CPUUsage.TotalUsage)
|
cpuDelta := float64(stats.CPUStats.CPUUsage.TotalUsage - stats.PreCPUStats.CPUUsage.TotalUsage)
|
||||||
systemDelta := float64(stats.CPUStats.SystemUsage - stats.PreCPUStats.SystemUsage)
|
systemDelta := float64(stats.CPUStats.SystemUsage - stats.PreCPUStats.SystemUsage)
|
||||||
|
|
||||||
utils.Debug("StatsAll - CPU CPUUsage TotalUsage " + strconv.FormatUint(stats.CPUStats.CPUUsage.TotalUsage, 10))
|
perCore := len(stats.CPUStats.CPUUsage.PercpuUsage)
|
||||||
utils.Debug("StatsAll - CPU PreCPUStats TotalUsage " + strconv.FormatUint(stats.PreCPUStats.CPUUsage.TotalUsage, 10))
|
if perCore == 0 {
|
||||||
utils.Debug("StatsAll - CPU CPUUsage PercpuUsage " + strconv.Itoa(len(stats.CPUStats.CPUUsage.PercpuUsage)))
|
perCore = 1
|
||||||
utils.Debug("StatsAll - CPU CPUUsage SystemUsage " + strconv.FormatUint(stats.CPUStats.SystemUsage, 10))
|
}
|
||||||
|
|
||||||
utils.Debug("StatsAll - CPU CPUUsage CPU Delta " + strconv.FormatFloat(cpuDelta, 'f', 6, 64))
|
// utils.Debug("StatsAll - CPU CPUUsage TotalUsage " + strconv.FormatUint(stats.CPUStats.CPUUsage.TotalUsage, 10))
|
||||||
utils.Debug("StatsAll - CPU CPUUsage System Delta " + strconv.FormatFloat(systemDelta, 'f', 6, 64))
|
// utils.Debug("StatsAll - CPU PreCPUStats TotalUsage " + strconv.FormatUint(stats.PreCPUStats.CPUUsage.TotalUsage, 10))
|
||||||
|
// utils.Debug("StatsAll - CPU CPUUsage PercpuUsage " + strconv.Itoa(perCore))
|
||||||
|
// utils.Debug("StatsAll - CPU CPUUsage SystemUsage " + strconv.FormatUint(stats.CPUStats.SystemUsage, 10))
|
||||||
|
|
||||||
|
// utils.Debug("StatsAll - CPU CPUUsage CPU Delta " + strconv.FormatFloat(cpuDelta, 'f', 6, 64))
|
||||||
|
// utils.Debug("StatsAll - CPU CPUUsage System Delta " + strconv.FormatFloat(systemDelta, 'f', 6, 64))
|
||||||
|
|
||||||
cpuUsage := 0.0
|
cpuUsage := 0.0
|
||||||
|
|
||||||
if systemDelta > 0 && cpuDelta > 0 {
|
if systemDelta > 0 && cpuDelta > 0 {
|
||||||
cpuUsage = (cpuDelta / systemDelta) * float64(len(stats.CPUStats.CPUUsage.PercpuUsage)) * 100
|
cpuUsage = (cpuDelta / systemDelta) * float64(perCore) * 100
|
||||||
|
|
||||||
utils.Debug("StatsAll - CPU CPUUsage " + strconv.FormatFloat(cpuUsage, 'f', 6, 64))
|
utils.Debug("StatsAll - CPU CPUUsage " + strconv.FormatFloat(cpuUsage, 'f', 6, 64))
|
||||||
} else {
|
} else {
|
||||||
|
|
94
src/docker/ip.go
Normal file
94
src/docker/ip.go
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/azukaar/cosmos-server/src/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cache struct {
|
||||||
|
data map[string]cacheItem
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type cacheItem struct {
|
||||||
|
ipAddress string
|
||||||
|
expires time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCache() *Cache {
|
||||||
|
return &Cache{
|
||||||
|
data: make(map[string]cacheItem),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Get(key string) (string, bool) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
item, found := c.data[key]
|
||||||
|
if !found || time.Now().After(item.expires) {
|
||||||
|
if found {
|
||||||
|
delete(c.data, key)
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return item.ipAddress, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Set(key string, value string, duration time.Duration) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
c.data[key] = cacheItem{
|
||||||
|
ipAddress: value,
|
||||||
|
expires: time.Now().Add(duration),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _getContainerIPByName(containerName string) (string, error) {
|
||||||
|
container, err := DockerClient.ContainerInspect(DockerContext, containerName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prioritize "host"
|
||||||
|
if net, ok := container.NetworkSettings.Networks["host"]; ok && net.IPAddress != "" {
|
||||||
|
return net.IPAddress, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, prioritize "bridge"
|
||||||
|
if net, ok := container.NetworkSettings.Networks["bridge"]; ok && net.IPAddress != "" {
|
||||||
|
return net.IPAddress, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, return the IP of the first network we find
|
||||||
|
for _, net := range container.NetworkSettings.Networks {
|
||||||
|
if net.IPAddress != "" {
|
||||||
|
return net.IPAddress, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("no IP address found for container %s", containerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cache = NewCache()
|
||||||
|
|
||||||
|
func GetContainerIPByName(containerName string) (ip string, err error) {
|
||||||
|
// Check cache first
|
||||||
|
ip, found := cache.Get(containerName)
|
||||||
|
if !found {
|
||||||
|
ip, err = _getContainerIPByName(containerName)
|
||||||
|
if err != nil {
|
||||||
|
utils.Error("Docker - Cannot get container IP", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.Set(containerName, ip, 10*time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Debug("Docker - Docker IP " + containerName + " : " + ip)
|
||||||
|
return ip, nil
|
||||||
|
}
|
10
src/index.go
10
src/index.go
|
@ -42,15 +42,25 @@ func main() {
|
||||||
utils.Log("Docker API version: " + version.APIVersion)
|
utils.Log("Docker API version: " + version.APIVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utils.Log("Starting monitoring services...")
|
||||||
|
|
||||||
metrics.Init()
|
metrics.Init()
|
||||||
|
|
||||||
|
utils.Log("Starting market services...")
|
||||||
|
|
||||||
market.Init()
|
market.Init()
|
||||||
|
|
||||||
|
utils.Log("Starting OpenID services...")
|
||||||
|
|
||||||
authorizationserver.Init()
|
authorizationserver.Init()
|
||||||
|
|
||||||
|
utils.Log("Starting constellation services...")
|
||||||
|
|
||||||
constellation.InitDNS()
|
constellation.InitDNS()
|
||||||
|
|
||||||
constellation.Init()
|
constellation.Init()
|
||||||
|
|
||||||
|
utils.Log("Starting server...")
|
||||||
|
|
||||||
StartServer()
|
StartServer()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/shirou/gopsutil/v3/net"
|
"github.com/shirou/gopsutil/v3/net"
|
||||||
"github.com/shirou/gopsutil/v3/disk"
|
"github.com/shirou/gopsutil/v3/disk"
|
||||||
"github.com/shirou/gopsutil/v3/common"
|
"github.com/shirou/gopsutil/v3/common"
|
||||||
|
"github.com/shirou/gopsutil/v3/host"
|
||||||
|
|
||||||
"github.com/azukaar/cosmos-server/src/utils"
|
"github.com/azukaar/cosmos-server/src/utils"
|
||||||
"github.com/azukaar/cosmos-server/src/docker"
|
"github.com/azukaar/cosmos-server/src/docker"
|
||||||
|
@ -82,10 +83,10 @@ func GetSystemMetrics() {
|
||||||
// Get Network Usage
|
// Get Network Usage
|
||||||
netIO, err := net.IOCountersWithContext(ctx, false)
|
netIO, err := net.IOCountersWithContext(ctx, false)
|
||||||
|
|
||||||
netIOTest, _ := net.IOCountersWithContext(ctx, true)
|
// netIOTest, _ := net.IOCountersWithContext(ctx, true)
|
||||||
for _, v := range netIOTest {
|
// for _, v := range netIOTest {
|
||||||
utils.Debug("Metrics - Network " + v.Name + " : " + strconv.Itoa(int(v.BytesRecv)) + " / " + strconv.Itoa(int(v.BytesSent)) + " / " + strconv.Itoa(int(v.Errin + v.Errout)) + " / " + strconv.Itoa(int(v.Dropin + v.Dropout)))
|
// utils.Debug("Metrics - Network " + v.Name + " : " + strconv.Itoa(int(v.BytesRecv)) + " / " + strconv.Itoa(int(v.BytesSent)) + " / " + strconv.Itoa(int(v.Errin + v.Errout)) + " / " + strconv.Itoa(int(v.Dropin + v.Dropout)))
|
||||||
}
|
// }
|
||||||
|
|
||||||
PushSetMetric("system.netRx", int(netIO[0].BytesRecv), DataDef{
|
PushSetMetric("system.netRx", int(netIO[0].BytesRecv), DataDef{
|
||||||
Max: 0,
|
Max: 0,
|
||||||
|
@ -159,6 +160,11 @@ func GetSystemMetrics() {
|
||||||
|
|
||||||
for _, part := range parts {
|
for _, part := range parts {
|
||||||
if strings.HasPrefix(part.Mountpoint, "/dev") || (strings.HasPrefix(part.Mountpoint, "/mnt") && !strings.HasPrefix(part.Mountpoint, "/mnt/host")) {
|
if strings.HasPrefix(part.Mountpoint, "/dev") || (strings.HasPrefix(part.Mountpoint, "/mnt") && !strings.HasPrefix(part.Mountpoint, "/mnt/host")) {
|
||||||
|
// remove /dev/shm and /dev/pts
|
||||||
|
if part.Mountpoint == "/dev" || strings.HasPrefix(part.Mountpoint, "/dev/shm") || strings.HasPrefix(part.Mountpoint, "/dev/pts") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
realMount := part.Mountpoint
|
realMount := part.Mountpoint
|
||||||
|
|
||||||
if os.Getenv("HOSTNAME") != "" {
|
if os.Getenv("HOSTNAME") != "" {
|
||||||
|
@ -177,4 +183,31 @@ func GetSystemMetrics() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Temperature
|
||||||
|
temps, err := host.SensorsTemperatures()
|
||||||
|
avgTemp := 0
|
||||||
|
avgTempCount := 0
|
||||||
|
|
||||||
|
for _, temp := range temps {
|
||||||
|
utils.Debug("Metrics - Temperature " + temp.SensorKey + " : " + strconv.Itoa(int(temp.Temperature)))
|
||||||
|
if temp.Temperature > 0 {
|
||||||
|
avgTemp += int(temp.Temperature)
|
||||||
|
avgTempCount++
|
||||||
|
|
||||||
|
PushSetMetric("system.temp." + temp.SensorKey, int(temp.Temperature), DataDef{
|
||||||
|
Max: 0,
|
||||||
|
Period: time.Second * 30,
|
||||||
|
Label: "Temperature " + temp.SensorKey,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if avgTempCount > 0 {
|
||||||
|
PushSetMetric("system.temp.all", avgTemp / avgTempCount, DataDef{
|
||||||
|
Max: 0,
|
||||||
|
Period: time.Second * 30,
|
||||||
|
Label: "Temperature",
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -6,8 +6,13 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"os"
|
||||||
|
"io/ioutil"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
spa "github.com/roberthodgen/spa-server"
|
spa "github.com/roberthodgen/spa-server"
|
||||||
"github.com/azukaar/cosmos-server/src/utils"
|
"github.com/azukaar/cosmos-server/src/utils"
|
||||||
|
"github.com/azukaar/cosmos-server/src/docker"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,6 +68,20 @@ func NewProxy(targetHost string, AcceptInsecureHTTPSTarget bool, VerboseForwardH
|
||||||
urlQuery := url.RawQuery
|
urlQuery := url.RawQuery
|
||||||
req.URL.Scheme = url.Scheme
|
req.URL.Scheme = url.Scheme
|
||||||
req.URL.Host = url.Host
|
req.URL.Host = url.Host
|
||||||
|
|
||||||
|
if route.Mode == "SERVAPP" && os.Getenv("HOSTNAME") == "" {
|
||||||
|
targetHost := url.Hostname()
|
||||||
|
|
||||||
|
targetIP, err := docker.GetContainerIPByName(targetHost)
|
||||||
|
if err != nil {
|
||||||
|
utils.Error("Create Route", err)
|
||||||
|
}
|
||||||
|
utils.Debug("Dockerless Target IP: " + targetIP)
|
||||||
|
req.URL.Host = targetIP + ":" + url.Port()
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Debug("Request to backend: " + req.URL.String())
|
||||||
|
|
||||||
req.URL.Path, req.URL.RawPath = joinURLPath(url, req.URL)
|
req.URL.Path, req.URL.RawPath = joinURLPath(url, req.URL)
|
||||||
if urlQuery == "" || req.URL.RawQuery == "" {
|
if urlQuery == "" || req.URL.RawQuery == "" {
|
||||||
req.URL.RawQuery = urlQuery + req.URL.RawQuery
|
req.URL.RawQuery = urlQuery + req.URL.RawQuery
|
||||||
|
@ -105,8 +124,8 @@ func NewProxy(targetHost string, AcceptInsecureHTTPSTarget bool, VerboseForwardH
|
||||||
}
|
}
|
||||||
|
|
||||||
// hide hostname (dangerous)
|
// hide hostname (dangerous)
|
||||||
// req.Header.Set("Host", url.Host)
|
// req.Header.Set("Host", req.URL.Host)
|
||||||
// req.Host = url.Host
|
// req.Host = req.URL.Host
|
||||||
|
|
||||||
req.Header.Set("Host", hostDest)
|
req.Header.Set("Host", hostDest)
|
||||||
req.Host = hostDest
|
req.Host = hostDest
|
||||||
|
@ -143,6 +162,14 @@ func NewProxy(targetHost string, AcceptInsecureHTTPSTarget bool, VerboseForwardH
|
||||||
resp.Header.Del("X-XSS-Protection")
|
resp.Header.Del("X-XSS-Protection")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if 502
|
||||||
|
if resp.StatusCode == 502 {
|
||||||
|
// set body
|
||||||
|
rb := "502 Bad Gateway. This means your container / backend is not reachable by Cosmos."
|
||||||
|
resp.Body = ioutil.NopCloser(strings.NewReader(rb))
|
||||||
|
resp.Header.Set("Content-Length", strconv.Itoa(len(rb)))
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +183,7 @@ func RouteTo(route utils.ProxyRouteConfig) http.Handler {
|
||||||
destination := route.Target
|
destination := route.Target
|
||||||
routeType := route.Mode
|
routeType := route.Mode
|
||||||
|
|
||||||
if(routeType == "SERVAPP" || routeType == "PROXY") {
|
if(routeType == "SERVAPP" || routeType == "PROXY") {
|
||||||
proxy, err := NewProxy(destination, route.AcceptInsecureHTTPSTarget, route.VerboseForwardHeader, route.DisableHeaderHardening, route.CORSOrigin, route)
|
proxy, err := NewProxy(destination, route.AcceptInsecureHTTPSTarget, route.VerboseForwardHeader, route.DisableHeaderHardening, route.CORSOrigin, route)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Error("Create Route", err)
|
utils.Error("Create Route", err)
|
||||||
|
|
|
@ -37,7 +37,7 @@ var ReBootstrapContainer func(string) error
|
||||||
|
|
||||||
var LetsEncryptErrors = []string{}
|
var LetsEncryptErrors = []string{}
|
||||||
|
|
||||||
var CONFIGFOLDER = "/config/"
|
var CONFIGFOLDER = "/var/lib/cosmos/"
|
||||||
|
|
||||||
var DefaultConfig = Config{
|
var DefaultConfig = Config{
|
||||||
LoggingLevel: "INFO",
|
LoggingLevel: "INFO",
|
||||||
|
@ -245,6 +245,7 @@ func GetConfigFileName() string {
|
||||||
configFile := os.Getenv("CONFIG_FILE")
|
configFile := os.Getenv("CONFIG_FILE")
|
||||||
|
|
||||||
if configFile == "" {
|
if configFile == "" {
|
||||||
|
CONFIGFOLDER = os.Getenv("COSMOS_CONFIG_FOLDER")
|
||||||
configFile = CONFIGFOLDER + "cosmos.config.json"
|
configFile = CONFIGFOLDER + "cosmos.config.json"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue