Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Zinnia Runtime and Peer Checker module #92

Merged
merged 29 commits into from
May 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d10b02e
feat: install Zinnia runtime
bajtos Apr 12, 2023
e6d4589
feat: install mod-peer-checker
bajtos Apr 12, 2023
1ae29dd
feat: run peer checker module via Zinnia
bajtos Apr 12, 2023
6606423
fix formatting
bajtos Apr 12, 2023
45c9074
fixup! windows zinniad extension
bajtos Apr 12, 2023
f5cfb10
test: fix never ending test
bajtos Apr 12, 2023
305d766
upgrade zinniad to v0.7.0
bajtos Apr 19, 2023
c7c070a
Update commands/station.js
bajtos Apr 25, 2023
e7ec760
Merge branch 'main' into feat/zinnia
bajtos Apr 26, 2023
e31bb4d
update tests
bajtos Apr 26, 2023
7341f74
ci: execute Docker image and test activity
bajtos Apr 26, 2023
e9504fc
fixup! fix failing tests
bajtos Apr 26, 2023
9821d35
fixup! docker CI workflow
bajtos Apr 26, 2023
11a56e7
troubleshoot docker CI not running
bajtos Apr 26, 2023
d35197c
enable Docker workflow for all pushes
bajtos Apr 26, 2023
4de0921
Revert "troubleshoot docker CI not running"
bajtos Apr 26, 2023
164dd70
more docker tweaks
bajtos Apr 26, 2023
3e6262f
Merge branch 'main' into feat/zinnia
bajtos May 2, 2023
61e049e
feat: switch base Docker image to full `node:18`
bajtos May 2, 2023
bef48a8
fixup! standard coding style
bajtos May 2, 2023
a3bd223
fix zip assets on windows
juliangruber May 3, 2023
f85c4a6
fixup! remove github from zinnia module repo name
bajtos May 3, 2023
50ce104
Merge branch 'main' into feat/zinnia
bajtos May 3, 2023
9f41802
bump zinnia to 0.8.0
bajtos May 3, 2023
f45ccb8
rename Module Runtime to Zinnia
bajtos May 3, 2023
d629dc4
move mocha config to config file and add --exit
bajtos May 4, 2023
b587b26
make test/station easier to troubleshoot
bajtos May 4, 2023
77b2369
fix expected Saturn messages
bajtos May 4, 2023
275355d
Merge branch 'main' into feat/zinnia
bajtos May 4, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,6 @@ jobs:

- name: Check | Saturn started
run: docker exec station bin/station.js activity | grep "Saturn module started"

- name: Check | Zinnia started
run: docker exec station bin/station.js activity | grep "Zinnia started"
2 changes: 2 additions & 0 deletions .mocharc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
timeout: 15000
exit: true
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:18-alpine
FROM node:18
LABEL org.opencontainers.image.source https://github.com/filecoin-station/core
USER node
WORKDIR /usr/src/app
Expand Down
1 change: 1 addition & 0 deletions bin/station.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Sentry.init({

const core = new Core(getDefaultRootDirs())
const modules = [
'zinnia',
'saturn-L2-node',
'bacalhau'
]
Expand Down
11 changes: 11 additions & 0 deletions commands/station.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { join } from 'node:path'
import * as saturnNode from '../lib/saturn-node.js'
import * as zinniaRuntime from '../lib/zinnia.js'
import { formatActivityObject } from '../lib/activity.js'
import lockfile from 'proper-lockfile'
import { maybeCreateFile } from '../lib/util.js'
Expand Down Expand Up @@ -36,6 +37,16 @@ export const station = async ({ core, json, experimental }) => {
logStream: core.logs.createWriteStream(
join(core.paths.moduleLogs, 'saturn-L2-node.log')
)
}),
zinniaRuntime.start({
FIL_WALLET_ADDRESS,
STATE_ROOT: join(core.paths.moduleState, 'zinnia'),
CACHE_ROOT: join(core.paths.moduleCache, 'zinnia'),
metricsStream: await core.metrics.createWriteStream('zinnia'),
activityStream: core.activity.createWriteStream('Zinnia'),
logStream: core.logs.createWriteStream(
join(core.paths.moduleLogs, 'zinnia.log')
)
})
]

