Merge branch 'feature/page-cache' of http://git.yoho.cn/OPENTECH/yoho-node-ci in…
…to feature/page-cache
Showing
10 changed files
with
511 additions
and
370 deletions
apps/ci/collect_data.js
0 → 100644
1 | +/** | ||
2 | + * 采集数据 | ||
3 | + * | ||
4 | + * @class Collect | ||
5 | + * @author shenzm<zhimin.shen@yoho.cn> | ||
6 | + * @date 2016/10/12 | ||
7 | + */ | ||
8 | + | ||
9 | +import ssh from 'ssh2'; | ||
10 | +import Rp from 'request-promise'; | ||
11 | +import Trace from '../logger/trace.js'; | ||
12 | +import config from '../../config/config.js'; | ||
13 | + | ||
14 | +import { | ||
15 | + Server | ||
16 | +} from '../models'; | ||
17 | + | ||
18 | +var tracer = new Trace({ | ||
19 | + host: config.influxdb.host, | ||
20 | + port: config.influxdb.port | ||
21 | +}); | ||
22 | + | ||
23 | +class Collect { | ||
24 | + constructor(host, projectname) { | ||
25 | + this.host = host; | ||
26 | + this.projectname = projectname; | ||
27 | + this.scriptRunning = false; | ||
28 | + this.retry = {}; | ||
29 | + } | ||
30 | + | ||
31 | + async collect() { | ||
32 | + let self = this; | ||
33 | + let server = await Server.findByHost(self.host); | ||
34 | + self.server = { | ||
35 | + host: server.host, | ||
36 | + username: server.username, | ||
37 | + password: server.password, | ||
38 | + port: server.port | ||
39 | + } | ||
40 | + | ||
41 | + let obj = { | ||
42 | + 'total': 0, | ||
43 | + 'status': {} | ||
44 | + }; | ||
45 | + | ||
46 | + Rp({ | ||
47 | + uri: `http://${self.host}:9615`, | ||
48 | + json: true | ||
49 | + }).then(function(data) { | ||
50 | + var processes = data.processes || []; | ||
51 | + processes.forEach(function(p) { | ||
52 | + if (p.name === 'pm2-server-monit') { | ||
53 | + var cpuUsg = p.pm2_env.axm_monitor['CPU usage'].value; | ||
54 | + var freeMen = p.pm2_env.axm_monitor['Free memory'].value; | ||
55 | + | ||
56 | + obj.cpuUsg = cpuUsg ? parseFloat(cpuUsg.replace('%', '')).toFixed(2) : ''; | ||
57 | + obj.freeMen = freeMen ? parseFloat(freeMen.replace('%', '')).toFixed(2) : ''; | ||
58 | + } | ||
59 | + | ||
60 | + if (p.name === self.projectname) { | ||
61 | + obj.total++; | ||
62 | + | ||
63 | + if (!obj.status[p.pm2_env.status]) { | ||
64 | + obj.status[p.pm2_env.status] = 1; | ||
65 | + } else { | ||
66 | + obj.status[p.pm2_env.status]++; | ||
67 | + } | ||
68 | + } | ||
69 | + }); | ||
70 | + | ||
71 | + if (obj.cpuUsg === undefined) { // install server monit | ||
72 | + const script = 'pm2 install pm2-server-monit'; | ||
73 | + self.execScript(script); | ||
74 | + } | ||
75 | + | ||
76 | + // add into influxDB todo | ||
77 | + tracer.report('server_data', { | ||
78 | + host: self.host | ||
79 | + }, obj); | ||
80 | + }).catch(function(err) { | ||
81 | + const script = 'pm2 web'; | ||
82 | + self.execScript(script); | ||
83 | + }); | ||
84 | + } | ||
85 | + | ||
86 | + execScript(script) { | ||
87 | + let self = this; | ||
88 | + if (self.scriptRunning || self.retry[`${self.host}_${script}`] > 5) { | ||
89 | + // 脚本执行中,或者重试次数大于5次以上时,不执行脚本 | ||
90 | + return; | ||
91 | + } | ||
92 | + | ||
93 | + let retryCount = self.retry[`${self.host}_${script}`] || 0; | ||
94 | + self.retry[`${self.host}_${script}`] = ++retryCount; | ||
95 | + self.scriptRunning = true; | ||
96 | + | ||
97 | + let conn = new ssh.Client(); | ||
98 | + conn.on('ready', () => { | ||
99 | + self._log(`>>>>host:[${self.host}] script[${script}]`); | ||
100 | + conn.exec(`${script}`, (err, stream) => { | ||
101 | + if (err) { | ||
102 | + conn.end(); | ||
103 | + self._log(`host:[${self.host}] script:[${script}] exec fail error: ${err}`); | ||
104 | + self.scriptRunning = false; | ||
105 | + } else { | ||
106 | + stream.stdout.on('data', (data) => { | ||
107 | + //self._log(data.toString()); | ||
108 | + }); | ||
109 | + stream.stderr.on('data', (data) => { | ||
110 | + //self._log(data.toString()); | ||
111 | + }); | ||
112 | + stream.on('exit', (code) => { | ||
113 | + conn.end(); | ||
114 | + if (code === 0) { | ||
115 | + self._log(`host:[${self.host}] script[${script}] exec success`); | ||
116 | + } else { | ||
117 | + self._log(`host:[${self.host}] script[${script}] exec fail`); | ||
118 | + } | ||
119 | + self.scriptRunning = false; | ||
120 | + }); | ||
121 | + } | ||
122 | + }); | ||
123 | + }).on('error', (err) => { | ||
124 | + self._log(`connect error ${self.host} ${err}`); | ||
125 | + self.scriptRunning = false; | ||
126 | + }).connect(Object.assign(self.server, { | ||
127 | + readyTimeout: 5000 | ||
128 | + })); | ||
129 | + } | ||
130 | + | ||
131 | + _log(msg) { | ||
132 | + console.log(msg); | ||
133 | + } | ||
134 | +} | ||
135 | + | ||
136 | +export default Collect; |
@@ -170,7 +170,6 @@ class Deploy { | @@ -170,7 +170,6 @@ class Deploy { | ||
170 | } | 170 | } |
171 | 171 | ||
172 | _log(msg) { | 172 | _log(msg) { |
173 | - console.log(msg) | ||
174 | ws.broadcast(`/deploy/${this.project._id}/log`, { | 173 | ws.broadcast(`/deploy/${this.project._id}/log`, { |
175 | host: this.info.host, | 174 | host: this.info.host, |
176 | msg: msg | 175 | msg: msg |
1 | /** | 1 | /** |
2 | * 跟踪监控工具, 将监控数据写入influxdb | 2 | * 跟踪监控工具, 将监控数据写入influxdb |
3 | * | 3 | * |
4 | - * @usage: | ||
5 | - * <code> | ||
6 | - * let trace = new Trace({ | ||
7 | - * host: '54.222.219.223', | ||
8 | - * port: 4444 | ||
9 | - * }); | ||
10 | - * | ||
11 | - * let testTrace = trace.trace('test_key'); // createOrChoose a measurement | ||
12 | - * | ||
13 | - * // testTrace(someTags, someFields); | ||
14 | - * testTrace({ foo: 'bar', foobar: 'baz2'}, {value: 123, value2: 'aaa 123', value3: 1.3, value4: false}); | ||
15 | - * | ||
16 | - * </code> | ||
17 | - * | ||
18 | * @author: jiangfeng<jeff.jiang@yoho.cn> | 4 | * @author: jiangfeng<jeff.jiang@yoho.cn> |
19 | * @date: 16/8/1 | 5 | * @date: 16/8/1 |
20 | */ | 6 | */ |
@@ -36,79 +22,13 @@ class Trace { | @@ -36,79 +22,13 @@ class Trace { | ||
36 | this.options = options; | 22 | this.options = options; |
37 | } | 23 | } |
38 | 24 | ||
39 | - /** | ||
40 | - * create or choose a measurement to write point in. | ||
41 | - * @param name {string} the measurement name | ||
42 | - * @param options {object} some options. the protocol of influxdb connection. | ||
43 | - * @returns {function()} a point write function | ||
44 | - */ | ||
45 | - trace(name, options) { | ||
46 | - options = _.assign({ | ||
47 | - protocol: 'udp' | ||
48 | - }, options); | ||
49 | - | ||
50 | - let self = this; | ||
51 | - | ||
52 | - if (options.protocol === 'udp') { | ||
53 | - return (key, fields) => { | ||
54 | - return self.udpTrace(name, options, key, fields); | ||
55 | - }; | ||
56 | - } else if (options.protocol === 'http') { | ||
57 | - return (key, fields) => { | ||
58 | - return self.httpTrace(name, options, key, fields); | ||
59 | - }; | 25 | + report(name, keys, data) { |
26 | + if (this.options.appName) { | ||
27 | + keys.appName = this.options.appName; | ||
60 | } | 28 | } |
61 | - } | ||
62 | 29 | ||
63 | - /** | ||
64 | - * write point into influxdb by UDP | ||
65 | - * | ||
66 | - * @param name {string} the measurement name | ||
67 | - * @param options {object} | ||
68 | - * @param key {object} some keys of data | ||
69 | - * @param fields {object} some fields of data | ||
70 | - * @returns {Promise} | ||
71 | - */ | ||
72 | - udpTrace(name, options, key, fields) { | ||
73 | - | ||
74 | - if (_.isArray(key)) { | ||
75 | - key.forEach(p => { | ||
76 | - let line = `${this._escape(name)},${this._makeLine(p)}`; | ||
77 | - | ||
78 | - return this._updPostLine(line); | ||
79 | - }); | ||
80 | - } else { | ||
81 | - let line = `${this._escape(name)},${this._makeLine(key, false)} ${this._makeLine(fields, true)}`; | ||
82 | - | ||
83 | - return this._updPostLine(line); | ||
84 | - } | ||
85 | - } | ||
86 | - | ||
87 | - /** | ||
88 | - * upd send. | ||
89 | - * @param line {string} @see infulxdb's line protocol | ||
90 | - * @returns {Promise} | ||
91 | - * @private | ||
92 | - */ | ||
93 | - _updPostLine(line) { | ||
94 | - let self = this; | ||
95 | - | ||
96 | - return new Promise((resolve, reject) => { | ||
97 | - let socket = dgram.createSocket("udp4"); | ||
98 | - let buff = new Buffer(line); | ||
99 | - | ||
100 | - | ||
101 | - socket.send(buff, 0, buff.length, self.options.port, self.options.host, (err, rp) => { | ||
102 | - socket.close(); | ||
103 | - | ||
104 | - console.log(rp); | ||
105 | - if (err) { | ||
106 | - reject(err); | ||
107 | - } else { | ||
108 | - resolve(); | ||
109 | - } | ||
110 | - }); | ||
111 | - }); | 30 | + let line = `${this._escape(name)},${this._makeLine(keys, false)} ${this._makeLine(data, true)}`; |
31 | + return this._send(line); | ||
112 | } | 32 | } |
113 | 33 | ||
114 | /** | 34 | /** |
@@ -137,79 +57,51 @@ class Trace { | @@ -137,79 +57,51 @@ class Trace { | ||
137 | } | 57 | } |
138 | 58 | ||
139 | /** | 59 | /** |
140 | - * data escape with influxdb's line protocol. | ||
141 | - * | ||
142 | - * @param value {*} | ||
143 | - * @param withQuote {boolean} | ||
144 | - * @returns {*} | 60 | + * upd send. |
61 | + * @param line {string} @see infulxdb's line protocol | ||
62 | + * @returns {Promise} | ||
145 | * @private | 63 | * @private |
146 | */ | 64 | */ |
65 | + _send(line) { | ||
66 | + let self = this; | ||
67 | + | ||
68 | + return new Promise((resolve, reject) => { | ||
69 | + let socket = dgram.createSocket('udp4'); | ||
70 | + let buff = new Buffer(line); | ||
71 | + | ||
72 | + socket.send(buff, 0, buff.length, self.options.port, self.options.host, (err) => { | ||
73 | + socket.close(); | ||
74 | + | ||
75 | + if (err) { | ||
76 | + reject(err); | ||
77 | + } else { | ||
78 | + resolve(); | ||
79 | + } | ||
80 | + }); | ||
81 | + }); | ||
82 | + } | ||
83 | + | ||
147 | _escape(value, withQuote) { | 84 | _escape(value, withQuote) { |
148 | if (_.isString(value)) { | 85 | if (_.isString(value)) { |
149 | - value = _.replace(value, /,/g, '\\,'); | ||
150 | - value = _.replace(value, /=/g, '\\='); | ||
151 | - | ||
152 | if (withQuote) { | 86 | if (withQuote) { |
153 | value = '"' + value + '"'; | 87 | value = '"' + value + '"'; |
154 | } else { | 88 | } else { |
89 | + value = _.replace(value, /,/g, '\\,'); | ||
90 | + value = _.replace(value, /=/g, '\\='); | ||
155 | value = _.replace(value, /\s/g, '\\ '); | 91 | value = _.replace(value, /\s/g, '\\ '); |
156 | } | 92 | } |
157 | } else if (_.isInteger(value)) { | 93 | } else if (_.isInteger(value)) { |
158 | if (withQuote) { | 94 | if (withQuote) { |
159 | value = value + 'i'; | 95 | value = value + 'i'; |
160 | } | 96 | } |
97 | + } else if (_.isObject(value)) { | ||
98 | + value = '"' + _.replace(JSON.stringify(value), /"/g, '\\"') + '"'; | ||
99 | + } else if (_.isNull(value) || _.isNil(value)) { | ||
100 | + value = '""'; | ||
161 | } | 101 | } |
162 | 102 | ||
163 | return value; | 103 | return value; |
164 | } | 104 | } |
165 | - | ||
166 | - /** | ||
167 | - * write point into influxdb by HTTP. use the open source node-influx module. | ||
168 | - * @see https://github.com/node-influx/node-influx | ||
169 | - * | ||
170 | - * @param name {string} the measurement name | ||
171 | - * @param options {object} | ||
172 | - * @param key {object} some keys of data | ||
173 | - * @param fields {object} some fields of data | ||
174 | - * @returns {Promise} | ||
175 | - */ | ||
176 | - httpTrace(name, options, key, fields) { | ||
177 | - let client = this.getHttpClient(); | ||
178 | - | ||
179 | - return new Promise((resolve, reject) => { | ||
180 | - if (_.isArray(key)) { | ||
181 | - client.writePoints(name, key, options, (err, rp) => { | ||
182 | - if (err) { | ||
183 | - reject(err); | ||
184 | - } else { | ||
185 | - resolve(rp) | ||
186 | - } | ||
187 | - }); | ||
188 | - } else { | ||
189 | - client.writePoint(name, fields, key, options, (err, rp) => { | ||
190 | - if (err) { | ||
191 | - reject(err); | ||
192 | - } else { | ||
193 | - resolve(rp) | ||
194 | - } | ||
195 | - }); | ||
196 | - } | ||
197 | - }); | ||
198 | - } | ||
199 | - | ||
200 | - /** | ||
201 | - * the singleton http client. | ||
202 | - * | ||
203 | - * @returns {*} | ||
204 | - */ | ||
205 | - getHttpClient() { | ||
206 | - if (!this.httpClient) { | ||
207 | - this.httpClient = influx(this.options); | ||
208 | - } | ||
209 | - | ||
210 | - return this.httpClient; | ||
211 | - } | ||
212 | } | 105 | } |
213 | 106 | ||
214 | - | ||
215 | -module.exports = Trace; | 107 | +module.exports = Trace; |
apps/web/actions/collect_data.js
0 → 100644
1 | +'use strict'; | ||
2 | + | ||
3 | +import Collect from '../../ci/collect_data'; | ||
4 | + | ||
5 | +import { | ||
6 | + Project | ||
7 | +} from '../../models'; | ||
8 | + | ||
9 | +export default { | ||
10 | + async collect(ctx) { | ||
11 | + let projects, | ||
12 | + servers = {}; | ||
13 | + | ||
14 | + setInterval(async() => { | ||
15 | + if (!projects) projects = await Project.findAll(); | ||
16 | + if (!projects || !projects.length) { | ||
17 | + return; | ||
18 | + } | ||
19 | + | ||
20 | + projects.forEach(async(p) => { | ||
21 | + const hosts = p.deploy['production'].target; | ||
22 | + hosts.forEach((host) => { | ||
23 | + if (!servers[host]) { | ||
24 | + servers[host] = new Collect(host, p.name); | ||
25 | + } | ||
26 | + | ||
27 | + servers[host].collect(); | ||
28 | + }); | ||
29 | + }); | ||
30 | + }, 5000); | ||
31 | + } | ||
32 | +}; |
1 | -'use strict'; | ||
2 | - | ||
3 | -import Router from 'koa-router'; | ||
4 | -import moment from 'moment'; | ||
5 | -import _ from 'lodash'; | ||
6 | - | ||
7 | -import {Degrade, DegradeServer} from '../../models'; | ||
8 | - | ||
9 | -import getter from '../../zookeeper/getter'; | ||
10 | -import setter from '../../zookeeper/setter'; | ||
11 | -import tester from '../../zookeeper/tester'; | ||
12 | - | ||
13 | -const router = new Router(); | ||
14 | - | ||
15 | -const ctl = { | ||
16 | - async getServer() { | ||
17 | - let server = await DegradeServer.findAll({}); | ||
18 | - | ||
19 | - server = _.last(server); | ||
20 | - | ||
21 | - if (server) { | ||
22 | - return `${server.ip}:${server.port}`; | ||
23 | - } | ||
24 | - }, | ||
25 | - async index (ctx) { | ||
26 | - let serverPath = await ctl.getServer(); | ||
27 | - let serverSplit = serverPath ? serverPath.split(':') : ['', '']; | ||
28 | - | ||
29 | - await ctx.render('action/degrade', { | ||
30 | - ip: serverSplit[0], | ||
31 | - port: serverSplit[1] | ||
32 | - }); | ||
33 | - }, | ||
34 | - async connect(ctx) { | ||
35 | - let {ip, port} = ctx.query; | ||
36 | - | ||
37 | - let server = `${ip}:${port}`; | ||
38 | - | ||
39 | - let connected = await tester(server); | ||
40 | - | ||
41 | - // connecting test | ||
42 | - if (!connected) { | ||
43 | - ctx.body = `<p class="connect-err"> | ||
44 | - <i class="fa fa-wheelchair" aria-hidden="true"></i> | ||
45 | - Sorry, I can not connect to <span class="server-name">${server}</span>.Please check whether your ip/port is correct or the zookeeper server is running | ||
46 | - </p>`; | ||
47 | - return; | ||
48 | - } | ||
49 | - | ||
50 | - let degrades = await Degrade.findAll(); | ||
51 | - | ||
52 | - for(let i of degrades) { | ||
53 | - i.checked = await getter(server, i.path); | ||
54 | - } | ||
55 | - | ||
56 | - let pc = _.filter(degrades, o => _.startsWith(o.path, '/pc')); | ||
57 | - let wap = _.filter(degrades, o => _.startsWith(o.path, '/wap')); | ||
58 | - | ||
59 | - await ctx.render('action/degrade_list', { | ||
60 | - layout: false, | ||
61 | - pc: pc, | ||
62 | - wap: wap | ||
63 | - }); | ||
64 | - }, | ||
65 | - async server(ctx) { | ||
66 | - let ip = ctx.request.body.ip; | ||
67 | - let port = ctx.request.body.port; | ||
68 | - | ||
69 | - let serverCount = await DegradeServer.count({}); | ||
70 | - | ||
71 | - // keep one server | ||
72 | - if (serverCount) { | ||
73 | - let serverConfig = await DegradeServer.findAll({}); | ||
74 | - let id = _.last(serverConfig)._id; // get the latest item | ||
75 | - | ||
76 | - await DegradeServer.update({ | ||
77 | - _id: id | ||
78 | - }, { | ||
79 | - $set: { | ||
80 | - ip: ip, | ||
81 | - port: port | ||
82 | - } | ||
83 | - }); | ||
84 | - } else { | ||
85 | - await DegradeServer.insert({ | ||
86 | - ip: ip, | ||
87 | - port: port | ||
88 | - }); | ||
89 | - } | ||
90 | - | ||
91 | - ctx.body = { | ||
92 | - code: 200, | ||
93 | - message: `${serverCount ? 'update' : 'new'} server success` | ||
94 | - }; | ||
95 | - }, | ||
96 | - async setter(ctx) { | ||
97 | - let {checked, id} = ctx.query; | ||
98 | - | ||
99 | - let theDegrade = await Degrade.findById(id); | ||
100 | - | ||
101 | - let path = theDegrade.path; | ||
102 | - | ||
103 | - let serverPath = await ctl.getServer(); | ||
104 | - | ||
105 | - await setter(serverPath, path, checked.toString()); | ||
106 | - | ||
107 | - ctx.body = { | ||
108 | - code: 200, | ||
109 | - message: 'update success' | ||
110 | - }; | ||
111 | - } | ||
112 | -}; | ||
113 | - | ||
114 | -router.get('/', ctl.index); | ||
115 | -router.post('/server', ctl.server); | ||
116 | -router.get('/connect', ctl.connect); | ||
117 | -router.get('/setter', ctl.setter); | ||
118 | - | ||
119 | -export default router; | ||
1 | +'use strict'; | ||
2 | + | ||
3 | +import Router from 'koa-router'; | ||
4 | +import moment from 'moment'; | ||
5 | +import _ from 'lodash'; | ||
6 | +import process from 'process'; | ||
7 | + | ||
8 | +import {Degrade, DegradeServer} from '../../models'; | ||
9 | + | ||
10 | +import getter from '../../zookeeper/getter'; | ||
11 | +import setter from '../../zookeeper/setter'; | ||
12 | +import tester from '../../zookeeper/tester'; | ||
13 | + | ||
14 | +const router = new Router(); | ||
15 | + | ||
16 | +const ctl = { | ||
17 | + async index (ctx) { | ||
18 | + let qcloudServer = await DegradeServer.findOne({ | ||
19 | + type: 'qcloud' | ||
20 | + }); | ||
21 | + | ||
22 | + if (!qcloudServer) { | ||
23 | + qcloudServer = {}; | ||
24 | + } | ||
25 | + | ||
26 | + let awsServer = await DegradeServer.findOne({ | ||
27 | + type: 'aws' | ||
28 | + }); | ||
29 | + | ||
30 | + if (!awsServer) { | ||
31 | + awsServer = {}; | ||
32 | + } | ||
33 | + | ||
34 | + await ctx.render('action/degrade', { | ||
35 | + qCloudConfig: { | ||
36 | + ip: qcloudServer.ip, | ||
37 | + port: qcloudServer.port | ||
38 | + }, | ||
39 | + awsConfig: { | ||
40 | + ip: awsServer.ip, | ||
41 | + port: awsServer.port | ||
42 | + } | ||
43 | + }); | ||
44 | + }, | ||
45 | + async connect(ctx) { | ||
46 | + let {ip, port} = ctx.query; | ||
47 | + | ||
48 | + let server = `${ip}:${port}`; | ||
49 | + | ||
50 | + let connected = await tester(server); | ||
51 | + | ||
52 | + // connecting test | ||
53 | + if (!connected) { | ||
54 | + ctx.body = `<p class="connect-err"> | ||
55 | + <i class="fa fa-wheelchair" aria-hidden="true"></i> | ||
56 | + Sorry, I can not connect to <span class="server-name">${server}</span>.Please check whether your ip/port is correct or the zookeeper server is running | ||
57 | + </p>`; | ||
58 | + return; | ||
59 | + } | ||
60 | + | ||
61 | + let degrades = await Degrade.findAll(); | ||
62 | + | ||
63 | + for(let i of degrades) { | ||
64 | + // 从zookeeper读取配置信息,memcached只做PHP读取使用 | ||
65 | + i.checked = await getter(server, i.path); | ||
66 | + } | ||
67 | + | ||
68 | + let pc = _.filter(degrades, o => _.startsWith(o.path, '/pc')); | ||
69 | + let wap = _.filter(degrades, o => _.startsWith(o.path, '/wap')); | ||
70 | + | ||
71 | + await ctx.render('action/degrade_list', { | ||
72 | + layout: false, | ||
73 | + pc: pc, | ||
74 | + wap: wap | ||
75 | + }); | ||
76 | + }, | ||
77 | + async server(ctx) { | ||
78 | + let {ip, port, type} = ctx.request.body; | ||
79 | + | ||
80 | + let serverCount = await DegradeServer.count({ | ||
81 | + type: type | ||
82 | + }); | ||
83 | + | ||
84 | + // keep one server | ||
85 | + if (serverCount) { | ||
86 | + let serverConfig = await DegradeServer.findOne({ | ||
87 | + type: type | ||
88 | + }); | ||
89 | + let id = serverConfig._id; // get the latest item | ||
90 | + | ||
91 | + await DegradeServer.update({ | ||
92 | + _id: id | ||
93 | + }, { | ||
94 | + $set: { | ||
95 | + ip: ip, | ||
96 | + port: port | ||
97 | + } | ||
98 | + }); | ||
99 | + } else { | ||
100 | + await DegradeServer.insert({ | ||
101 | + ip: ip, | ||
102 | + port: port, | ||
103 | + type: type | ||
104 | + }); | ||
105 | + } | ||
106 | + | ||
107 | + ctx.body = { | ||
108 | + code: 200, | ||
109 | + message: `${serverCount ? 'update' : 'new'} ${type} server success` | ||
110 | + }; | ||
111 | + }, | ||
112 | + async setter(ctx) { | ||
113 | + let {checked, id, type} = ctx.query; | ||
114 | + | ||
115 | + let theDegrade = await Degrade.findById(id); | ||
116 | + | ||
117 | + let path = theDegrade.path; | ||
118 | + | ||
119 | + let server = await await DegradeServer.findOne({ | ||
120 | + type: type | ||
121 | + }); | ||
122 | + | ||
123 | + let result = await setter(`${server.ip}:${server.port}`, path, checked.toString()); | ||
124 | + | ||
125 | + // result结果以zookeeper写为准 | ||
126 | + if (result) { | ||
127 | + ctx.body = { | ||
128 | + code: 200, | ||
129 | + message: 'update success' | ||
130 | + }; | ||
131 | + } else { | ||
132 | + ctx.body = { | ||
133 | + code: 500, | ||
134 | + message: 'update fail,Please retry' | ||
135 | + } | ||
136 | + } | ||
137 | + } | ||
138 | +}; | ||
139 | + | ||
140 | +router.get('/', ctl.index); | ||
141 | +router.post('/server', ctl.server); | ||
142 | +router.get('/connect', ctl.connect); | ||
143 | +router.get('/setter', ctl.setter); | ||
144 | + | ||
145 | +export default router; |
@@ -61,6 +61,9 @@ const p = { | @@ -61,6 +61,9 @@ const p = { | ||
61 | deploy.env = env; | 61 | deploy.env = env; |
62 | deploy.name = envs[env]; | 62 | deploy.name = envs[env]; |
63 | 63 | ||
64 | + if (typeof deploy.target === 'string') { | ||
65 | + deploy.target = [deploy.target]; | ||
66 | + } | ||
64 | 67 | ||
65 | let promises = deploy.target.map((host) => { | 68 | let promises = deploy.target.map((host) => { |
66 | console.log('read host :' + host); | 69 | console.log('read host :' + host); |
@@ -220,6 +223,10 @@ const p = { | @@ -220,6 +223,10 @@ const p = { | ||
220 | let project = await Project.findById(building.projectId); | 223 | let project = await Project.findById(building.projectId); |
221 | let targets = project.deploy[building.env].target; | 224 | let targets = project.deploy[building.env].target; |
222 | 225 | ||
226 | + if (typeof targets === 'string') { | ||
227 | + targets = [targets]; | ||
228 | + } | ||
229 | + | ||
223 | targets.forEach(async(host) => { | 230 | targets.forEach(async(host) => { |
224 | 231 | ||
225 | let doc = await DeployInfo.findOne({ | 232 | let doc = await DeployInfo.findOne({ |
@@ -394,7 +401,11 @@ const p = { | @@ -394,7 +401,11 @@ const p = { | ||
394 | return; | 401 | return; |
395 | } | 402 | } |
396 | 403 | ||
397 | - const hosts = project.deploy[env].target; | 404 | + let hosts = project.deploy[env].target; |
405 | + | ||
406 | + if (typeof hosts === 'string') { | ||
407 | + hosts = [hosts]; | ||
408 | + } | ||
398 | hosts.forEach((host) => { | 409 | hosts.forEach((host) => { |
399 | var obj = { | 410 | var obj = { |
400 | 'total': 0, | 411 | 'total': 0, |
@@ -10,6 +10,7 @@ import Koa from 'koa'; | @@ -10,6 +10,7 @@ import Koa from 'koa'; | ||
10 | import hbs from '../../middleware/yoho-koa-hbs'; | 10 | import hbs from '../../middleware/yoho-koa-hbs'; |
11 | import helpers from '../../lib/helpers'; | 11 | import helpers from '../../lib/helpers'; |
12 | import routers from './routers' | 12 | import routers from './routers' |
13 | +import collectData from './actions/collect_data'; | ||
13 | 14 | ||
14 | const app = new Koa(); | 15 | const app = new Koa(); |
15 | 16 | ||
@@ -29,6 +30,9 @@ const mastersUrl = [ | @@ -29,6 +30,9 @@ const mastersUrl = [ | ||
29 | '/users' | 30 | '/users' |
30 | ]; | 31 | ]; |
31 | 32 | ||
33 | +// 服务器监控数据采集 | ||
34 | +collectData.collect(); | ||
35 | + | ||
32 | app.use(async(ctx, next) => { | 36 | app.use(async(ctx, next) => { |
33 | ctx.locals = { | 37 | ctx.locals = { |
34 | title: 'Yoho Node.js 持续集成平台' | 38 | title: 'Yoho Node.js 持续集成平台' |
@@ -38,7 +42,7 @@ app.use(async(ctx, next) => { | @@ -38,7 +42,7 @@ app.use(async(ctx, next) => { | ||
38 | ctx.locals.layout = null; | 42 | ctx.locals.layout = null; |
39 | } | 43 | } |
40 | 44 | ||
41 | - if (ctx.session && ctx.session.user ) { | 45 | + if (ctx.session && ctx.session.user) { |
42 | ctx.locals.is_master = ctx.session.user.role === '1000'; | 46 | ctx.locals.is_master = ctx.session.user.role === '1000'; |
43 | ctx.locals.current_user = ctx.session.user; | 47 | ctx.locals.current_user = ctx.session.user; |
44 | } | 48 | } |
@@ -35,136 +35,161 @@ | @@ -35,136 +35,161 @@ | ||
35 | float: right; | 35 | float: right; |
36 | cursor: pointer; | 36 | cursor: pointer; |
37 | } | 37 | } |
38 | + | ||
39 | + .config-panel .server-port { | ||
40 | + width: 100px; | ||
41 | + } | ||
42 | + | ||
43 | + .config-panel .form-inline { | ||
44 | + padding: 20px 0; | ||
45 | + border-bottom: 1px solid #d9edf7; | ||
46 | + } | ||
38 | </style> | 47 | </style> |
39 | <div class="degrade-page"> | 48 | <div class="degrade-page"> |
40 | <div class="panel panel-info"> | 49 | <div class="panel panel-info"> |
41 | - <div class="panel-heading">zookeeper server</div> | 50 | + <div class="panel-heading">zookeeper configure</div> |
42 | <div class="panel-body"> | 51 | <div class="panel-body"> |
43 | - <div class="form-inline"> | ||
44 | - <div class="current-server{{#unless ip}} hide{{/unless}}"> | ||
45 | - 当前server地址: | ||
46 | - <span id="server-in-use" class="server-in-use" data-ip="{{ip}}" data-port="{{port}}">{{ip}}:{{port}}</span> | ||
47 | - <span id="server-modify-btn" class="ip-port-modify-btn">修改</span> | ||
48 | - </div> | ||
49 | - <div class="server-editor{{#if ip}} hide{{/if}}"> | ||
50 | - <div class="form-group"> | ||
51 | - <label for="server-ip">Server IP</label> | ||
52 | - <input id="server-ip" class="form-control" type="text" placeholder="input server ip" value="{{ip}}"> | 52 | + <div class="row"> |
53 | + <div class="col-md-6 config-panel" data-type="qcloud"> | ||
54 | + <div class="panel panel-success"> | ||
55 | + <div class="panel-heading"> | ||
56 | + 腾讯云 | ||
57 | + <span class="refresh"> | ||
58 | + <i class="glyphicon glyphicon-refresh"></i> | ||
59 | + </span> | ||
60 | + </div> | ||
61 | + <div class="panel-body"> | ||
62 | + {{# qCloudConfig}} | ||
63 | + <div class="form-inline"> | ||
64 | + <div class="current-server{{#unless ip}} hide{{/unless}}"> | ||
65 | + 当前server地址: | ||
66 | + <span class="server-in-use" data-ip="{{ip}}" data-port="{{port}}">{{ip}}:{{port}}</span> | ||
67 | + <span class="ip-port-modify-btn">修改</span> | ||
68 | + </div> | ||
69 | + <div class="server-editor{{#if ip}} hide{{/if}}"> | ||
70 | + <div class="form-group"> | ||
71 | + <label>Server IP</label> | ||
72 | + <input class="form-control server-ip" type="text" value="{{ip}}"> | ||
73 | + </div> | ||
74 | + <div class="form-group"> | ||
75 | + <label for="server-port">Server Port</label> | ||
76 | + <input class="form-control server-port" type="text" value="{{port}}"> | ||
77 | + </div> | ||
78 | + <span class="btn btn-default server-sure">保存</span> | ||
79 | + </div> | ||
80 | + </div> | ||
81 | + {{/ qCloudConfig}} | ||
82 | + | ||
83 | + <div class="panel-body list-container"> | ||
84 | + Waitting for connecting to server... | ||
85 | + </div> | ||
86 | + </div> | ||
53 | </div> | 87 | </div> |
54 | - <div class="form-group"> | ||
55 | - <label for="server-port">Server Port</label> | ||
56 | - <input id="server-port" class="form-control" type="text" placeholder="input server port" value="{{port}}"> | 88 | + </div> |
89 | + <div class="col-md-6 config-panel" data-type="aws"> | ||
90 | + <div class="panel panel-warning"> | ||
91 | + <div class="panel-heading"> | ||
92 | + AWS | ||
93 | + <span class="refresh"> | ||
94 | + <i class="glyphicon glyphicon-refresh"></i> | ||
95 | + </span> | ||
96 | + </div> | ||
97 | + <div class="panel-body"> | ||
98 | + {{# awsConfig}} | ||
99 | + <div class="form-inline"> | ||
100 | + <div class="current-server{{#unless ip}} hide{{/unless}}"> | ||
101 | + 当前server地址: | ||
102 | + <span class="server-in-use" data-ip="{{ip}}" data-port="{{port}}">{{ip}}:{{port}}</span> | ||
103 | + <span class="ip-port-modify-btn">修改</span> | ||
104 | + </div> | ||
105 | + <div class="server-editor{{#if ip}} hide{{/if}}"> | ||
106 | + <div class="form-group"> | ||
107 | + <label>Server IP</label> | ||
108 | + <input class="form-control server-ip" type="text" value="{{ip}}"> | ||
109 | + </div> | ||
110 | + <div class="form-group"> | ||
111 | + <label>Server Port</label> | ||
112 | + <input class="form-control server-port" type="text" value="{{port}}"> | ||
113 | + </div> | ||
114 | + <span class="btn btn-default server-sure">保存</span> | ||
115 | + </div> | ||
116 | + </div> | ||
117 | + {{/ awsConfig}} | ||
118 | + | ||
119 | + <div class="panel-body list-container"> | ||
120 | + Waitting for connecting to server... | ||
121 | + </div> | ||
122 | + </div> | ||
57 | </div> | 123 | </div> |
58 | - <span id="server-sure" class="btn btn-default">保存</span> | ||
59 | - <span id="err-tip" class="err-tip hide"> | ||
60 | - <i class="glyphicon glyphicon-remove-sign"></i> | ||
61 | - 小哥,填错了吧 | ||
62 | - </span> | ||
63 | </div> | 124 | </div> |
64 | </div> | 125 | </div> |
65 | </div> | 126 | </div> |
66 | </div> | 127 | </div> |
67 | - <div class="panel panel-danger"> | ||
68 | - <div class="panel-heading"> | ||
69 | - degrade point | ||
70 | - <span id="refresh-connection" class="refresh"> | ||
71 | - <i class="glyphicon glyphicon-refresh"></i> | ||
72 | - </span> | ||
73 | - </div> | ||
74 | - <div id="list-container" class="panel-body"> | ||
75 | - Waitting for connecting to server... | ||
76 | - </div> | ||
77 | - </div> | ||
78 | </div> | 128 | </div> |
79 | 129 | ||
80 | <script> | 130 | <script> |
81 | $(function() { | 131 | $(function() { |
82 | - var $ip = $('#server-ip'); | ||
83 | - var $port = $('#server-port'); | ||
84 | - var $tip = $('#err-tip'); | ||
85 | - | ||
86 | - var $listContainer = $('#list-container'); | ||
87 | - | ||
88 | - function validateIP(ip) { | ||
89 | - if (ip === 'localhost' || | ||
90 | - /^(\d)+\.(\d)+\.(\d)+\.(\d)+$/.test(ip)) | ||
91 | - { | ||
92 | - $ip.closest('.form-group').removeClass('has-error'); | ||
93 | - return true; | ||
94 | - } | ||
95 | - | ||
96 | - $ip.closest('.form-group').addClass('has-error'); | ||
97 | - return false; | ||
98 | - } | ||
99 | 132 | ||
100 | - function validatePort(port) { | ||
101 | - if (/^[1-9]\d*$/.test(port) && +port >= 1 && +port <= 65535) { | ||
102 | - $port.closest('.form-group').removeClass('has-error'); | ||
103 | - return true; | ||
104 | - } | ||
105 | - | ||
106 | - $port.closest('.form-group').addClass('has-error'); | ||
107 | - return false; | ||
108 | - } | 133 | + $('.ip-port-modify-btn').click(function() { |
134 | + var $form = $(this).closest('.form-inline'); | ||
109 | 135 | ||
110 | - $('#server-modify-btn').click(function() { | ||
111 | - $('.server-editor').removeClass('hide'); | ||
112 | - $('.current-server').addClass('hide'); | 136 | + $('.server-editor', $form).removeClass('hide'); |
137 | + $('.current-server', $form).addClass('hide'); | ||
113 | }); | 138 | }); |
114 | 139 | ||
115 | // server | 140 | // server |
116 | - $('#server-sure').click(function() { | ||
117 | - var ip = $.trim($ip.val()); | ||
118 | - var port = $.trim($port.val()); | 141 | + $('.server-sure').click(function() { |
142 | + var $panel = $(this).closest('.config-panel'); | ||
119 | 143 | ||
120 | - if (!validateIP(ip) || !validatePort(port)) { | ||
121 | - $tip.removeClass('hide'); | ||
122 | - return; | ||
123 | - } | 144 | + var $ip = $('.server-ip', $panel); |
145 | + var $port = $('.server-port', $panel); | ||
124 | 146 | ||
125 | - $tip.addClass('hide'); | 147 | + var ip = $.trim($ip.val()); |
148 | + var port = $.trim($port.val()); | ||
126 | 149 | ||
127 | $.ajax({ | 150 | $.ajax({ |
128 | type: 'POST', | 151 | type: 'POST', |
129 | url: '/degrade/server', | 152 | url: '/degrade/server', |
130 | data: { | 153 | data: { |
131 | ip: ip, | 154 | ip: ip, |
132 | - port: port | 155 | + port: port, |
156 | + type: $panel.data('type') | ||
133 | } | 157 | } |
134 | }).then(function(data) { | 158 | }).then(function(data) { |
135 | if (data.code === 200) { | 159 | if (data.code === 200) { |
136 | //$.pjax.reload('#pjax-container'); | 160 | //$.pjax.reload('#pjax-container'); |
137 | - $('.server-editor').addClass('hide'); | ||
138 | - $('.current-server').removeClass('hide'); | 161 | + $('.server-editor', $panel).addClass('hide'); |
162 | + $('.current-server', $panel).removeClass('hide'); | ||
139 | 163 | ||
140 | - $('#server-in-use').text(ip + ':' + port).data('ip', ip).data('port', port); | 164 | + $('.server-in-use', $panel).text(ip + ':' + port).data('ip', ip).data('port', port); |
141 | 165 | ||
142 | - $('#list-container').html('waiting for connecting server...'); | 166 | + $('.list-container', $panel).html('waiting for connecting server...'); |
143 | } | 167 | } |
144 | }); | 168 | }); |
145 | }); | 169 | }); |
146 | 170 | ||
147 | - $listContainer.on('click', '#degrade-tab li', function() { | 171 | + $('.list-container').on('click', '.degrade-tab li', function() { |
148 | var $this = $(this); | 172 | var $this = $(this); |
173 | + var $panel = $this.closest('.config-panel'); | ||
149 | 174 | ||
150 | if ($this.hasClass('active')) { | 175 | if ($this.hasClass('active')) { |
151 | return; | 176 | return; |
152 | } | 177 | } |
153 | 178 | ||
154 | - $('li', $('#degrade-tab')).toggleClass('active'); | 179 | + $this.parent().children('li').toggleClass('active'); |
155 | 180 | ||
156 | var index = $this.index(); | 181 | var index = $this.index(); |
157 | 182 | ||
158 | if (index === 0) { | 183 | if (index === 0) { |
159 | 184 | ||
160 | //PC active | 185 | //PC active |
161 | - $('.pc-degrade').removeClass('hide'); | ||
162 | - $('.wap-degrade').addClass('hide'); | 186 | + $('.pc-degrade', $panel).removeClass('hide'); |
187 | + $('.wap-degrade', $panel).addClass('hide'); | ||
163 | } else { | 188 | } else { |
164 | 189 | ||
165 | // wap active | 190 | // wap active |
166 | - $('.wap-degrade').removeClass('hide'); | ||
167 | - $('.pc-degrade').addClass('hide'); | 191 | + $('.wap-degrade', $panel).removeClass('hide'); |
192 | + $('.pc-degrade', $panel).addClass('hide'); | ||
168 | } | 193 | } |
169 | }).on('change', '.degrade-content input[type="checkbox"]', function() { | 194 | }).on('change', '.degrade-content input[type="checkbox"]', function() { |
170 | var $checkbox = $(this), | 195 | var $checkbox = $(this), |
@@ -180,19 +205,26 @@ | @@ -180,19 +205,26 @@ | ||
180 | checked: checked, | 205 | checked: checked, |
181 | id: id | 206 | id: id |
182 | } | 207 | } |
208 | + }).then(function(data) { | ||
209 | + if (data.code !== 200) { | ||
210 | + $checkbox.prop('checked', !checked); | ||
211 | + alert(data.message); | ||
212 | + } | ||
183 | }); | 213 | }); |
184 | }); | 214 | }); |
185 | 215 | ||
186 | - $('#refresh-connection').click(function() { | 216 | + $('.refresh').click(function() { |
217 | + var $panel = $(this).closest('.config-panel'); | ||
218 | + | ||
187 | $.ajax({ | 219 | $.ajax({ |
188 | type: 'GET', | 220 | type: 'GET', |
189 | url: '/degrade/connect', | 221 | url: '/degrade/connect', |
190 | data: { | 222 | data: { |
191 | - ip: $('#server-in-use').data('ip'), | ||
192 | - port: $('#server-in-use').data('port') | 223 | + ip: $('.server-in-use', $panel).data('ip'), |
224 | + port: $('.server-in-use', $panel).data('port') | ||
193 | } | 225 | } |
194 | }).then(function(data) { | 226 | }).then(function(data) { |
195 | - $('#list-container').html(data); | 227 | + $('.list-container', $panel).html(data); |
196 | }); | 228 | }); |
197 | }) | 229 | }) |
198 | }) | 230 | }) |
1 | -'usu strict'; | ||
2 | - | ||
3 | -import _ from 'lodash'; | ||
4 | -import zookeeper from 'node-zookeeper-client'; | ||
5 | - | ||
6 | -module.exports = (server, path, val) => new Promise((resolve, reject) => { | ||
7 | - const client = zookeeper.createClient(server); | ||
8 | - | ||
9 | - client.once('connected', function () { | ||
10 | - client.setData(path, new Buffer(val.toString()), function(err, data, stat) { | ||
11 | - console.log('path %s data change to', path, val); | ||
12 | - resolve(); | ||
13 | - client.close(); | ||
14 | - }); | ||
15 | - }); | ||
16 | - | ||
17 | - client.connect(); | ||
18 | -}); | 1 | +'usu strict'; |
2 | + | ||
3 | +import _ from 'lodash'; | ||
4 | +import zookeeper from 'node-zookeeper-client'; | ||
5 | + | ||
6 | +module.exports = (server, path, val) => new Promise((resolve, reject) => { | ||
7 | + const client = zookeeper.createClient(server); | ||
8 | + | ||
9 | + client.once('connected', function () { | ||
10 | + client.setData(path, new Buffer(val.toString()), function(err, data, stat) { | ||
11 | + if (err) { | ||
12 | + console.log('update path %s data error'); | ||
13 | + resolve(); | ||
14 | + } else { | ||
15 | + console.log('path %s data change to', path, val); | ||
16 | + resolve(true); | ||
17 | + } | ||
18 | + client.close(); | ||
19 | + }); | ||
20 | + }); | ||
21 | + | ||
22 | + client.connect(); | ||
23 | +}); |
@@ -5,15 +5,19 @@ import path from 'path'; | @@ -5,15 +5,19 @@ import path from 'path'; | ||
5 | const env = process.env.NODE_ENV || 'development'; | 5 | const env = process.env.NODE_ENV || 'development'; |
6 | 6 | ||
7 | const defaults = { | 7 | const defaults = { |
8 | - port: 9000, | ||
9 | - buildDir: path.normalize(__dirname + '/../packages/'), | ||
10 | - dbDir: path.normalize(__dirname + '/../db') | 8 | + port: 9000, |
9 | + buildDir: path.normalize(__dirname + '/../packages/'), | ||
10 | + dbDir: path.normalize(__dirname + '/../db'), | ||
11 | + influxdb: { | ||
12 | + host: '54.222.219.223', | ||
13 | + port: 4444 | ||
14 | + } | ||
11 | }; | 15 | }; |
12 | 16 | ||
13 | const specific = { | 17 | const specific = { |
14 | - development: {}, | ||
15 | - test: {}, | ||
16 | - production: {} | 18 | + development: {}, |
19 | + test: {}, | ||
20 | + production: {}, | ||
17 | }; | 21 | }; |
18 | 22 | ||
19 | export default Object.assign(defaults, specific[env]); | 23 | export default Object.assign(defaults, specific[env]); |
-
Please register or login to post a comment