mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-03 09:49:20 +02:00
Add list-jobs runner command
This commit is contained in:
parent
1fc7e003e5
commit
b9a53addc9
12 changed files with 144 additions and 16 deletions
8
apps/peertube-runner/CHANGELOG.md
Normal file
8
apps/peertube-runner/CHANGELOG.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## v0.1.0
|
||||||
|
|
||||||
|
* Requires Node 20
|
||||||
|
* Introduce `list-jobs` command to list processing jobs
|
||||||
|
* Update dependencies
|
||||||
|
* Send last chunks/playlist content to correctly end the live
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import { Command, InvalidArgumentError } from '@commander-js/extra-typings'
|
import { Command, InvalidArgumentError } from '@commander-js/extra-typings'
|
||||||
import { RunnerJobType } from '@peertube/peertube-models'
|
import { RunnerJobType } from '@peertube/peertube-models'
|
||||||
import { listRegistered, registerRunner, unregisterRunner } from './register/index.js'
|
import { listJobs, listRegistered, registerRunner, unregisterRunner } from './register/index.js'
|
||||||
import { gracefulShutdown } from './register/shutdown.js'
|
import { gracefulShutdown } from './register/shutdown.js'
|
||||||
import { RunnerServer } from './server/index.js'
|
import { RunnerServer } from './server/index.js'
|
||||||
import { getSupportedJobsList } from './server/shared/supported-job.js'
|
import { getSupportedJobsList } from './server/shared/supported-job.js'
|
||||||
|
@ -99,6 +99,19 @@ program.command('list-registered')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
program.command('list-jobs')
|
||||||
|
.description('List processing jobs')
|
||||||
|
.option('--include-payload', 'Include job payload in the output')
|
||||||
|
.action(async options => {
|
||||||
|
try {
|
||||||
|
await listJobs({ includePayload: options.includePayload })
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Cannot list processing jobs.')
|
||||||
|
console.error(err)
|
||||||
|
process.exit(-1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
program.command('graceful-shutdown')
|
program.command('graceful-shutdown')
|
||||||
.description('Exit runner when all processing tasks are finished')
|
.description('Exit runner when all processing tasks are finished')
|
||||||
.action(async () => {
|
.action(async () => {
|
||||||
|
|
|
@ -34,3 +34,14 @@ export async function listRegistered () {
|
||||||
|
|
||||||
client.stop()
|
client.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function listJobs (options: {
|
||||||
|
includePayload: boolean
|
||||||
|
}) {
|
||||||
|
const client = new IPCClient()
|
||||||
|
await client.run()
|
||||||
|
|
||||||
|
await client.askListJobs(options)
|
||||||
|
|
||||||
|
client.stop()
|
||||||
|
}
|
||||||
|
|
|
@ -61,11 +61,13 @@ export function scheduleTranscodingProgress (options: {
|
||||||
: 60000
|
: 60000
|
||||||
|
|
||||||
const update = () => {
|
const update = () => {
|
||||||
|
job.progress = progressGetter() || 0
|
||||||
|
|
||||||
server.runnerJobs.update({
|
server.runnerJobs.update({
|
||||||
jobToken: job.jobToken,
|
jobToken: job.jobToken,
|
||||||
jobUUID: job.uuid,
|
jobUUID: job.uuid,
|
||||||
runnerToken,
|
runnerToken,
|
||||||
progress: progressGetter()
|
progress: job.progress
|
||||||
}).catch(err => logger.error({ err }, 'Cannot send job progress'))
|
}).catch(err => logger.error({ err }, 'Cannot send job progress'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -183,6 +183,19 @@ export class RunnerServer {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
listJobs () {
|
||||||
|
return {
|
||||||
|
concurrency: ConfigManager.Instance.getConfig().jobs.concurrency,
|
||||||
|
|
||||||
|
processingJobs: this.processingJobs.map(j => ({
|
||||||
|
serverUrl: j.server.url,
|
||||||
|
job: pick(j.job, [ 'type', 'startedAt', 'progress', 'payload' ])
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
requestGracefulShutdown () {
|
requestGracefulShutdown () {
|
||||||
logger.info('Received graceful shutdown request')
|
logger.info('Received graceful shutdown request')
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
import { Client as NetIPC } from '@peertube/net-ipc'
|
||||||
import CliTable3 from 'cli-table3'
|
import CliTable3 from 'cli-table3'
|
||||||
import { ensureDir } from 'fs-extra/esm'
|
import { ensureDir } from 'fs-extra/esm'
|
||||||
import { Client as NetIPC } from '@peertube/net-ipc'
|
|
||||||
import { ConfigManager } from '../config-manager.js'
|
import { ConfigManager } from '../config-manager.js'
|
||||||
import { IPCResponse, IPCResponseData, IPCRequest } from './shared/index.js'
|
import { IPCRequest, IPCResponse, IPCResponseListJobs, IPCResponseListRegistered } from './shared/index.js'
|
||||||
|
|
||||||
export class IPCClient {
|
export class IPCClient {
|
||||||
private netIPC: NetIPC
|
private netIPC: NetIPC
|
||||||
|
@ -65,7 +65,7 @@ export class IPCClient {
|
||||||
type: 'list-registered'
|
type: 'list-registered'
|
||||||
}
|
}
|
||||||
|
|
||||||
const { success, error, data } = await this.netIPC.request(req) as IPCResponse<IPCResponseData>
|
const { success, error, data } = await this.netIPC.request(req) as IPCResponse<IPCResponseListRegistered>
|
||||||
if (!success) {
|
if (!success) {
|
||||||
console.error('Could not list registered PeerTube instances', error)
|
console.error('Could not list registered PeerTube instances', error)
|
||||||
return
|
return
|
||||||
|
@ -82,6 +82,39 @@ export class IPCClient {
|
||||||
console.log(table.toString())
|
console.log(table.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async askListJobs (options: {
|
||||||
|
includePayload: boolean
|
||||||
|
}) {
|
||||||
|
const req: IPCRequest = {
|
||||||
|
type: 'list-jobs'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { success, error, data } = await this.netIPC.request(req) as IPCResponse<IPCResponseListJobs>
|
||||||
|
if (!success) {
|
||||||
|
console.error('Could not list jobs', error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const head = [ 'instance', 'type', 'started', 'progress' ]
|
||||||
|
if (options.includePayload) head.push('payload')
|
||||||
|
|
||||||
|
const table = new CliTable3({
|
||||||
|
head,
|
||||||
|
wordWrap: true,
|
||||||
|
wrapOnWordBoundary: false
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const { serverUrl, job } of data.processingJobs) {
|
||||||
|
const row = [ serverUrl, job.type, job.startedAt.toLocaleString(), `${job.progress}%` ]
|
||||||
|
if (options.includePayload) row.push(JSON.stringify(job.payload, undefined, 2))
|
||||||
|
|
||||||
|
table.push(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Processing ${data.processingJobs.length}/${data.concurrency} jobs`)
|
||||||
|
console.log(table.toString())
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async askGracefulShutdown () {
|
async askGracefulShutdown () {
|
||||||
|
|
|
@ -46,6 +46,9 @@ export class IPCServer {
|
||||||
case 'list-registered':
|
case 'list-registered':
|
||||||
return Promise.resolve(this.runnerServer.listRegistered())
|
return Promise.resolve(this.runnerServer.listRegistered())
|
||||||
|
|
||||||
|
case 'list-jobs':
|
||||||
|
return Promise.resolve(this.runnerServer.listJobs())
|
||||||
|
|
||||||
case 'graceful-shutdown':
|
case 'graceful-shutdown':
|
||||||
this.runnerServer.requestGracefulShutdown()
|
this.runnerServer.requestGracefulShutdown()
|
||||||
return undefined
|
return undefined
|
||||||
|
|
|
@ -2,7 +2,8 @@ export type IPCRequest =
|
||||||
IPCRequestRegister |
|
IPCRequestRegister |
|
||||||
IPCRequestUnregister |
|
IPCRequestUnregister |
|
||||||
IPCRequestListRegistered |
|
IPCRequestListRegistered |
|
||||||
IPCRequestGracefulShutdown
|
IPCRequestGracefulShutdown |
|
||||||
|
IPCRequestListJobs
|
||||||
|
|
||||||
export type IPCRequestRegister = {
|
export type IPCRequestRegister = {
|
||||||
type: 'register'
|
type: 'register'
|
||||||
|
@ -14,5 +15,6 @@ export type IPCRequestRegister = {
|
||||||
|
|
||||||
export type IPCRequestUnregister = { type: 'unregister', url: string, runnerName: string }
|
export type IPCRequestUnregister = { type: 'unregister', url: string, runnerName: string }
|
||||||
export type IPCRequestListRegistered = { type: 'list-registered' }
|
export type IPCRequestListRegistered = { type: 'list-registered' }
|
||||||
|
export type IPCRequestListJobs = { type: 'list-jobs' }
|
||||||
|
|
||||||
export type IPCRequestGracefulShutdown = { type: 'graceful-shutdown' }
|
export type IPCRequestGracefulShutdown = { type: 'graceful-shutdown' }
|
||||||
|
|
|
@ -1,15 +1,24 @@
|
||||||
|
import { RunnerJob } from '@peertube/peertube-models'
|
||||||
|
|
||||||
export type IPCResponse <T extends IPCResponseData = undefined> = {
|
export type IPCResponse <T extends IPCResponseData = undefined> = {
|
||||||
success: boolean
|
success: boolean
|
||||||
error?: string
|
error?: string
|
||||||
data?: T
|
data?: T
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IPCResponseData =
|
export type IPCResponseData = IPCResponseListRegistered | IPCResponseListJobs
|
||||||
// list registered
|
|
||||||
{
|
export type IPCResponseListRegistered = {
|
||||||
servers: {
|
servers: {
|
||||||
runnerName: string
|
runnerName: string
|
||||||
runnerDescription: string
|
runnerDescription: string
|
||||||
url: string
|
url: string
|
||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
|
export type IPCResponseListJobs = {
|
||||||
|
concurrency: number
|
||||||
|
processingJobs: {
|
||||||
|
serverUrl: string
|
||||||
|
job: Pick<RunnerJob, 'type' | 'startedAt' | 'progress' | 'payload'>
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
|
@ -53,6 +53,13 @@ describe('Test Live transcoding in peertube-runner program', function () {
|
||||||
transcoded: true
|
transcoded: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Check jobs output
|
||||||
|
{
|
||||||
|
const jobsList = await peertubeRunner.listJobs()
|
||||||
|
expect(jobsList).to.contain(servers[0].url)
|
||||||
|
expect(jobsList).to.contain('live-rtmp-hls-transcoding')
|
||||||
|
}
|
||||||
|
|
||||||
await stopFfmpeg(ffmpegCommand)
|
await stopFfmpeg(ffmpegCommand)
|
||||||
await waitUntilLiveWaitingOnAllServers(servers, video.uuid)
|
await waitUntilLiveWaitingOnAllServers(servers, video.uuid)
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,13 @@ export class PeerTubeRunnerProcess {
|
||||||
return stdout
|
return stdout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async listJobs () {
|
||||||
|
const args = [ 'list-jobs', ...this.buildIdArg() ]
|
||||||
|
const { stdout } = await this.runCommand(this.getRunnerPath(), args)
|
||||||
|
|
||||||
|
return stdout
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
gracefulShutdown () {
|
gracefulShutdown () {
|
||||||
|
|
|
@ -359,6 +359,26 @@ sudo -u prunner peertube-runner list-registered
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
### List jobs
|
||||||
|
|
||||||
|
**Runner >= 0.1.0**
|
||||||
|
|
||||||
|
To list jobs that are processed by the runner:
|
||||||
|
|
||||||
|
::: code-group
|
||||||
|
|
||||||
|
```bash [Shell]
|
||||||
|
peertube-runner list-jobs
|
||||||
|
peertube-runner list-jobs --include-payload
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [Systemd]
|
||||||
|
sudo -u prunner peertube-runner list-jobs
|
||||||
|
sudo -u prunner peertube-runner list-jobs --include-payload
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
### Graceful shutdown
|
### Graceful shutdown
|
||||||
|
|
||||||
Ask the runner to shutdown when it has finished all of its current tasks:
|
Ask the runner to shutdown when it has finished all of its current tasks:
|
||||||
|
@ -424,7 +444,7 @@ docker compose exec -u peertube peertube npm run parse-log -- --level info
|
||||||
|
|
||||||
`--level` is optional and could be `info`/`warn`/`error`
|
`--level` is optional and could be `info`/`warn`/`error`
|
||||||
|
|
||||||
You can also remove SQL or HTTP logs using `--not-tags` (PeerTube >= 3.2):
|
You can also remove SQL or HTTP logs using `--not-tags`:
|
||||||
|
|
||||||
::: code-group
|
::: code-group
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue