UNPKG

4.33 kBJavaScriptView Raw
1import fs from 'fs'
2import request from 'request'
3import async from 'async'
4import yaml from 'js-yaml'
5import assign from 'object.assign'
6import zsf from '@zombiec0rn/zombie-service-format'
7import znf from '@zombiec0rn/zombie-node-format'
8import Ora from 'ora'
9import * as route from './route'
10import * as mdns from './mdns'
11
12// NOTE:
13// `initCmd` needs to be called whenever mdns is involved.
14// It creates a `route` for mdns packets -> interface
15// and hooks up listeners to remove this route when app exits.
16export function initCmd(args) {
17 // Prevent app from closing immediately
18 process.stdin.resume()
19 // exitHandler
20 function exitHandler(err) {
21 route.del(args, () => {
22 console.error(err)
23 process.exit()
24 })
25 }
26
27 // Call exitHandler on exit
28 process.on('exit', exitHandler)
29 process.on('SIGINT', exitHandler)
30 process.on('uncaughtException', exitHandler)
31
32 // Add route
33 route.add(args)
34}
35
36export function assignZombieRC(args) {
37 let rc = {}
38 try {
39 rc = yaml.safeLoad(fs.readFileSync('.zombierc'))
40 } catch(e) {
41 if (e.code != 'ENOENT') throw e
42 }
43 assign(rc, args) // Overwrite passed args
44 assign(args, rc) // Assign non-overwritten to args
45}
46
47let defaultOptions = [
48 {
49 name: 'interface',
50 help: 'Which interface to use (required)'
51 }
52]
53export { defaultOptions }
54
55export function validateArgs(args) {
56 if (!args.interface) {
57 console.log('Missing required argument `interface`. Exiting...')
58 process.exit(1)
59 }
60}
61
62export function readSwarmConfigRaw(path) {
63 try {
64 return yaml.safeLoad(fs.readFileSync(path))
65 } catch(e) {
66 throw e
67 process.exit(1)
68 }
69}
70
71export function formatSwarmConfig(config) {
72 let defaultServiceConfig = config.services.default || {}
73 config.services = Object.keys(config.services)
74 .filter(k => k != 'default')
75 .map(k => {
76 let serviceConfig = config.services[k]
77 serviceConfig.id = k
78 return assign({}, config.services[k], defaultServiceConfig)
79 })
80 return config
81}
82
83export function readSwarmConfig(path) {
84 let raw = readSwarmConfigRaw(path)
85 let formatted = formatSwarmConfig(raw)
86 return formatted
87}
88
89export function validateServices(services) {
90 services.forEach(s => {
91 // TODO: Move to scheduler.validateServices !?
92 // scheduler needs to export validateServices & validateNodes ??
93 if (!s.memory) throw new Error(`Missing memory - ${s.id}`)
94 if (!s.cpu || typeof s.cpu != 'number') throw new Error(`Missing cpu - ${s.id}`)
95 })
96 return zsf.validate(services)
97}
98
99export function readNodesConfig(path) {
100 return JSON.parse(fs.readFileSync(path))
101}
102
103export function validateNodes(nodes) {
104 return znf.validate(nodes)
105}
106
107export function querySwarmNodes(callback, args, queryTime) {
108 queryTime = queryTime || 5000
109 let _mdns = mdns.default(args)
110 let spinner = new Ora({ text: `Looking for swarm nodes on ${args.interface}...` })
111 spinner.start()
112 mdns.onResponse(_mdns, (answers) => {
113 async.map(answers, (a, cb) => {
114 request(`http://${a.data}:8901`, (err, res, body) => {
115 if (res.statusCode != 200) return cb(err, a)
116 let info = JSON.parse(body)
117 info.ip = a.data
118 cb(err, info)
119 })
120 }, (err, nodes) => {
121 _mdns.destroy()
122 spinner.stop()
123 if (err) {
124 console.error(err)
125 process.exit(1)
126 }
127 if (args.swarm) {
128 nodes = nodes.filter(n => n.swarm == args.swarm)
129 }
130 if (nodes.length == 0) {
131 console.log(`No swarm nodes found on ${args.interface} ¯\_(ツ)_/¯`)
132 process.exit(0)
133 }
134 callback(nodes)
135 })
136 }, queryTime)
137 mdns.query(_mdns)
138}
139
140export function detectDuplicateFingerprints(services) {
141 return services
142 .filter(s => s.fingerprint)
143 .map(s => s.fingerprint)
144 .filter((fp, i, arr) => {
145 let arrIndex = arr.indexOf(fp)
146 return arrIndex >= 0 && arrIndex != i
147 })
148}
149
150export function extractFingerprint(service) {
151 let fingerprint
152 if (service.env) {
153 service.env.forEach(e => {
154 if (e.indexOf('ZOMBIE_SWARM_FINGERPRINT') == 0) {
155 fingerprint = e.split('=')[1]
156 }
157 })
158 }
159 return fingerprint
160}
161
162export function extractServices(node) {
163 return (node.services || []).map(s => {
164 s.host = node
165 s.fingerprint = extractFingerprint(s)
166 return s
167 })
168}