1 | import fs from 'fs'
|
2 | import request from 'request'
|
3 | import async from 'async'
|
4 | import yaml from 'js-yaml'
|
5 | import assign from 'object.assign'
|
6 | import zsf from '@zombiec0rn/zombie-service-format'
|
7 | import znf from '@zombiec0rn/zombie-node-format'
|
8 | import Ora from 'ora'
|
9 | import * as route from './route'
|
10 | import * as mdns from './mdns'
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | export function initCmd(args) {
|
17 |
|
18 | process.stdin.resume()
|
19 |
|
20 | function exitHandler(err) {
|
21 | route.del(args, () => {
|
22 | console.error(err)
|
23 | process.exit()
|
24 | })
|
25 | }
|
26 |
|
27 |
|
28 | process.on('exit', exitHandler)
|
29 | process.on('SIGINT', exitHandler)
|
30 | process.on('uncaughtException', exitHandler)
|
31 |
|
32 |
|
33 | route.add(args)
|
34 | }
|
35 |
|
36 | export 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)
|
44 | assign(args, rc)
|
45 | }
|
46 |
|
47 | let defaultOptions = [
|
48 | {
|
49 | name: 'interface',
|
50 | help: 'Which interface to use (required)'
|
51 | }
|
52 | ]
|
53 | export { defaultOptions }
|
54 |
|
55 | export function validateArgs(args) {
|
56 | if (!args.interface) {
|
57 | console.log('Missing required argument `interface`. Exiting...')
|
58 | process.exit(1)
|
59 | }
|
60 | }
|
61 |
|
62 | export 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 |
|
71 | export 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 |
|
83 | export function readSwarmConfig(path) {
|
84 | let raw = readSwarmConfigRaw(path)
|
85 | let formatted = formatSwarmConfig(raw)
|
86 | return formatted
|
87 | }
|
88 |
|
89 | export function validateServices(services) {
|
90 | services.forEach(s => {
|
91 |
|
92 |
|
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 |
|
99 | export function readNodesConfig(path) {
|
100 | return JSON.parse(fs.readFileSync(path))
|
101 | }
|
102 |
|
103 | export function validateNodes(nodes) {
|
104 | return znf.validate(nodes)
|
105 | }
|
106 |
|
107 | export 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 |
|
140 | export 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 |
|
150 | export 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 |
|
162 | export function extractServices(node) {
|
163 | return (node.services || []).map(s => {
|
164 | s.host = node
|
165 | s.fingerprint = extractFingerprint(s)
|
166 | return s
|
167 | })
|
168 | }
|