Expand Down
43 changes: 40 additions & 3 deletions lib/modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@ export const getBinaryModuleExecutable = ({
return join(
moduleBinaries,
module,
`${executable}${os.platform() === 'win32' ? '.exe' : ''}`
getExecutableFileName(executable)
)
}

const getExecutableFileName = executable => {
return `${executable}${os.platform() === 'win32' ? '.exe' : ''}`
}

export const installBinaryModule = async ({
module,
repo,
Expand Down Expand Up @@ -79,8 +83,9 @@ export const installBinaryModule = async ({
const [entry] =
/** @type {[UnzipStreamEntry]} */
(await once(parser, 'entry'))
if (entry.path === executable) {
const outPath = join(moduleBinaries, module, executable)
const executableFileName = getExecutableFileName(executable)
if (entry.path === executableFileName) {
const outPath = join(moduleBinaries, module, executableFileName)
await pipeline(entry, createWriteStream(outPath))
await chmod(outPath, 0o755)
return
Expand All @@ -92,3 +97,35 @@ export const installBinaryModule = async ({
}
console.log(`[${module}] ✓ ${outFile}`)
}

export async function downloadSourceFiles ({ module, repo, distTag }) {
await mkdir(moduleBinaries, { recursive: true })
const outDir = join(moduleBinaries, module)

console.log(`[${module}] ⇣ downloading source files`)

const url = `https://github.com/${repo}/archive/refs/tags/${distTag}.tar.gz`
const res = await fetch(url, {
headers: {
...(authorization ? { authorization } : {})
},
redirect: 'follow'
})

if (res.status >= 300) {
throw new Error(
`[${module}] Cannot fetch ${module} archive for tag ${distTag}: ${res.status}\n` +
await res.text()
)
}

if (!res.body) {
throw new Error(
`[${module}] Cannot fetch ${module} archive for tag ${distTag}: no response body`
)
}

// `{ strip: 1}` tells tar to remove the top-level directory (e.g. `mod-peer-checker-v1.0.0`)
await pipeline(res.body, gunzip(), tar.extract(outDir, { strip: 1 }))
console.log(`[${module}] ✓ ${outDir}`)
}
144 changes: 144 additions & 0 deletions lib/zinnia.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import timers from 'node:timers/promises'
import { execa } from 'execa'
import * as Sentry from '@sentry/node'
import { installBinaryModule, downloadSourceFiles, getBinaryModuleExecutable } from './modules.js'
import { moduleBinaries } from './paths.js'

const ZINNIA_DIST_TAG = 'v0.8.0'
const ZINNIA_MODULES = [
{
module: 'peer-checker',
repo: 'filecoin-station/mod-peer-checker',
distTag: 'v1.0.0'
}
]

export async function install () {
await Promise.all([
installBinaryModule({
module: 'zinnia',
repo: 'filecoin-station/zinnia',
distTag: ZINNIA_DIST_TAG,
executable: 'zinniad',
targets: [
{ platform: 'darwin', arch: 'arm64', asset: 'zinniad-macos-arm64.zip' },
{ platform: 'darwin', arch: 'x64', asset: 'zinniad-macos-x64.zip' },
{ platform: 'linux', arch: 'arm64', asset: 'zinniad-linux-arm64.tar.gz' },
{ platform: 'linux', arch: 'x64', asset: 'zinniad-linux-x64.tar.gz' },
{ platform: 'win32', arch: 'x64', asset: 'zinniad-windows-x64.zip' }
]
}),

...Object.values(ZINNIA_MODULES).map(downloadSourceFiles)
])
}

async function start ({
FIL_WALLET_ADDRESS,
STATE_ROOT,
CACHE_ROOT,
metricsStream,
activityStream,
logStream
}) {
logStream.write('Starting Zinnia')

const zinniadExe = getBinaryModuleExecutable({ module: 'zinnia', executable: 'zinniad' })
const modules = [
// all paths are relative to `moduleBinaries`
'peer-checker/peer-checker.js'
]
const childProcess = execa(zinniadExe, modules, {
cwd: moduleBinaries,
env: {
FIL_WALLET_ADDRESS,
STATE_ROOT,
CACHE_ROOT
}
})

const readyPromise = new Promise((resolve, reject) => {
childProcess.stdout.setEncoding('utf-8')
childProcess.stdout.on('data', data => {
logStream.write(data)
handleEvents({ activityStream, metricsStream }, data)
})

childProcess.stderr.setEncoding('utf-8')
childProcess.stderr.on('data', data => {
logStream.write(data)
})

childProcess.stdout.once('data', _data => {
// This is based on an implicit assumption that zinniad reports an info activity
// after it starts
resolve()
})
childProcess.catch(reject)
})

childProcess.on('close', code => {
logStream.write(`Zinnia closed all stdio with code ${code ?? '<no code>'}`)
childProcess.stderr.removeAllListeners()
childProcess.stdout.removeAllListeners()
Sentry.captureException('Zinnia exited')
})

childProcess.on('exit', (code, signal) => {
const reason = signal ? `via signal ${signal}` : `with code: ${code}`
const msg = `Zinnia exited ${reason}`
logStream.write(msg)
activityStream.write({ type: 'info', message: msg })
})

try {
await Promise.race([
readyPromise,
timers.setTimeout(500)
])
} catch (err) {
const errorMsg = err instanceof Error ? err.message : '' + err
const message = `Cannot start Zinnia: ${errorMsg}`
logStream.write(message)
activityStream.write({ type: 'error', message })
}
}

function handleEvents ({ activityStream, metricsStream }, text) {
text
.trimEnd()
.split(/\n/g)
.forEach(line => {
try {
const event = JSON.parse(line)
switch (event.type) {
case 'activity:info':
activityStream.write({
type: 'info',
message: event.message.replace(/Module Runtime/, 'Zinnia'),
bajtos marked this conversation as resolved.
Show resolved Hide resolved
source: event.module ?? 'Zinnia'
})
break

case 'activity:error':
activityStream.write({
type: 'error',
message: event.message.replace(/Module Runtime/, 'Zinnia'),
bajtos marked this conversation as resolved.
Show resolved Hide resolved
source: event.module ?? 'Zinnia'
})
break

case 'jobs-completed':
metricsStream.write({ totalJobsCompleted: event.total, totalEarnings: '0' })
break

default:
console.error('Ignoring Zinnia event of unknown type:', event)
}
} catch (err) {
console.error('Ignoring malformed Zinnia event:', line)
}
})
}

export { start }
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
"scripts": {
"build": "tsc",
"format": "prettier --write .",
"start": "node ./bin/station.js",
"test": "npm run build && npm run test:lint && npm run test:unit",
"test:lint": "prettier --check . && standard",
"test:unit": "mocha --timeout 15000",
"test:unit": "mocha",
"version": "npm run build && node ./scripts/version.js",
"postinstall": "node ./scripts/post-install.js",
"postpublish": "node ./scripts/post-publish.js",
Expand All @@ -29,6 +30,7 @@
]
},
"devDependencies": {
"get-stream": "^6.0.1",
"mocha": "^10.2.0",
"np": "^7.6.3",
"prettier": "^2.8.4",
Expand Down
4 changes: 3 additions & 1 deletion scripts/post-install.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import { install as installSaturn } from '../lib/saturn-node.js'
import { install as installBacalhau } from '../lib/bacalhau.js'
import { install as installZinnia } from '../lib/zinnia.js'

await Promise.all([
installSaturn(),
installBacalhau()
installBacalhau(),
installZinnia()
])
Loading