mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
Refactor/status info (#3066)
* refactor status info * wip fe * frontend changes and version bump * fix tests and motd * add registry workflow * better starttunnel instructions * placeholders for starttunnel tables --------- Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
@@ -26,7 +26,7 @@ const allowedStatuses = {
|
||||
'restoring',
|
||||
'stopping',
|
||||
'starting',
|
||||
'backingUp',
|
||||
'backing-up',
|
||||
]),
|
||||
}
|
||||
|
||||
@@ -45,9 +45,7 @@ export class ActionService {
|
||||
const { pkgInfo, actionInfo } = data
|
||||
|
||||
if (
|
||||
allowedStatuses[actionInfo.metadata.allowedStatuses].has(
|
||||
pkgInfo.mainStatus,
|
||||
)
|
||||
allowedStatuses[actionInfo.metadata.allowedStatuses].has(pkgInfo.status)
|
||||
) {
|
||||
if (actionInfo.metadata.hasInput) {
|
||||
this.formDialog.open<PackageActionData>(ActionInputModal, {
|
||||
|
||||
@@ -1921,8 +1921,9 @@ export namespace Mock {
|
||||
s9pk: '/media/startos/data/package-data/archive/installed/asdfasdf.s9pk',
|
||||
icon: '/assets/img/service-icons/bitcoin-core.svg',
|
||||
lastBackup: null,
|
||||
status: {
|
||||
main: 'running',
|
||||
statusInfo: {
|
||||
error: null,
|
||||
desired: { main: 'running' },
|
||||
started: new Date().toISOString(),
|
||||
health: {},
|
||||
},
|
||||
@@ -2201,8 +2202,11 @@ export namespace Mock {
|
||||
s9pk: '/media/startos/data/package-data/archive/installed/asdfasdf.s9pk',
|
||||
icon: '/assets/img/service-icons/btc-rpc-proxy.png',
|
||||
lastBackup: null,
|
||||
status: {
|
||||
main: 'stopped',
|
||||
statusInfo: {
|
||||
desired: { main: 'stopped' },
|
||||
started: null,
|
||||
health: {},
|
||||
error: null,
|
||||
},
|
||||
actions: {},
|
||||
serviceInterfaces: {
|
||||
@@ -2246,8 +2250,11 @@ export namespace Mock {
|
||||
s9pk: '/media/startos/data/package-data/archive/installed/asdfasdf.s9pk',
|
||||
icon: '/assets/img/service-icons/lnd.png',
|
||||
lastBackup: null,
|
||||
status: {
|
||||
main: 'stopped',
|
||||
statusInfo: {
|
||||
desired: { main: 'stopped' },
|
||||
error: null,
|
||||
health: {},
|
||||
started: null,
|
||||
},
|
||||
actions: {
|
||||
config: {
|
||||
|
||||
@@ -762,12 +762,12 @@ export class MockApiService extends ApiService {
|
||||
setTimeout(async () => {
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
const id = ids[i]
|
||||
const appPath = `/packageData/${id}/status/main/`
|
||||
const appPatch: ReplaceOperation<T.MainStatus['main']>[] = [
|
||||
const appPath = `/packageData/${id}/statusInfo/desired/main`
|
||||
const appPatch: ReplaceOperation<T.DesiredStatus['main']>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: appPath,
|
||||
value: 'backingUp',
|
||||
value: 'backing-up',
|
||||
},
|
||||
]
|
||||
this.mockRevision(appPatch)
|
||||
@@ -1073,17 +1073,18 @@ export class MockApiService extends ApiService {
|
||||
}
|
||||
|
||||
async startPackage(params: RR.StartPackageReq): Promise<RR.StartPackageRes> {
|
||||
const path = `/packageData/${params.id}/status`
|
||||
const path = `/packageData/${params.id}/statusInfo`
|
||||
|
||||
await pauseFor(2000)
|
||||
|
||||
setTimeout(async () => {
|
||||
const patch2: ReplaceOperation<T.MainStatus & { main: 'running' }>[] = [
|
||||
const patch2: ReplaceOperation<T.StatusInfo>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path,
|
||||
value: {
|
||||
main: 'running',
|
||||
error: null,
|
||||
desired: { main: 'running' },
|
||||
started: new Date().toISOString(),
|
||||
health: {
|
||||
'ephemeral-health-check': {
|
||||
@@ -1118,14 +1119,14 @@ export class MockApiService extends ApiService {
|
||||
this.mockRevision(patch2)
|
||||
}, 2000)
|
||||
|
||||
const originalPatch: ReplaceOperation<
|
||||
T.MainStatus & { main: 'starting' }
|
||||
>[] = [
|
||||
const originalPatch: ReplaceOperation<T.StatusInfo>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path,
|
||||
value: {
|
||||
main: 'starting',
|
||||
desired: { main: 'running' },
|
||||
started: null,
|
||||
error: null,
|
||||
health: {},
|
||||
},
|
||||
},
|
||||
@@ -1140,15 +1141,16 @@ export class MockApiService extends ApiService {
|
||||
params: RR.RestartPackageReq,
|
||||
): Promise<RR.RestartPackageRes> {
|
||||
await pauseFor(2000)
|
||||
const path = `/packageData/${params.id}/status`
|
||||
const path = `/packageData/${params.id}/statusInfo`
|
||||
|
||||
setTimeout(async () => {
|
||||
const patch2: ReplaceOperation<T.MainStatus & { main: 'running' }>[] = [
|
||||
const patch2: ReplaceOperation<T.StatusInfo>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path,
|
||||
value: {
|
||||
main: 'running',
|
||||
desired: { main: 'running' },
|
||||
error: null,
|
||||
started: new Date().toISOString(),
|
||||
health: {
|
||||
'ephemeral-health-check': {
|
||||
@@ -1183,12 +1185,15 @@ export class MockApiService extends ApiService {
|
||||
this.mockRevision(patch2)
|
||||
}, this.revertTime)
|
||||
|
||||
const patch: ReplaceOperation<T.MainStatus & { main: 'restarting' }>[] = [
|
||||
const patch: ReplaceOperation<T.StatusInfo>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path,
|
||||
value: {
|
||||
main: 'restarting',
|
||||
desired: { main: 'restarting' },
|
||||
started: null,
|
||||
error: null,
|
||||
health: {},
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -1200,24 +1205,34 @@ export class MockApiService extends ApiService {
|
||||
|
||||
async stopPackage(params: RR.StopPackageReq): Promise<RR.StopPackageRes> {
|
||||
await pauseFor(2000)
|
||||
const path = `/packageData/${params.id}/status`
|
||||
const path = `/packageData/${params.id}/statusInfo`
|
||||
|
||||
setTimeout(() => {
|
||||
const patch2: ReplaceOperation<T.MainStatus & { main: 'stopped' }>[] = [
|
||||
const patch2: ReplaceOperation<T.StatusInfo>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: path,
|
||||
value: { main: 'stopped' },
|
||||
value: {
|
||||
desired: { main: 'stopped' },
|
||||
error: null,
|
||||
health: {},
|
||||
started: null,
|
||||
},
|
||||
},
|
||||
]
|
||||
this.mockRevision(patch2)
|
||||
}, this.revertTime)
|
||||
|
||||
const patch: ReplaceOperation<T.MainStatus & { main: 'stopping' }>[] = [
|
||||
const patch: ReplaceOperation<T.StatusInfo>[] = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: path,
|
||||
value: { main: 'stopping' },
|
||||
value: {
|
||||
desired: { main: 'stopped' },
|
||||
error: null,
|
||||
health: {},
|
||||
started: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@@ -232,8 +232,11 @@ export const mockPatchData: DataModel = {
|
||||
s9pk: '/media/startos/data/package-data/archive/installed/asdfasdf.s9pk',
|
||||
icon: '/assets/img/service-icons/bitcoin-core.svg',
|
||||
lastBackup: new Date(new Date().valueOf() - 604800001).toISOString(),
|
||||
status: {
|
||||
main: 'stopped',
|
||||
statusInfo: {
|
||||
desired: { main: 'stopped' },
|
||||
error: null,
|
||||
health: {},
|
||||
started: null,
|
||||
},
|
||||
// status: {
|
||||
// main: 'error',
|
||||
@@ -518,8 +521,11 @@ export const mockPatchData: DataModel = {
|
||||
s9pk: '/media/startos/data/package-data/archive/installed/asdfasdf.s9pk',
|
||||
icon: '/assets/img/service-icons/lnd.png',
|
||||
lastBackup: null,
|
||||
status: {
|
||||
main: 'stopped',
|
||||
statusInfo: {
|
||||
desired: { main: 'stopped' },
|
||||
error: null,
|
||||
health: {},
|
||||
started: null,
|
||||
},
|
||||
actions: {
|
||||
config: {
|
||||
|
||||
@@ -11,6 +11,7 @@ import deepEqual from 'fast-deep-equal'
|
||||
import { Observable } from 'rxjs'
|
||||
import { isInstalled } from 'src/app/utils/get-package-data'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { getInstalledBaseStatus } from './pkg-status-rendering.service'
|
||||
|
||||
export type AllDependencyErrors = Record<string, PkgDependencyErrors>
|
||||
export type PkgDependencyErrors = Record<string, DependencyError | null>
|
||||
@@ -153,7 +154,7 @@ export class DepErrorService {
|
||||
}
|
||||
}
|
||||
|
||||
const depStatus = dep.status.main
|
||||
const depStatus = getInstalledBaseStatus(dep.statusInfo)
|
||||
|
||||
// not running
|
||||
if (depStatus !== 'running' && depStatus !== 'starting') {
|
||||
@@ -165,7 +166,7 @@ export class DepErrorService {
|
||||
// health check failure
|
||||
if (depStatus === 'running' && currentDep?.kind === 'running') {
|
||||
for (let id of currentDep.healthChecks) {
|
||||
const check = dep.status.health[id]
|
||||
const check = dep.statusInfo.health[id]
|
||||
if (check && check?.result !== 'success') {
|
||||
return {
|
||||
type: 'healthChecksFailed',
|
||||
|
||||
@@ -13,7 +13,7 @@ export function renderPkgStatus(pkg: PackageDataEntry): PackageStatus {
|
||||
|
||||
if (pkg.stateInfo.state === 'installed') {
|
||||
primary = getInstalledPrimaryStatus(pkg)
|
||||
health = getHealthStatus(pkg.status)
|
||||
health = getHealthStatus(pkg.statusInfo)
|
||||
} else {
|
||||
primary = pkg.stateInfo.state
|
||||
}
|
||||
@@ -21,33 +21,43 @@ export function renderPkgStatus(pkg: PackageDataEntry): PackageStatus {
|
||||
return { primary, health }
|
||||
}
|
||||
|
||||
export function getInstalledPrimaryStatus({
|
||||
tasks,
|
||||
status,
|
||||
}: T.PackageDataEntry): PrimaryStatus {
|
||||
export function getInstalledBaseStatus(statusInfo: T.StatusInfo): BaseStatus {
|
||||
if (
|
||||
Object.values(tasks).some(t => t.active && t.task.severity === 'critical')
|
||||
) {
|
||||
return 'taskRequired'
|
||||
}
|
||||
|
||||
if (
|
||||
Object.values(status.main === 'running' && status.health)
|
||||
.filter(h => !!h)
|
||||
.some(h => h.result === 'starting')
|
||||
statusInfo.desired.main === 'running' &&
|
||||
(!statusInfo.started ||
|
||||
Object.values(statusInfo.health)
|
||||
.filter(h => !!h)
|
||||
.some(h => h.result === 'starting'))
|
||||
) {
|
||||
return 'starting'
|
||||
}
|
||||
|
||||
return status.main
|
||||
if (statusInfo.desired.main === 'stopped' && statusInfo.started) {
|
||||
return 'stopping'
|
||||
}
|
||||
|
||||
return statusInfo.desired.main
|
||||
}
|
||||
|
||||
function getHealthStatus(status: T.MainStatus): T.HealthStatus | null {
|
||||
if (status.main !== 'running' || !status.main) {
|
||||
export function getInstalledPrimaryStatus({
|
||||
tasks,
|
||||
statusInfo,
|
||||
}: T.PackageDataEntry): PrimaryStatus {
|
||||
if (
|
||||
Object.values(tasks).some(t => t.active && t.task.severity === 'critical')
|
||||
) {
|
||||
return 'task-required'
|
||||
}
|
||||
|
||||
return getInstalledBaseStatus(statusInfo)
|
||||
}
|
||||
|
||||
function getHealthStatus(statusInfo: T.StatusInfo): T.HealthStatus | null {
|
||||
if (statusInfo.desired.main !== 'running') {
|
||||
return null
|
||||
}
|
||||
|
||||
const values = Object.values(status.health).filter(h => !!h)
|
||||
const values = Object.values(statusInfo.health).filter(h => !!h)
|
||||
|
||||
if (values.some(h => h.result === 'failure')) {
|
||||
return 'failure'
|
||||
@@ -70,7 +80,7 @@ export interface StatusRendering {
|
||||
showDots?: boolean
|
||||
}
|
||||
|
||||
export type PrimaryStatus =
|
||||
export type BaseStatus =
|
||||
| 'installing'
|
||||
| 'updating'
|
||||
| 'removing'
|
||||
@@ -80,10 +90,11 @@ export type PrimaryStatus =
|
||||
| 'stopping'
|
||||
| 'restarting'
|
||||
| 'stopped'
|
||||
| 'backingUp'
|
||||
| 'taskRequired'
|
||||
| 'backing-up'
|
||||
| 'error'
|
||||
|
||||
export type PrimaryStatus = BaseStatus | 'task-required'
|
||||
|
||||
export type DependencyStatus = 'warning' | 'satisfied'
|
||||
|
||||
export const PrimaryRendering: Record<PrimaryStatus, StatusRendering> = {
|
||||
@@ -122,7 +133,7 @@ export const PrimaryRendering: Record<PrimaryStatus, StatusRendering> = {
|
||||
color: 'dark-shade',
|
||||
showDots: false,
|
||||
},
|
||||
backingUp: {
|
||||
'backing-up': {
|
||||
display: 'Backing Up',
|
||||
color: 'primary',
|
||||
showDots: true,
|
||||
@@ -137,7 +148,7 @@ export const PrimaryRendering: Record<PrimaryStatus, StatusRendering> = {
|
||||
color: 'success',
|
||||
showDots: false,
|
||||
},
|
||||
taskRequired: {
|
||||
'task-required': {
|
||||
display: 'Task Required',
|
||||
color: 'warning',
|
||||
showDots: false,
|
||||
|
||||
Reference in New Issue
Block a user