mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
Feature/restart service (#1554)
* add restart button to service show page and restart rpc api * Feature/restart rpc (#1555) * add restart rpc and status * wire up rpc * add restarting bool Co-authored-by: Aiden McClelland <me@drbonez.dev> * check if service is restarting * filter package when restarting to avoid glitch Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
@@ -3,6 +3,7 @@ import { NavController } from '@ionic/angular'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
import {
|
||||
PackageDataEntry,
|
||||
PackageMainStatus,
|
||||
PackageState,
|
||||
} from 'src/app/services/patch-db/data-model'
|
||||
import {
|
||||
@@ -13,7 +14,7 @@ import {
|
||||
ConnectionFailure,
|
||||
ConnectionService,
|
||||
} from 'src/app/services/connection.service'
|
||||
import { map, startWith } from 'rxjs/operators'
|
||||
import { map, startWith, filter } from 'rxjs/operators'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { getPkgId } from '@start9labs/shared'
|
||||
|
||||
@@ -32,6 +33,13 @@ export class AppShowPage {
|
||||
private readonly pkgId = getPkgId(this.route)
|
||||
|
||||
readonly pkg$ = this.patch.watch$('package-data', this.pkgId).pipe(
|
||||
filter(
|
||||
(p: PackageDataEntry) =>
|
||||
!(
|
||||
p.installed?.status.main.status === PackageMainStatus.Starting &&
|
||||
p.installed?.status.main.restarting
|
||||
),
|
||||
),
|
||||
map(pkg => {
|
||||
// if package disappears, navigate to list page
|
||||
if (!pkg) {
|
||||
|
||||
@@ -21,6 +21,14 @@
|
||||
<ion-icon slot="start" name="stop-outline"></ion-icon>
|
||||
Stop
|
||||
</ion-button>
|
||||
<ion-button
|
||||
class="action-button"
|
||||
color="warning"
|
||||
(click)="tryRestart()"
|
||||
>
|
||||
<ion-icon slot="start" name="refresh"></ion-icon>
|
||||
Restart
|
||||
</ion-button>
|
||||
</ng-container>
|
||||
|
||||
<ion-button
|
||||
@@ -58,4 +66,4 @@
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
}
|
||||
|
||||
.action-button {
|
||||
margin: 20px 20px 10px 0;
|
||||
margin: 12px 20px 10px 0;
|
||||
min-height: 42px;
|
||||
min-width: 140px;
|
||||
}
|
||||
}
|
||||
@@ -137,8 +137,6 @@ export class AppShowStatusComponent {
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
async tryRestart(): Promise<void> {
|
||||
if (hasCurrentDeps(this.pkg)) {
|
||||
const alert = await this.alertCtrl.create({
|
||||
@@ -165,7 +163,28 @@ export class AppShowStatusComponent {
|
||||
}
|
||||
}
|
||||
|
||||
>>>>>>> 918a1907... Remove app wiz and dry calls (#1541)
|
||||
async presentAlertRestart(): Promise<void> {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Confirm',
|
||||
message: 'Are you sure you want to restart this service?',
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
role: 'cancel',
|
||||
},
|
||||
{
|
||||
text: 'Restart',
|
||||
handler: () => {
|
||||
this.restart()
|
||||
},
|
||||
cssClass: 'enter-click',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
await alert.present()
|
||||
}
|
||||
|
||||
private async start(): Promise<void> {
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: `Starting...`,
|
||||
@@ -181,8 +200,6 @@ export class AppShowStatusComponent {
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
private async stop(): Promise<void> {
|
||||
const loader = await this.loadingCtrl.create({
|
||||
message: 'Stopping...',
|
||||
@@ -212,8 +229,6 @@ export class AppShowStatusComponent {
|
||||
loader.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
>>>>>>> 918a1907... Remove app wiz and dry calls (#1541)
|
||||
private async presentAlertStart(message: string): Promise<boolean> {
|
||||
return new Promise(async resolve => {
|
||||
const alert = await this.alertCtrl.create({
|
||||
|
||||
@@ -215,6 +215,9 @@ export module RR {
|
||||
export type StartPackageReq = WithExpire<{ id: string }> // package.start
|
||||
export type StartPackageRes = WithRevision<null>
|
||||
|
||||
export type RestartPackageReq = WithExpire<{ id: string }> // package.restart
|
||||
export type RestartPackageRes = WithRevision<null>
|
||||
|
||||
export type StopPackageReq = WithExpire<{ id: string }> // package.stop
|
||||
export type StopPackageRes = WithRevision<null>
|
||||
|
||||
|
||||
@@ -233,6 +233,12 @@ export abstract class ApiService implements Source<DataModel>, Http<DataModel> {
|
||||
startPackage = (params: RR.StartPackageReq) =>
|
||||
this.syncResponse(() => this.startPackageRaw(params))()
|
||||
|
||||
protected abstract restartPackageRaw(
|
||||
params: RR.RestartPackageReq,
|
||||
): Promise<RR.RestartPackageRes>
|
||||
restartPackage = (params: RR.RestartPackageReq) =>
|
||||
this.syncResponse(() => this.restartPackageRaw(params))()
|
||||
|
||||
protected abstract stopPackageRaw(
|
||||
params: RR.StopPackageReq,
|
||||
): Promise<RR.StopPackageRes>
|
||||
|
||||
@@ -306,6 +306,12 @@ export class LiveApiService extends ApiService {
|
||||
return this.http.rpcRequest({ method: 'package.start', params })
|
||||
}
|
||||
|
||||
async restartPackageRaw(
|
||||
params: RR.RestartPackageReq,
|
||||
): Promise<RR.RestartPackageRes> {
|
||||
return this.http.rpcRequest({ method: 'package.restart', params })
|
||||
}
|
||||
|
||||
async stopPackageRaw(params: RR.StopPackageReq): Promise<RR.StopPackageRes> {
|
||||
return this.http.rpcRequest({ method: 'package.stop', params })
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export class MockApiService extends ApiService {
|
||||
value: mockPatchData,
|
||||
expireId: null,
|
||||
})
|
||||
private readonly revertTime = 4000
|
||||
private readonly revertTime = 2000
|
||||
sequence: number
|
||||
|
||||
constructor(private readonly bootstrapper: LocalStorageBootstrap) {
|
||||
@@ -654,6 +654,63 @@ export class MockApiService extends ApiService {
|
||||
return this.withRevision(originalPatch)
|
||||
}
|
||||
|
||||
async restartPackageRaw(
|
||||
params: RR.RestartPackageReq,
|
||||
): Promise<RR.RestartPackageRes> {
|
||||
// first enact stop
|
||||
await pauseFor(2000)
|
||||
const path = `/package-data/${params.id}/installed/status/main`
|
||||
|
||||
setTimeout(() => {
|
||||
const patch2 = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: path + '/status',
|
||||
value: PackageMainStatus.Running,
|
||||
},
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: path + '/health',
|
||||
value: {
|
||||
'ephemeral-health-check': {
|
||||
result: 'starting',
|
||||
},
|
||||
'unnecessary-health-check': {
|
||||
result: 'disabled',
|
||||
},
|
||||
'chain-state': {
|
||||
result: 'loading',
|
||||
message: 'Bitcoin is syncing from genesis',
|
||||
},
|
||||
'p2p-interface': {
|
||||
result: 'success',
|
||||
},
|
||||
'rpc-interface': {
|
||||
result: 'failure',
|
||||
error: 'RPC interface unreachable.',
|
||||
},
|
||||
},
|
||||
} as any,
|
||||
]
|
||||
this.updateMock(patch2)
|
||||
}, this.revertTime)
|
||||
|
||||
const patch = [
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: path + '/status',
|
||||
value: PackageMainStatus.Restarting,
|
||||
},
|
||||
{
|
||||
op: PatchOp.REPLACE,
|
||||
path: path + '/health',
|
||||
value: {},
|
||||
},
|
||||
]
|
||||
|
||||
return this.withRevision(patch)
|
||||
}
|
||||
|
||||
async stopPackageRaw(params: RR.StopPackageReq): Promise<RR.StopPackageRes> {
|
||||
await pauseFor(2000)
|
||||
const path = `/package-data/${params.id}/installed/status/main`
|
||||
|
||||
@@ -250,6 +250,7 @@ export type MainStatus =
|
||||
| MainStatusStarting
|
||||
| MainStatusRunning
|
||||
| MainStatusBackingUp
|
||||
| MainStatusRestarting
|
||||
|
||||
export interface MainStatusStopped {
|
||||
status: PackageMainStatus.Stopped
|
||||
@@ -261,6 +262,7 @@ export interface MainStatusStopping {
|
||||
|
||||
export interface MainStatusStarting {
|
||||
status: PackageMainStatus.Starting
|
||||
restarting: boolean
|
||||
}
|
||||
|
||||
export interface MainStatusRunning {
|
||||
@@ -274,12 +276,17 @@ export interface MainStatusBackingUp {
|
||||
started: string | null // UTC date string
|
||||
}
|
||||
|
||||
export interface MainStatusRestarting {
|
||||
status: PackageMainStatus.Restarting
|
||||
}
|
||||
|
||||
export enum PackageMainStatus {
|
||||
Starting = 'starting',
|
||||
Running = 'running',
|
||||
Stopping = 'stopping',
|
||||
Stopped = 'stopped',
|
||||
BackingUp = 'backing-up',
|
||||
Restarting = 'restarting',
|
||||
}
|
||||
|
||||
export type HealthCheckResult =
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { isEmptyObject } from '@start9labs/shared'
|
||||
import {
|
||||
MainStatusStarting,
|
||||
PackageDataEntry,
|
||||
PackageMainStatus,
|
||||
PackageState,
|
||||
@@ -32,6 +33,8 @@ export function renderPkgStatus(pkg: PackageDataEntry): PackageStatus {
|
||||
function getPrimaryStatus(status: Status): PrimaryStatus {
|
||||
if (!status.configured) {
|
||||
return PrimaryStatus.NeedsConfig
|
||||
} else if ((status.main as MainStatusStarting).restarting) {
|
||||
return PrimaryStatus.Restarting
|
||||
} else {
|
||||
return status.main.status as any as PrimaryStatus
|
||||
}
|
||||
@@ -57,7 +60,6 @@ function getHealthStatus(
|
||||
}
|
||||
|
||||
const values = Object.values(status.main.health)
|
||||
console.log('HEALTH CHECKS', values)
|
||||
|
||||
if (values.some(h => h.result === 'failure')) {
|
||||
return HealthStatus.Failure
|
||||
@@ -94,6 +96,7 @@ export enum PrimaryStatus {
|
||||
Starting = 'starting',
|
||||
Running = 'running',
|
||||
Stopping = 'stopping',
|
||||
Restarting = 'restarting',
|
||||
Stopped = 'stopped',
|
||||
BackingUp = 'backing-up',
|
||||
// config
|
||||
@@ -139,6 +142,11 @@ export const PrimaryRendering: Record<string, StatusRendering> = {
|
||||
color: 'dark-shade',
|
||||
showDots: true,
|
||||
},
|
||||
[PrimaryStatus.Restarting]: {
|
||||
display: 'Restarting',
|
||||
color: 'warning',
|
||||
showDots: true,
|
||||
},
|
||||
[PrimaryStatus.Stopped]: {
|
||||
display: 'Stopped',
|
||||
color: 'dark-shade',
|
||||
|
||||
Reference in New Issue
Block a user