├── .gitignore ├── README.md ├── bin ├── docs ├── node-haproxy.js ├── node-haproxy.sh └── parseArgs.js ├── docs ├── api.html ├── custom.css ├── docco.css └── public │ ├── fonts │ ├── aller-bold.eot │ ├── aller-bold.ttf │ ├── aller-bold.woff │ ├── aller-light.eot │ ├── aller-light.ttf │ ├── aller-light.woff │ ├── roboto-black.eot │ ├── roboto-black.ttf │ └── roboto-black.woff │ └── stylesheets │ └── normalize.css ├── haproxy ├── haproxycfg.tmpl ├── node-haproxy.org ├── old ├── Dockerfile ├── README.md ├── docker │ ├── .bashrc │ ├── .gitconfig │ ├── .z │ ├── haproxy │ ├── scripts │ │ ├── git-completion.sh │ │ ├── git-prompt.sh │ │ └── z.sh │ ├── serf │ └── snapshot ├── etc-default-haproxy ├── etc-init.d-haproxy ├── server.js ├── test-api.js ├── test-serf.js └── testhap.js ├── package.js ├── package.json ├── src ├── Data.js ├── Db.js ├── HaproxyManager.js ├── HaproxyStats.js ├── api.js └── ipc-client.js └── test ├── ipc-server.js └── test-flic.js /.gitignore: -------------------------------------------------------------------------------- 1 | .tern-port 2 | node_modules 3 | !node_modules/node-static 4 | npm-debug.log 5 | .#* 6 | temp/* 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-haproxy 2 | -------- 3 | 4 | Set, modify and hot load haproxy configuration from node. 5 | 6 | Functionality and code lifted and adapted from 7 | [thalassa=aqueduct](https://github.com/PearsonEducation/thalassa-aqueduct). 8 | 9 | You can run this module standalone (`npm install -g node-haproxy; node-haproxy --ipc`) and 10 | communicate with it using ipc (`var ipcClient = require ('node-haproxy/src/ipc-client'`) 11 | or use the api directly (`var haproxy = require('node-haproxy'`). 12 | 13 | In both cases it will fire up a haproxy instance which you can query and modify programmatically. 14 | 15 | When modifying or adding/removing front and backends programmatically haproxy is 16 | reconfigured on the fly. Back and frontends are persisted between restarts of the node process. 17 | 18 | Changes are saved to a leveldb. 19 | 20 | When no options are passed in, the included haproxy executable (v1.5) is used. 21 | The leveldb and persisted data is stored by default in the module's folder. Which means 22 | that on updating or reinstalling the module the data is wiped. 23 | 24 | Locations of the leveldb, template haproxy.cfg file and persisted info file can 25 | be set using options, amongst some more settings relating to haproxy. 26 | 27 | Paraphrased from the original readme: 28 | 29 | ## HAProxy Fundamentals 30 | 31 | Node-haproxy does not try to obfuscate HAProxy, and it's important to know the fundamentals of how HAProxy works to understand node-haproxy. The API mirrors HAProxy's semantics. The [HAProxy documentation](http://cbonte.github.io/haproxy-dconv/configuration-1.4.html) contains a wealth of detailed information. 32 | 33 | 1. **Frontends** - A "frontend" section describes a set of listening sockets accepting client 34 | connections. 35 | 36 | 2. **Backends** - A "backend" section describes a set of servers to which the proxy will connect 37 | to forward incoming connections. 38 | 39 | 3. **Members/Servers** - node-haproxy calls the servers that *backends* route to "members". In other words, members of a backend pool of servers. 40 | 41 | 4. **Config file** - At startup, HAProxy loads a configuration file and never looks at that file again. node-haproxy manages this by re-templating a new config file and gracefully restarting the HAProxy process. 42 | 43 | 5. **Stats Socket** - a UNIX socket in stream mode that will return various statistics outputs and even allows some basic configuration tweaking like enabling and disabling existing backend members, setting weights, etc. 44 | 45 | ## Options 46 | 47 | ./node_modules/.bin/node-haproxy --help 48 | Options: 49 | --haproxySocketPath path to Haproxy socket file 50 | --haproxyPidPath path to Haproxy pid file 51 | --haproxyCfgPath generated Haproxy config location 52 | --templateFile template used to generate Haproxy config 53 | --persistence directory to save configuration 54 | --dbPath filesystem path for leveldb 55 | --sudo use sudo when starting haproxy 56 | --which path for haproxy, set to 'system' to look for it, otherwise the included haproxy (v1.5) is used 57 | --ipc start up ipc server 58 | 59 | The following functions can be invoked on the required node-haproxy module or 60 | using the ipc-client (`ipcClient(functionName, args`), which uses promises: 61 | Don't forget to call ipcClient.close() if you want to stop the node process.. 62 | 63 | Example of using the ipc-client: 64 | 65 | ipcClient('getFrontends', []) 66 | .when( 67 | function(result) { 68 | console.log("Frontends\n", result); 69 | return ipcClient('getBackends', []); 70 | }) 71 | .when( 72 | function(result) { 73 | console.log("Backends\n", result); 74 | ipcClient.close(); 75 | }, 76 | function(error) { 77 | console.log("Error\n", error); 78 | ipcClient.close(); 79 | } 80 | 81 | ); 82 | 83 | ### getFrontends() 84 | 85 | Returns Array of `frontend` objects for all of the frontends configured 86 | 87 | For example: 88 | 89 | [{ 90 | "id": "frontend/myapp", 91 | "_type": "frontend", 92 | "key": "myapp", 93 | "bind": "*:8080,*:80", 94 | "backend": "live", 95 | "mode": "http", 96 | "keepalive": "default", 97 | "rules": [{ 98 | "type": "header", 99 | "header": "host", 100 | "operation": "hdr_dom", 101 | "value": "staged.myapp.com", 102 | "backend": "staged" 103 | }], 104 | "natives": [] 105 | }] 106 | 107 | 108 | ### getFrontend(key) 109 | 110 | Gets a specific frontend by `key`. 111 | 112 | 113 | ### putFrontend(key, obj) 114 | 115 | Create or update a `frontend` by `key`. 116 | 117 | { 118 | "bind": "10.2.2.2:80,*:8080" // IP and ports to bind to, comma separated, host may be * 119 | , "backend": "foo" // the default backend to route to, it must be defined already 120 | , "mode": "tcp" // default: http, expects tcp|http 121 | , "keepalive": "close" // default: "default", expects default|close|server-close 122 | , "rules": [] // array of rules, see below 123 | , "natives": [] // array of strings of raw config USE SPARINGLY!! 124 | } 125 | 126 | ### deleteFrontend(key) 127 | 128 | Delete a specific frontend by `key`. 129 | 130 | 131 | ### getBackends() 132 | 133 | Returns Array of `backend` objects for all of the backends configured 134 | 135 | For example: 136 | 137 | [{ 138 | "id": "backend/live", 139 | "_type": "backend", 140 | "key": "live", 141 | "type": "dynamic", 142 | "name": "classroom-ui", 143 | "version": "1.0.0", 144 | "balance": "roundrobin", 145 | "host": null, 146 | "mode": "http", 147 | "members": [{ 148 | "name": "myapp", 149 | "version": "1.0.0", 150 | "host": "10.10.240.121", 151 | "port": 8080, 152 | "lastKnown": 1378762056885, 153 | "meta": { 154 | "hostname": "dev-use1b-pr-01-myapp-01x00x00-01", 155 | "pid": 17941, 156 | "registered": 1378740834616 157 | }, 158 | "id": "/myapp/1.0.0/10.10.240.121/8080" 159 | }, 160 | { 161 | "name": "myapp", 162 | "version": "1.0.0", 163 | "host": "10.10.240.80", 164 | "port": 8080, 165 | "lastKnown": 1378762060226, 166 | "meta": { 167 | "hostname": "dev-use1b-pr-01-myapp-01x00x00-02", 168 | "pid": 18020, 169 | "registered": 1378762079883 170 | }, 171 | "id": "/myapp/1.0.0/10.10.240.80/8080" 172 | }], 173 | "natives": [] 174 | }] 175 | 176 | 177 | ### getBackend(key) 178 | 179 | Gets a specific `backend` by `key`. 180 | 181 | 182 | ### putBackend(key, obj) 183 | 184 | Create or update a `backend` by `key`. 185 | 186 | { 187 | "type" : "dynamic|static" 188 | , "name" : "foo" // only required if type = dynamic 189 | , "version" : "1.0.0" // only required if type = dynamic 190 | , "balance" : "roundrobin|source" // defaults to roundrobin 191 | , "host" : "myapp.com" // default: undefined, if specified request to member will contain this host header 192 | , "health" : { // optional health check 193 | "method": "GET" // HTTP method 194 | , "uri": "/checkity-check" // URI to call 195 | , "httpVersion": "HTTP/1.1" // HTTP/1.0 or HTTP/1.1 `host` required if HTTP/1.1 196 | , "interval": 5000 // period to check, milliseconds 197 | } 198 | , "mode" : "http|tcp" // default: http 199 | , "natives": [] // array of strings of raw config USE SPARINGLY!! 200 | , "members" : [] // if type = dynamic this is dynamically populated based on role/version subscription 201 | // otherwise expects { host: '10.10.10.10', port: 8080} 202 | } 203 | 204 | 205 | ### deleteBackend(key) 206 | 207 | Delete a specific `backend` by `key`. 208 | 209 | 210 | 211 | ### updateBackend(key) 212 | 213 | Update a `backend`s `role` and `version` 214 | 215 | { 216 | "name": "myapp" 217 | , "version": "1.1.0" // version to route to 218 | } 219 | 220 | `name` is actually optional. You may also just send the `version`: 221 | 222 | { 223 | "version": "1.1.0" 224 | } 225 | 226 | 227 | ### getHaproxyConfig() 228 | 229 | Return the last know generated HAProxy config file contents that were written to the location of `opts.haproxyCfgPath`. 230 | 231 | 232 | global 233 | log 127.0.0.1 local0 234 | log 127.0.0.1 local1 notice 235 | daemon 236 | maxconn 4096 237 | user haproxy 238 | group haproxy 239 | stats socket /tmp/haproxy.status.sock user appuser level admin 240 | 241 | defaults 242 | log global 243 | option dontlognull 244 | option redispatch 245 | retries 3 246 | maxconn 2000 247 | timeout connect 5000ms 248 | timeout client 50000ms 249 | timeout server 50000ms 250 | 251 | listen stats :1988 252 | mode http 253 | stats enable 254 | stats uri / 255 | stats refresh 2s 256 | stats realm Haproxy\ Stats 257 | stats auth showme:showme 258 | 259 | 260 | frontend myapp 261 | bind *:8080,*:80 262 | mode http 263 | default_backend live 264 | option httplog 265 | option http-server-close 266 | option http-pretend-keepalive 267 | acl header_uv7vi hdr_dom(host) myapp-staged.com 268 | use_backend staged if header_uv7vi 269 | 270 | 271 | 272 | backend live 273 | mode http 274 | balance roundrobin 275 | server live_10.10.240.121:8080 10.10.240.121:8080 check inter 2000 276 | server live_10.10.240.80:8080 10.10.240.80:8080 check inter 2000 277 | 278 | backend staged 279 | mode http 280 | balance roundrobin 281 | server staged_10.10.240.174:8080 10.10.240.174:8080 check inter 2000 282 | server staged_10.10.240.206:8080 10.10.240.206:8080 check inter 2000 283 | 284 | 285 | #### Routing Rules 286 | 287 | There are currently 3 types of rules that can be applied to frontends: `path`, `url`, and `header`. 288 | 289 | Path rules support `path`, `path_beg`, and `path_reg` HAProxy operations 290 | 291 | { 292 | "type": "path" 293 | , "operation": "path|path_beg|path_reg" 294 | , "value": "favicon.ico|/ecxd/|^/article/[^/]*$" 295 | , "backend": "foo" // if rule is met, the backend to route the request to 296 | } 297 | 298 | 299 | Url rules support `url`, `url_beg`, and `url_reg` HAProxy operations 300 | 301 | { 302 | "type": "url" 303 | , "operation": "url|url_beg|url_reg" 304 | , "value": "/bar" // value for the operation 305 | , "backend": "bar" // if rule is met, the backend to route the request to 306 | } 307 | 308 | Header rules support `hdr_dom` with a entire value at this point 309 | 310 | { 311 | "type": "header" 312 | , "header": "host" // the name of the HTTP header 313 | , "operation": "hdr_dom" 314 | , "value": "baz.com" 315 | , "backend": "baz" // if rule is met, the backend to route the request to 316 | } 317 | 318 | #### Natives 319 | 320 | The natives property is an end around way to insert raw lines of config for front ends and backends. Use them sparingly but use them if you need them. 321 | 322 | 323 | ### TODO 324 | * thrown exceptions are are not handled in api.js and dependencies 325 | * remove health checks/roles/version leftover from Aqea 326 | -------------------------------------------------------------------------------- /bin/docs: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # node_modules/doccoh/bin/doccoh lib/couchapi.js -o ./www/docs -c ./www/docs/custom.css 3 | npm run-script docs 4 | -------------------------------------------------------------------------------- /bin/node-haproxy.js: -------------------------------------------------------------------------------- 1 | var api = require('../src/api'); 2 | var args = require('./parseArgs'); 3 | 4 | api(args); 5 | -------------------------------------------------------------------------------- /bin/node-haproxy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var api = require('../src/api'); 4 | var args = require('./parseArgs'); 5 | 6 | api(args); 7 | -------------------------------------------------------------------------------- /bin/parseArgs.js: -------------------------------------------------------------------------------- 1 | var optimist = require('optimist') 2 | .options({ 3 | haproxySocketPath: { 4 | describe: 'path to Haproxy socket file' 5 | }, 6 | haproxyPidPath: { 7 | describe: 'path to Haproxy pid file' 8 | }, 9 | haproxyCfgPath: { 10 | describe: 'generated Haproxy config location' 11 | }, 12 | templateFile: { 13 | describe: 'template used to generate Haproxy config' 14 | }, 15 | persistence: { 16 | describe: 'directory to save configuration' 17 | }, 18 | dbPath: { 19 | describe: 'filesystem path for leveldb' 20 | }, 21 | sudo: { 22 | describe: 'use sudo when starting haproxy' 23 | }, 24 | which: { 25 | describe: 'path for haproxy, set to \'system\' to look for it, otherwise the included haproxy (v1.5) is used'}, 26 | ipc: { 27 | describe: 'start up ipc server ' 28 | }, 29 | help: { 30 | alias: 'h' 31 | } 32 | }); 33 | 34 | var argv = optimist.argv; 35 | if (argv.h) { 36 | optimist.showHelp(); 37 | process.exit(0); 38 | } 39 | 40 | module.exports = argv; 41 | -------------------------------------------------------------------------------- /docs/api.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | api.js 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | 14 | 324 |
325 | 326 | 327 | -------------------------------------------------------------------------------- /docs/custom.css: -------------------------------------------------------------------------------- 1 | /*--------------------- Layout and Typography ----------------------------*/ 2 | body { 3 | font-family: 'Arial','Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; 4 | font-size: 15px; 5 | line-height: 22px; 6 | color: #252519; 7 | margin: 0; padding: 0; 8 | } 9 | a { 10 | color: #261a3b; 11 | } 12 | a:visited { 13 | color: #261a3b; 14 | } 15 | p { 16 | margin: 0 0 15px 0; 17 | } 18 | h1, h2, h3, h4, h5, h6 { 19 | margin: 0px 0 15px 0; 20 | } 21 | h1 { 22 | margin-top: 40px; 23 | } 24 | hr { 25 | border: 0 none; 26 | border-top: 1px solid #e5e5ee; 27 | height: 1px; 28 | margin: 20px 0; 29 | } 30 | #container { 31 | position: relative; 32 | } 33 | #background { 34 | position: fixed; 35 | top: 0; left: 525px; right: 0; bottom: 0; 36 | background: #f5f5ff; 37 | border-left: 1px solid #e5e5ee; 38 | z-index: -1; 39 | } 40 | #jump_to, #jump_page { 41 | background: white; 42 | -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; 43 | -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; 44 | font: 10px Arial; 45 | text-transform: uppercase; 46 | cursor: pointer; 47 | text-align: right; 48 | } 49 | #jump_to, #jump_wrapper { 50 | position: fixed; 51 | right: 0; top: 0; 52 | padding: 5px 10px; 53 | } 54 | #jump_wrapper { 55 | padding: 0; 56 | display: none; 57 | } 58 | #jump_to:hover #jump_wrapper { 59 | display: block; 60 | } 61 | #jump_page { 62 | padding: 5px 0 3px; 63 | margin: 0 0 25px 25px; 64 | } 65 | #jump_page .source { 66 | display: block; 67 | padding: 5px 10px; 68 | text-decoration: none; 69 | border-top: 1px solid #eee; 70 | } 71 | #jump_page .source:hover { 72 | background: #f5f5ff; 73 | } 74 | #jump_page .source:first-child { 75 | } 76 | table td { 77 | border: 0; 78 | outline: 0; 79 | } 80 | td.docs, th.docs { 81 | max-width: 450px; 82 | min-width: 450px; 83 | min-height: 5px; 84 | padding: 10px 25px 1px 50px; 85 | overflow-x: hidden; 86 | vertical-align: top; 87 | text-align: left; 88 | } 89 | .docs pre { 90 | margin: 15px 0 15px; 91 | padding-left: 15px; 92 | } 93 | .docs p tt, .docs p code { 94 | background: #f8f8ff; 95 | border: 1px solid #dedede; 96 | font-size: 12px; 97 | padding: 0 0.2em; 98 | } 99 | .pilwrap { 100 | position: relative; 101 | } 102 | .pilcrow { 103 | font: 12px Arial; 104 | text-decoration: none; 105 | color: #454545; 106 | position: absolute; 107 | top: 3px; left: -20px; 108 | padding: 1px 2px; 109 | opacity: 0; 110 | -webkit-transition: opacity 0.2s linear; 111 | } 112 | td.docs:hover .pilcrow { 113 | opacity: 1; 114 | } 115 | td.code, th.code { 116 | padding: 14px 15px 16px 25px; 117 | width: 100%; 118 | vertical-align: top; 119 | background: #f5f5ff; 120 | border-left: 1px solid #e5e5ee; 121 | } 122 | pre, tt, code { 123 | font-size: 12px; line-height: 18px; 124 | font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; 125 | margin: 0; padding: 0; 126 | } 127 | 128 | 129 | /*---------------------- Syntax Highlighting -----------------------------*/ 130 | td.linenos { background-color: #f0f0f0; padding-right: 10px; } 131 | span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } 132 | body .hll { background-color: #ffffcc } 133 | body .c { color: #408080; font-style: italic } /* Comment */ 134 | body .err { border: 1px solid #FF0000 } /* Error */ 135 | body .k { color: #954121 } /* Keyword */ 136 | body .o { color: #666666 } /* Operator */ 137 | body .cm { color: #408080; font-style: italic } /* Comment.Multiline */ 138 | body .cp { color: #BC7A00 } /* Comment.Preproc */ 139 | body .c1 { color: #408080; font-style: italic } /* Comment.Single */ 140 | body .cs { color: #408080; font-style: italic } /* Comment.Special */ 141 | body .gd { color: #A00000 } /* Generic.Deleted */ 142 | body .ge { font-style: italic } /* Generic.Emph */ 143 | body .gr { color: #FF0000 } /* Generic.Error */ 144 | body .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 145 | body .gi { color: #00A000 } /* Generic.Inserted */ 146 | body .go { color: #808080 } /* Generic.Output */ 147 | body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 148 | body .gs { font-weight: bold } /* Generic.Strong */ 149 | body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 150 | body .gt { color: #0040D0 } /* Generic.Traceback */ 151 | body .kc { color: #954121 } /* Keyword.Constant */ 152 | body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */ 153 | body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */ 154 | body .kp { color: #954121 } /* Keyword.Pseudo */ 155 | body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */ 156 | body .kt { color: #B00040 } /* Keyword.Type */ 157 | body .m { color: #666666 } /* Literal.Number */ 158 | body .s { color: #219161 } /* Literal.String */ 159 | body .na { color: #7D9029 } /* Name.Attribute */ 160 | body .nb { color: #954121 } /* Name.Builtin */ 161 | body .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 162 | body .no { color: #880000 } /* Name.Constant */ 163 | body .nd { color: #AA22FF } /* Name.Decorator */ 164 | body .ni { color: #999999; font-weight: bold } /* Name.Entity */ 165 | body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 166 | body .nf { color: #0000FF } /* Name.Function */ 167 | body .nl { color: #A0A000 } /* Name.Label */ 168 | body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 169 | body .nt { color: #954121; font-weight: bold } /* Name.Tag */ 170 | body .nv { color: #19469D } /* Name.Variable */ 171 | body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 172 | body .w { color: #bbbbbb } /* Text.Whitespace */ 173 | body .mf { color: #666666 } /* Literal.Number.Float */ 174 | body .mh { color: #666666 } /* Literal.Number.Hex */ 175 | body .mi { color: #666666 } /* Literal.Number.Integer */ 176 | body .mo { color: #666666 } /* Literal.Number.Oct */ 177 | body .sb { color: #219161 } /* Literal.String.Backtick */ 178 | body .sc { color: #219161 } /* Literal.String.Char */ 179 | body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */ 180 | body .s2 { color: #219161 } /* Literal.String.Double */ 181 | body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 182 | body .sh { color: #219161 } /* Literal.String.Heredoc */ 183 | body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 184 | body .sx { color: #954121 } /* Literal.String.Other */ 185 | body .sr { color: #BB6688 } /* Literal.String.Regex */ 186 | body .s1 { color: #219161 } /* Literal.String.Single */ 187 | body .ss { color: #19469D } /* Literal.String.Symbol */ 188 | body .bp { color: #954121 } /* Name.Builtin.Pseudo */ 189 | body .vc { color: #19469D } /* Name.Variable.Class */ 190 | body .vg { color: #19469D } /* Name.Variable.Global */ 191 | body .vi { color: #19469D } /* Name.Variable.Instance */ 192 | body .il { color: #666666 } /* Literal.Number.Integer.Long */ 193 | /*---------------------- Syntax Highlighting -----------------------------*/ 194 | td.linenos { background-color: #f0f0f0; padding-right: 10px; } 195 | span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } 196 | /* 197 | 198 | github.com style (c) Vasily Polovnyov 199 | 200 | */ 201 | 202 | pre code { 203 | display: block; padding: 0.5em; 204 | color: #000; 205 | background: #f8f8ff 206 | } 207 | 208 | pre .comment, 209 | pre .template_comment, 210 | pre .diff .header, 211 | pre .javadoc { 212 | color: #408080; 213 | font-style: italic 214 | } 215 | 216 | pre .keyword, 217 | pre .assignment, 218 | pre .literal, 219 | pre .css .rule .keyword, 220 | pre .winutils, 221 | pre .javascript .title, 222 | pre .lisp .title, 223 | pre .subst { 224 | color: #954121; 225 | /*font-weight: bold*/ 226 | } 227 | 228 | pre .number, 229 | pre .hexcolor { 230 | color: #40a070 231 | } 232 | 233 | pre .string, 234 | pre .tag .value, 235 | pre .phpdoc, 236 | pre .tex .formula { 237 | color: #219161; 238 | } 239 | 240 | pre .title, 241 | pre .id { 242 | color: #19469D; 243 | } 244 | pre .params { 245 | color: #00F; 246 | } 247 | 248 | pre .javascript .title, 249 | pre .lisp .title, 250 | pre .subst { 251 | font-weight: normal 252 | } 253 | 254 | pre .class .title, 255 | pre .haskell .label, 256 | pre .tex .command { 257 | color: #458; 258 | font-weight: bold 259 | } 260 | 261 | pre .tag, 262 | pre .tag .title, 263 | pre .rules .property, 264 | pre .django .tag .keyword { 265 | color: #000080; 266 | font-weight: normal 267 | } 268 | 269 | pre .attribute, 270 | pre .variable, 271 | pre .instancevar, 272 | pre .lisp .body { 273 | color: #008080 274 | } 275 | 276 | pre .regexp { 277 | color: #B68 278 | } 279 | 280 | pre .class { 281 | color: #458; 282 | font-weight: bold 283 | } 284 | 285 | pre .symbol, 286 | pre .ruby .symbol .string, 287 | pre .ruby .symbol .keyword, 288 | pre .ruby .symbol .keymethods, 289 | pre .lisp .keyword, 290 | pre .tex .special, 291 | pre .input_number { 292 | color: #990073 293 | } 294 | 295 | pre .builtin, 296 | pre .constructor, 297 | pre .built_in, 298 | pre .lisp .title { 299 | color: #0086b3 300 | } 301 | 302 | pre .preprocessor, 303 | pre .pi, 304 | pre .doctype, 305 | pre .shebang, 306 | pre .cdata { 307 | color: #999; 308 | font-weight: bold 309 | } 310 | 311 | pre .deletion { 312 | background: #fdd 313 | } 314 | 315 | pre .addition { 316 | background: #dfd 317 | } 318 | 319 | pre .diff .change { 320 | background: #0086b3 321 | } 322 | 323 | pre .chunk { 324 | color: #aaa 325 | } 326 | 327 | pre .tex .formula { 328 | opacity: 0.5; 329 | } 330 | -------------------------------------------------------------------------------- /docs/docco.css: -------------------------------------------------------------------------------- 1 | /*--------------------- Typography ----------------------------*/ 2 | 3 | @font-face { 4 | font-family: 'aller-light'; 5 | src: url('public/fonts/aller-light.eot'); 6 | src: url('public/fonts/aller-light.eot?#iefix') format('embedded-opentype'), 7 | url('public/fonts/aller-light.woff') format('woff'), 8 | url('public/fonts/aller-light.ttf') format('truetype'); 9 | font-weight: normal; 10 | font-style: normal; 11 | } 12 | 13 | @font-face { 14 | font-family: 'aller-bold'; 15 | src: url('public/fonts/aller-bold.eot'); 16 | src: url('public/fonts/aller-bold.eot?#iefix') format('embedded-opentype'), 17 | url('public/fonts/aller-bold.woff') format('woff'), 18 | url('public/fonts/aller-bold.ttf') format('truetype'); 19 | font-weight: normal; 20 | font-style: normal; 21 | } 22 | 23 | @font-face { 24 | font-family: 'roboto-black'; 25 | src: url('public/fonts/roboto-black.eot'); 26 | src: url('public/fonts/roboto-black.eot?#iefix') format('embedded-opentype'), 27 | url('public/fonts/roboto-black.woff') format('woff'), 28 | url('public/fonts/roboto-black.ttf') format('truetype'); 29 | font-weight: normal; 30 | font-style: normal; 31 | } 32 | 33 | /*--------------------- Layout ----------------------------*/ 34 | html { height: 100%; } 35 | body { 36 | font-family: "aller-light"; 37 | font-size: 14px; 38 | line-height: 18px; 39 | color: #30404f; 40 | margin: 0; padding: 0; 41 | height:100%; 42 | } 43 | #container { min-height: 100%; } 44 | 45 | a { 46 | color: #000; 47 | } 48 | 49 | b, strong { 50 | font-weight: normal; 51 | font-family: "aller-bold"; 52 | } 53 | 54 | p { 55 | margin: 15px 0 0px; 56 | } 57 | .annotation ul, .annotation ol { 58 | margin: 25px 0; 59 | } 60 | .annotation ul li, .annotation ol li { 61 | font-size: 14px; 62 | line-height: 18px; 63 | margin: 10px 0; 64 | } 65 | 66 | h1, h2, h3, h4, h5, h6 { 67 | color: #112233; 68 | line-height: 1em; 69 | font-weight: normal; 70 | font-family: "roboto-black"; 71 | text-transform: uppercase; 72 | margin: 30px 0 15px 0; 73 | } 74 | 75 | h1 { 76 | margin-top: 40px; 77 | } 78 | h2 { 79 | font-size: 1.26em; 80 | } 81 | 82 | hr { 83 | border: 0; 84 | background: 1px #ddd; 85 | height: 1px; 86 | margin: 20px 0; 87 | } 88 | 89 | pre, tt, code { 90 | font-size: 12px; line-height: 16px; 91 | font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; 92 | margin: 0; padding: 0; 93 | } 94 | .annotation pre { 95 | display: block; 96 | margin: 0; 97 | padding: 7px 10px; 98 | background: #fcfcfc; 99 | -moz-box-shadow: inset 0 0 10px rgba(0,0,0,0.1); 100 | -webkit-box-shadow: inset 0 0 10px rgba(0,0,0,0.1); 101 | box-shadow: inset 0 0 10px rgba(0,0,0,0.1); 102 | overflow-x: auto; 103 | } 104 | .annotation pre code { 105 | border: 0; 106 | padding: 0; 107 | background: transparent; 108 | } 109 | 110 | 111 | blockquote { 112 | border-left: 5px solid #ccc; 113 | margin: 0; 114 | padding: 1px 0 1px 1em; 115 | } 116 | .sections blockquote p { 117 | font-family: Menlo, Consolas, Monaco, monospace; 118 | font-size: 12px; line-height: 16px; 119 | color: #999; 120 | margin: 10px 0 0; 121 | white-space: pre-wrap; 122 | } 123 | 124 | ul.sections { 125 | list-style: none; 126 | padding:0 0 5px 0;; 127 | margin:0; 128 | } 129 | 130 | /* 131 | Force border-box so that % widths fit the parent 132 | container without overlap because of margin/padding. 133 | 134 | More Info : http://www.quirksmode.org/css/box.html 135 | */ 136 | ul.sections > li > div { 137 | -moz-box-sizing: border-box; /* firefox */ 138 | -ms-box-sizing: border-box; /* ie */ 139 | -webkit-box-sizing: border-box; /* webkit */ 140 | -khtml-box-sizing: border-box; /* konqueror */ 141 | box-sizing: border-box; /* css3 */ 142 | } 143 | 144 | 145 | /*---------------------- Jump Page -----------------------------*/ 146 | #jump_to, #jump_page { 147 | margin: 0; 148 | background: white; 149 | -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; 150 | -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; 151 | font: 16px Arial; 152 | cursor: pointer; 153 | text-align: right; 154 | list-style: none; 155 | } 156 | 157 | #jump_to a { 158 | text-decoration: none; 159 | } 160 | 161 | #jump_to a.large { 162 | display: none; 163 | } 164 | #jump_to a.small { 165 | font-size: 22px; 166 | font-weight: bold; 167 | color: #676767; 168 | } 169 | 170 | #jump_to, #jump_wrapper { 171 | position: fixed; 172 | right: 0; top: 0; 173 | padding: 10px 15px; 174 | margin:0; 175 | } 176 | 177 | #jump_wrapper { 178 | display: none; 179 | padding:0; 180 | } 181 | 182 | #jump_to:hover #jump_wrapper { 183 | display: block; 184 | } 185 | 186 | #jump_page_wrapper{ 187 | position: fixed; 188 | right: 0; 189 | top: 0; 190 | bottom: 0; 191 | } 192 | 193 | #jump_page { 194 | padding: 5px 0 3px; 195 | margin: 0 0 25px 25px; 196 | max-height: 100%; 197 | overflow: auto; 198 | } 199 | 200 | #jump_page .source { 201 | display: block; 202 | padding: 15px; 203 | text-decoration: none; 204 | border-top: 1px solid #eee; 205 | } 206 | 207 | #jump_page .source:hover { 208 | background: #f5f5ff; 209 | } 210 | 211 | #jump_page .source:first-child { 212 | } 213 | 214 | /*---------------------- Low resolutions (> 320px) ---------------------*/ 215 | @media only screen and (min-width: 320px) { 216 | .pilwrap { display: none; } 217 | 218 | ul.sections > li > div { 219 | display: block; 220 | padding:5px 10px 0 10px; 221 | } 222 | 223 | ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol { 224 | padding-left: 30px; 225 | } 226 | 227 | ul.sections > li > div.content { 228 | overflow-x:auto; 229 | -webkit-box-shadow: inset 0 0 5px #e5e5ee; 230 | box-shadow: inset 0 0 5px #e5e5ee; 231 | border: 1px solid #dedede; 232 | margin:5px 10px 5px 10px; 233 | padding-bottom: 5px; 234 | } 235 | 236 | ul.sections > li > div.annotation pre { 237 | margin: 7px 0 7px; 238 | padding-left: 15px; 239 | } 240 | 241 | ul.sections > li > div.annotation p tt, .annotation code { 242 | background: #f8f8ff; 243 | border: 1px solid #dedede; 244 | font-size: 12px; 245 | padding: 0 0.2em; 246 | } 247 | } 248 | 249 | /*---------------------- (> 481px) ---------------------*/ 250 | @media only screen and (min-width: 481px) { 251 | #container { 252 | position: relative; 253 | } 254 | body { 255 | background-color: #F5F5FF; 256 | font-size: 15px; 257 | line-height: 21px; 258 | } 259 | pre, tt, code { 260 | line-height: 18px; 261 | } 262 | p, ul, ol { 263 | margin: 0 0 15px; 264 | } 265 | 266 | 267 | #jump_to { 268 | padding: 5px 10px; 269 | } 270 | #jump_wrapper { 271 | padding: 0; 272 | } 273 | #jump_to, #jump_page { 274 | font: 10px Arial; 275 | text-transform: uppercase; 276 | } 277 | #jump_page .source { 278 | padding: 5px 10px; 279 | } 280 | #jump_to a.large { 281 | display: inline-block; 282 | } 283 | #jump_to a.small { 284 | display: none; 285 | } 286 | 287 | 288 | 289 | #background { 290 | position: absolute; 291 | top: 0; bottom: 0; 292 | width: 350px; 293 | background: #fff; 294 | border-right: 1px solid #e5e5ee; 295 | z-index: -1; 296 | } 297 | 298 | ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol { 299 | padding-left: 40px; 300 | } 301 | 302 | ul.sections > li { 303 | white-space: nowrap; 304 | } 305 | 306 | ul.sections > li > div { 307 | display: inline-block; 308 | } 309 | 310 | ul.sections > li > div.annotation { 311 | max-width: 350px; 312 | min-width: 350px; 313 | min-height: 5px; 314 | padding: 13px; 315 | overflow-x: hidden; 316 | white-space: normal; 317 | vertical-align: top; 318 | text-align: left; 319 | } 320 | ul.sections > li > div.annotation pre { 321 | margin: 15px 0 15px; 322 | padding-left: 15px; 323 | } 324 | 325 | ul.sections > li > div.content { 326 | padding: 13px; 327 | vertical-align: top; 328 | border: none; 329 | -webkit-box-shadow: none; 330 | box-shadow: none; 331 | } 332 | 333 | .pilwrap { 334 | position: relative; 335 | display: inline; 336 | } 337 | 338 | .pilcrow { 339 | font: 12px Arial; 340 | text-decoration: none; 341 | color: #454545; 342 | position: absolute; 343 | top: 3px; left: -20px; 344 | padding: 1px 2px; 345 | opacity: 0; 346 | -webkit-transition: opacity 0.2s linear; 347 | } 348 | .for-h1 .pilcrow { 349 | top: 47px; 350 | } 351 | .for-h2 .pilcrow, .for-h3 .pilcrow, .for-h4 .pilcrow { 352 | top: 35px; 353 | } 354 | 355 | ul.sections > li > div.annotation:hover .pilcrow { 356 | opacity: 1; 357 | } 358 | } 359 | 360 | /*---------------------- (> 1025px) ---------------------*/ 361 | @media only screen and (min-width: 1025px) { 362 | 363 | body { 364 | font-size: 16px; 365 | line-height: 24px; 366 | } 367 | 368 | #background { 369 | width: 525px; 370 | } 371 | ul.sections > li > div.annotation { 372 | max-width: 525px; 373 | min-width: 525px; 374 | padding: 10px 25px 1px 50px; 375 | } 376 | ul.sections > li > div.content { 377 | padding: 9px 15px 16px 25px; 378 | } 379 | } 380 | 381 | /*---------------------- Syntax Highlighting -----------------------------*/ 382 | 383 | td.linenos { background-color: #f0f0f0; padding-right: 10px; } 384 | span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } 385 | /* 386 | 387 | github.com style (c) Vasily Polovnyov 388 | 389 | */ 390 | 391 | pre code { 392 | display: block; padding: 0.5em; 393 | color: #000; 394 | background: #f8f8ff 395 | } 396 | 397 | pre .hljs-comment, 398 | pre .hljs-template_comment, 399 | pre .hljs-diff .hljs-header, 400 | pre .hljs-javadoc { 401 | color: #408080; 402 | font-style: italic 403 | } 404 | 405 | pre .hljs-keyword, 406 | pre .hljs-assignment, 407 | pre .hljs-literal, 408 | pre .hljs-css .hljs-rule .hljs-keyword, 409 | pre .hljs-winutils, 410 | pre .hljs-javascript .hljs-title, 411 | pre .hljs-lisp .hljs-title, 412 | pre .hljs-subst { 413 | color: #954121; 414 | /*font-weight: bold*/ 415 | } 416 | 417 | pre .hljs-number, 418 | pre .hljs-hexcolor { 419 | color: #40a070 420 | } 421 | 422 | pre .hljs-string, 423 | pre .hljs-tag .hljs-value, 424 | pre .hljs-phpdoc, 425 | pre .hljs-tex .hljs-formula { 426 | color: #219161; 427 | } 428 | 429 | pre .hljs-title, 430 | pre .hljs-id { 431 | color: #19469D; 432 | } 433 | pre .hljs-params { 434 | color: #00F; 435 | } 436 | 437 | pre .hljs-javascript .hljs-title, 438 | pre .hljs-lisp .hljs-title, 439 | pre .hljs-subst { 440 | font-weight: normal 441 | } 442 | 443 | pre .hljs-class .hljs-title, 444 | pre .hljs-haskell .hljs-label, 445 | pre .hljs-tex .hljs-command { 446 | color: #458; 447 | font-weight: bold 448 | } 449 | 450 | pre .hljs-tag, 451 | pre .hljs-tag .hljs-title, 452 | pre .hljs-rules .hljs-property, 453 | pre .hljs-django .hljs-tag .hljs-keyword { 454 | color: #000080; 455 | font-weight: normal 456 | } 457 | 458 | pre .hljs-attribute, 459 | pre .hljs-variable, 460 | pre .hljs-instancevar, 461 | pre .hljs-lisp .hljs-body { 462 | color: #008080 463 | } 464 | 465 | pre .hljs-regexp { 466 | color: #B68 467 | } 468 | 469 | pre .hljs-class { 470 | color: #458; 471 | font-weight: bold 472 | } 473 | 474 | pre .hljs-symbol, 475 | pre .hljs-ruby .hljs-symbol .hljs-string, 476 | pre .hljs-ruby .hljs-symbol .hljs-keyword, 477 | pre .hljs-ruby .hljs-symbol .hljs-keymethods, 478 | pre .hljs-lisp .hljs-keyword, 479 | pre .hljs-tex .hljs-special, 480 | pre .hljs-input_number { 481 | color: #990073 482 | } 483 | 484 | pre .hljs-builtin, 485 | pre .hljs-constructor, 486 | pre .hljs-built_in, 487 | pre .hljs-lisp .hljs-title { 488 | color: #0086b3 489 | } 490 | 491 | pre .hljs-preprocessor, 492 | pre .hljs-pi, 493 | pre .hljs-doctype, 494 | pre .hljs-shebang, 495 | pre .hljs-cdata { 496 | color: #999; 497 | font-weight: bold 498 | } 499 | 500 | pre .hljs-deletion { 501 | background: #fdd 502 | } 503 | 504 | pre .hljs-addition { 505 | background: #dfd 506 | } 507 | 508 | pre .hljs-diff .hljs-change { 509 | background: #0086b3 510 | } 511 | 512 | pre .hljs-chunk { 513 | color: #aaa 514 | } 515 | 516 | pre .hljs-tex .hljs-formula { 517 | opacity: 0.5; 518 | } 519 | -------------------------------------------------------------------------------- /docs/public/fonts/aller-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Michieljoris/node-haproxy/54d2033461051eca7c445e4599f69db72f9ee004/docs/public/fonts/aller-bold.eot -------------------------------------------------------------------------------- /docs/public/fonts/aller-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Michieljoris/node-haproxy/54d2033461051eca7c445e4599f69db72f9ee004/docs/public/fonts/aller-bold.ttf -------------------------------------------------------------------------------- /docs/public/fonts/aller-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Michieljoris/node-haproxy/54d2033461051eca7c445e4599f69db72f9ee004/docs/public/fonts/aller-bold.woff -------------------------------------------------------------------------------- /docs/public/fonts/aller-light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Michieljoris/node-haproxy/54d2033461051eca7c445e4599f69db72f9ee004/docs/public/fonts/aller-light.eot -------------------------------------------------------------------------------- /docs/public/fonts/aller-light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Michieljoris/node-haproxy/54d2033461051eca7c445e4599f69db72f9ee004/docs/public/fonts/aller-light.ttf -------------------------------------------------------------------------------- /docs/public/fonts/aller-light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Michieljoris/node-haproxy/54d2033461051eca7c445e4599f69db72f9ee004/docs/public/fonts/aller-light.woff -------------------------------------------------------------------------------- /docs/public/fonts/roboto-black.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Michieljoris/node-haproxy/54d2033461051eca7c445e4599f69db72f9ee004/docs/public/fonts/roboto-black.eot -------------------------------------------------------------------------------- /docs/public/fonts/roboto-black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Michieljoris/node-haproxy/54d2033461051eca7c445e4599f69db72f9ee004/docs/public/fonts/roboto-black.ttf -------------------------------------------------------------------------------- /docs/public/fonts/roboto-black.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Michieljoris/node-haproxy/54d2033461051eca7c445e4599f69db72f9ee004/docs/public/fonts/roboto-black.woff -------------------------------------------------------------------------------- /docs/public/stylesheets/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v2.0.1 | MIT License | git.io/normalize */ 2 | 3 | /* ========================================================================== 4 | HTML5 display definitions 5 | ========================================================================== */ 6 | 7 | /* 8 | * Corrects `block` display not defined in IE 8/9. 9 | */ 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | nav, 20 | section, 21 | summary { 22 | display: block; 23 | } 24 | 25 | /* 26 | * Corrects `inline-block` display not defined in IE 8/9. 27 | */ 28 | 29 | audio, 30 | canvas, 31 | video { 32 | display: inline-block; 33 | } 34 | 35 | /* 36 | * Prevents modern browsers from displaying `audio` without controls. 37 | * Remove excess height in iOS 5 devices. 38 | */ 39 | 40 | audio:not([controls]) { 41 | display: none; 42 | height: 0; 43 | } 44 | 45 | /* 46 | * Addresses styling for `hidden` attribute not present in IE 8/9. 47 | */ 48 | 49 | [hidden] { 50 | display: none; 51 | } 52 | 53 | /* ========================================================================== 54 | Base 55 | ========================================================================== */ 56 | 57 | /* 58 | * 1. Sets default font family to sans-serif. 59 | * 2. Prevents iOS text size adjust after orientation change, without disabling 60 | * user zoom. 61 | */ 62 | 63 | html { 64 | font-family: sans-serif; /* 1 */ 65 | -webkit-text-size-adjust: 100%; /* 2 */ 66 | -ms-text-size-adjust: 100%; /* 2 */ 67 | } 68 | 69 | /* 70 | * Removes default margin. 71 | */ 72 | 73 | body { 74 | margin: 0; 75 | } 76 | 77 | /* ========================================================================== 78 | Links 79 | ========================================================================== */ 80 | 81 | /* 82 | * Addresses `outline` inconsistency between Chrome and other browsers. 83 | */ 84 | 85 | a:focus { 86 | outline: thin dotted; 87 | } 88 | 89 | /* 90 | * Improves readability when focused and also mouse hovered in all browsers. 91 | */ 92 | 93 | a:active, 94 | a:hover { 95 | outline: 0; 96 | } 97 | 98 | /* ========================================================================== 99 | Typography 100 | ========================================================================== */ 101 | 102 | /* 103 | * Addresses `h1` font sizes within `section` and `article` in Firefox 4+, 104 | * Safari 5, and Chrome. 105 | */ 106 | 107 | h1 { 108 | font-size: 2em; 109 | } 110 | 111 | /* 112 | * Addresses styling not present in IE 8/9, Safari 5, and Chrome. 113 | */ 114 | 115 | abbr[title] { 116 | border-bottom: 1px dotted; 117 | } 118 | 119 | /* 120 | * Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome. 121 | */ 122 | 123 | b, 124 | strong { 125 | font-weight: bold; 126 | } 127 | 128 | /* 129 | * Addresses styling not present in Safari 5 and Chrome. 130 | */ 131 | 132 | dfn { 133 | font-style: italic; 134 | } 135 | 136 | /* 137 | * Addresses styling not present in IE 8/9. 138 | */ 139 | 140 | mark { 141 | background: #ff0; 142 | color: #000; 143 | } 144 | 145 | 146 | /* 147 | * Corrects font family set oddly in Safari 5 and Chrome. 148 | */ 149 | 150 | code, 151 | kbd, 152 | pre, 153 | samp { 154 | font-family: monospace, serif; 155 | font-size: 1em; 156 | } 157 | 158 | /* 159 | * Improves readability of pre-formatted text in all browsers. 160 | */ 161 | 162 | pre { 163 | white-space: pre; 164 | white-space: pre-wrap; 165 | word-wrap: break-word; 166 | } 167 | 168 | /* 169 | * Sets consistent quote types. 170 | */ 171 | 172 | q { 173 | quotes: "\201C" "\201D" "\2018" "\2019"; 174 | } 175 | 176 | /* 177 | * Addresses inconsistent and variable font size in all browsers. 178 | */ 179 | 180 | small { 181 | font-size: 80%; 182 | } 183 | 184 | /* 185 | * Prevents `sub` and `sup` affecting `line-height` in all browsers. 186 | */ 187 | 188 | sub, 189 | sup { 190 | font-size: 75%; 191 | line-height: 0; 192 | position: relative; 193 | vertical-align: baseline; 194 | } 195 | 196 | sup { 197 | top: -0.5em; 198 | } 199 | 200 | sub { 201 | bottom: -0.25em; 202 | } 203 | 204 | /* ========================================================================== 205 | Embedded content 206 | ========================================================================== */ 207 | 208 | /* 209 | * Removes border when inside `a` element in IE 8/9. 210 | */ 211 | 212 | img { 213 | border: 0; 214 | } 215 | 216 | /* 217 | * Corrects overflow displayed oddly in IE 9. 218 | */ 219 | 220 | svg:not(:root) { 221 | overflow: hidden; 222 | } 223 | 224 | /* ========================================================================== 225 | Figures 226 | ========================================================================== */ 227 | 228 | /* 229 | * Addresses margin not present in IE 8/9 and Safari 5. 230 | */ 231 | 232 | figure { 233 | margin: 0; 234 | } 235 | 236 | /* ========================================================================== 237 | Forms 238 | ========================================================================== */ 239 | 240 | /* 241 | * Define consistent border, margin, and padding. 242 | */ 243 | 244 | fieldset { 245 | border: 1px solid #c0c0c0; 246 | margin: 0 2px; 247 | padding: 0.35em 0.625em 0.75em; 248 | } 249 | 250 | /* 251 | * 1. Corrects color not being inherited in IE 8/9. 252 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 253 | */ 254 | 255 | legend { 256 | border: 0; /* 1 */ 257 | padding: 0; /* 2 */ 258 | } 259 | 260 | /* 261 | * 1. Corrects font family not being inherited in all browsers. 262 | * 2. Corrects font size not being inherited in all browsers. 263 | * 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome 264 | */ 265 | 266 | button, 267 | input, 268 | select, 269 | textarea { 270 | font-family: inherit; /* 1 */ 271 | font-size: 100%; /* 2 */ 272 | margin: 0; /* 3 */ 273 | } 274 | 275 | /* 276 | * Addresses Firefox 4+ setting `line-height` on `input` using `!important` in 277 | * the UA stylesheet. 278 | */ 279 | 280 | button, 281 | input { 282 | line-height: normal; 283 | } 284 | 285 | /* 286 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 287 | * and `video` controls. 288 | * 2. Corrects inability to style clickable `input` types in iOS. 289 | * 3. Improves usability and consistency of cursor style between image-type 290 | * `input` and others. 291 | */ 292 | 293 | button, 294 | html input[type="button"], /* 1 */ 295 | input[type="reset"], 296 | input[type="submit"] { 297 | -webkit-appearance: button; /* 2 */ 298 | cursor: pointer; /* 3 */ 299 | } 300 | 301 | /* 302 | * Re-set default cursor for disabled elements. 303 | */ 304 | 305 | button[disabled], 306 | input[disabled] { 307 | cursor: default; 308 | } 309 | 310 | /* 311 | * 1. Addresses box sizing set to `content-box` in IE 8/9. 312 | * 2. Removes excess padding in IE 8/9. 313 | */ 314 | 315 | input[type="checkbox"], 316 | input[type="radio"] { 317 | box-sizing: border-box; /* 1 */ 318 | padding: 0; /* 2 */ 319 | } 320 | 321 | /* 322 | * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome. 323 | * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome 324 | * (include `-moz` to future-proof). 325 | */ 326 | 327 | input[type="search"] { 328 | -webkit-appearance: textfield; /* 1 */ 329 | -moz-box-sizing: content-box; 330 | -webkit-box-sizing: content-box; /* 2 */ 331 | box-sizing: content-box; 332 | } 333 | 334 | /* 335 | * Removes inner padding and search cancel button in Safari 5 and Chrome 336 | * on OS X. 337 | */ 338 | 339 | input[type="search"]::-webkit-search-cancel-button, 340 | input[type="search"]::-webkit-search-decoration { 341 | -webkit-appearance: none; 342 | } 343 | 344 | /* 345 | * Removes inner padding and border in Firefox 4+. 346 | */ 347 | 348 | button::-moz-focus-inner, 349 | input::-moz-focus-inner { 350 | border: 0; 351 | padding: 0; 352 | } 353 | 354 | /* 355 | * 1. Removes default vertical scrollbar in IE 8/9. 356 | * 2. Improves readability and alignment in all browsers. 357 | */ 358 | 359 | textarea { 360 | overflow: auto; /* 1 */ 361 | vertical-align: top; /* 2 */ 362 | } 363 | 364 | /* ========================================================================== 365 | Tables 366 | ========================================================================== */ 367 | 368 | /* 369 | * Remove most spacing between table cells. 370 | */ 371 | 372 | table { 373 | border-collapse: collapse; 374 | border-spacing: 0; 375 | } -------------------------------------------------------------------------------- /haproxy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Michieljoris/node-haproxy/54d2033461051eca7c445e4599f69db72f9ee004/haproxy -------------------------------------------------------------------------------- /haproxycfg.tmpl: -------------------------------------------------------------------------------- 1 | global 2 | log 127.0.0.1 local0 3 | log 127.0.0.1 local1 notice 4 | daemon 5 | maxconn 1000 ## watch out for ulimit max 6 | # user haproxy 7 | # group haproxy 8 | # stats socket {{haproxySocketPath}} user USER_RUNNING_NODE_PROCESS level admin 9 | stats socket {{haproxySocketPath}} level admin 10 | 11 | defaults 12 | log global 13 | option dontlognull 14 | option redispatch 15 | retries 3 16 | maxconn 1000 17 | timeout connect 5000ms 18 | timeout client 50000ms 19 | timeout server 50000ms 20 | 21 | listen stats :1989 22 | mode http 23 | stats enable 24 | stats uri / 25 | stats refresh 2s 26 | stats realm Haproxy\ Stats 27 | stats auth showme:showme 28 | 29 | {{#each frontends}} 30 | frontend {{key}} 31 | {{frontendHelper this}} 32 | {{/each}} 33 | 34 | {{#each backends}} 35 | backend {{key}} 36 | {{backendHelper this}} 37 | {{/each}} 38 | -------------------------------------------------------------------------------- /node-haproxy.org: -------------------------------------------------------------------------------- 1 | * good to know 2 | 3 | * bugs 4 | 5 | http://jpetazzo.github.io/2013/10/16/configure-docker-bridge-network/ 6 | https://github.com/jpetazzo/pipework 7 | https://blog.codecentric.de/en/2014/01/docker-networking-made-simple-3-ways-connect-lxc-containers/ 8 | http://blog.codeaholics.org/2013/giving-dockerlxc-containers-a-routable-ip-address/ 9 | http://goldmann.pl/blog/2014/01/30/assigning-ip-addresses-to-docker-containers-via-dhcp/ 10 | -------------------------------------------------------------------------------- /old/Dockerfile: -------------------------------------------------------------------------------- 1 | # FROM dockerfile/ubuntu 2 | FROM phusion/baseimage:0.9.11 3 | 4 | MAINTAINER Michiel van Oosten 5 | 6 | # Regenerate SSH host keys. baseimage-docker does not contain any, so you 7 | # have to do that yourself. You may also comment out this instruction; the 8 | # init system will auto-generate one during boot. 9 | RUN /etc/my_init.d/00_regen_ssh_host_keys.sh 10 | 11 | # Use baseimage-docker's init system. 12 | CMD ["/sbin/my_init"] 13 | 14 | ADD docker/scripts /root/scripts 15 | ADD docker/.bashrc /root/ 16 | ADD docker/.z /root/ 17 | ADD docker/.gitconfig /root/ 18 | 19 | # setting environment variables 20 | RUN echo "/root" > /etc/container_environment/HOME 21 | 22 | # my build instructions: 23 | 24 | RUN apt-get update 25 | RUN apt-get install rlwrap wget 26 | 27 | # haproxy 28 | # RUN \ 29 | # cd /opt && \ 30 | # apt-get install -qq build-essential libssl-dev libev-dev wget rlwrap&& \ 31 | # wget http://www.haproxy.org/download/1.5/src/haproxy-1.5.1.tar.gz && \ 32 | # tar xzvf haproxy-1.5.1.tar.gz && \ 33 | # cd haproxy-1.5.1 && \ 34 | # make TARGET=linux26 USE_OPENSSL=1 && \ 35 | # sudo make install && \ 36 | # rm -rf /opt/haproxy-1.5.1 && \ 37 | # rm -f /opt/haproxy-1.5.1.tar.gz 38 | 39 | # or just: 40 | ADD docker/haproxy /usr/local/sbin/ 41 | 42 | RUN useradd haproxy 43 | 44 | # serf 45 | ADD docker/serf /usr/local/sbin 46 | 47 | # ADD docker/node /opt/node #one can add the node dir to the project instead perhaps 48 | 49 | # # Install Node 50 | RUN \ 51 | cd /opt && \ 52 | wget http://nodejs.org/dist/v0.10.28/node-v0.10.28-linux-x64.tar.gz && \ 53 | tar -xzf node-v0.10.28-linux-x64.tar.gz && \ 54 | mv node-v0.10.28-linux-x64 node && \ 55 | 56 | cd /usr/local/bin && \ 57 | ln -s /opt/node/bin/* . && \ 58 | rm -f /opt/node-v0.10.28-linux-x64.tar.gz 59 | 60 | RUN \ 61 | npm install -g nodemon 62 | 63 | # Set the working directory 64 | WORKDIR /src 65 | 66 | # Clean up APT when done. 67 | RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 68 | -------------------------------------------------------------------------------- /old/README.md: -------------------------------------------------------------------------------- 1 | Thalassa Aqueduct 2 | ================= 3 | 4 | # Overview 5 | 6 | Aqueduct is a part of the [Thalassa](https://github.com/PearsonEducation/thalassa) system of components. Thalassa is primarily geared to enable continuous deployment scenarios through dynamic configuration of [HAProxy](http://haproxy.1wt.eu/) load balancers and seamless, no connection drop A/B deploys. Aqueduct is the node.js service that manages and controls an HAProxy server. 7 | 8 | Aqueduct exposes a REST API for configuring HAProxy and can dyanmically set HAProxy backends members based on the `name ` and `version` of Thalassa registered services. Aqueduct leverages HAProxy's ability to gracefully reload config without any interruption to user, in other words, without dropping any connections. 9 | 10 | The Thalassa version of Aqueduct has been updated to use [@3rd-Eden](https://github.com/3rd-Eden)'s [haproxy](https://github.com/observing/haproxy) module to manage and control the HAProxy process. 11 | 12 | ## HAProxy Fundamentals 13 | 14 | Aqueduct does not try to obfuscate HAProxy, and it's important to know the fundamentals of how HAProxy works to understand Aqueduct. The API mirrors HAProxy's semantics. The [HAProxy documentation](http://cbonte.github.io/haproxy-dconv/configuration-1.4.html) contains a wealth of detailed information. 15 | 16 | 1. **Frontends** - A "frontend" section describes a set of listening sockets accepting client 17 | connections. 18 | 19 | 2. **Backends** - A "backend" section describes a set of servers to which the proxy will connect 20 | to forward incoming connections. 21 | 22 | 3. **Members/Servers** - Aqueduct calls the servers that *backends* route to "members". In other words, members of a backend pool of servers. 23 | 24 | 4. **Config file** - At startup, HAProxy loads a configuration file and never looks at that file again. Aqueduct manages this by re-templating a new config file and gracefully restarting the HAProxy process. 25 | 26 | 5. **Stats Socket** - a UNIX socket in stream mode that will return various statistics outputs and even allows some basic configuration tweaking like enabling and disabling existing backend members, setting weights, etc. Aqueduct connects to this socket and provides realtime streaming stats over a web socket stream. 27 | 28 | 29 | # Installation 30 | 31 | npm install thalassa-aqueduct 32 | 33 | # Running 34 | 35 | The easiest way to run Aqueduct at this point is with the bin script from the command line. Aqueduct is exposed as a module and can be used as such in your own application but you should have [a close look](https://github.com/PearsonEducation/thalassa-aqueduct/blob/master/bin/server.js#L88) at how the Hapi server and web socket stream is configured. 36 | 37 | ./node_modules/.bin/thalassa-aqueduct 38 | 39 | ## Options 40 | 41 | ./node_modules/.bin/thalassa-aqueduct --help 42 | Options: 43 | --host host to bind to [default: "0.0.0.0"] 44 | --port port to bind to [default: 10000] 45 | --label logical label for this aqueduct 46 | --thalassaHost host of the Thalassa server [default: "127.0.0.1"] 47 | --thalassaPort socket port of the Thalassa server [default: 5001] 48 | --thalassaApiPort http API port of the Thalassa server [default: 9000] 49 | --haproxySocketPath path to Haproxy socket file [default: "/tmp/haproxy.status.sock"] 50 | --haproxyPidPath path to Haproxy pid file [default: "/var/run/haproxy.pid"] 51 | --haproxyCfgPath generated Haproxy config location [default: "/etc/haproxy/haproxy.cfg"] 52 | --templateFile template used to generate Haproxy config [default: "default.haproxycfg.tmpl"] 53 | --persistence directory to save configuration 54 | --sudo use sudo when starting haproxy 55 | --debug enabled debug logging 56 | --dbPath filesystem path for leveldb [default: "./node_modules/thalassa-crowsnest/bin/db"] 57 | 58 | For example the command to run might look something like this (typically how I run locally): 59 | 60 | ./node_modules/.bin/thalassa-aqueduct --haproxyCfgPath /tmp/haproxy.cfg --debug --persistence \ 61 | '/tmp/aqueduct.json' --templateFile dev.haproxycfg.tmpl --haproxyPidPath /tmp/haproxy.pid \ 62 | --label 'myapp-dev' 63 | 64 | # Web UI 65 | 66 | Aqueduct provides a web UI that allows users to get a visual representation of the frontends/backends/member data related to their haproxy instance (via the 'overview' page), as well as some insight into the activity (haproxy config changes, online/offline events) occuring within Aqueduct (via the 'activity' page). 67 | 68 | The UI can be accessed on the port specified by the --port parameter (by default port 10000). 69 | 70 | e.g. http://127.0.0.1:10000 would access the web UI on localhost at the default port of 10000 71 | 72 | # HTTP API 73 | 74 | ### GET `/frontends` 75 | 76 | Returns Array of `frontend` objects for all of the frontends configured for this Aqueduct server. 77 | 78 | For example: 79 | 80 | [{ 81 | "id": "frontend/myapp", 82 | "_type": "frontend", 83 | "key": "myapp", 84 | "bind": "*:8080,*:80", 85 | "backend": "live", 86 | "mode": "http", 87 | "keepalive": "default", 88 | "rules": [{ 89 | "type": "header", 90 | "header": "host", 91 | "operation": "hdr_dom", 92 | "value": "staged.myapp.com", 93 | "backend": "staged" 94 | }], 95 | "natives": [] 96 | }] 97 | 98 | 99 | ### GET `/frontends/{key}` 100 | 101 | Gets a specific frontend by `key`. Expect a response status code of `200` otherwise `404`. 102 | 103 | 104 | ### PUT `/frontends/{key}` 105 | 106 | Create or update a `frontend` by `key`. `PUT` with a `Content-Type` of `application/json` and a body like: 107 | 108 | { 109 | "bind": "10.2.2.2:80,*:8080" // IP and ports to bind to, comma separated, host may be * 110 | , "backend": "foo" // the default backend to route to, it must be defined already 111 | , "mode": "tcp" // default: http, expects tcp|http 112 | , "keepalive": "close" // default: "default", expects default|close|server-close 113 | , "rules": [] // array of rules, see next section 114 | , "natives": [] // array of strings of raw config USE SPARINGLY!! 115 | } 116 | 117 | #### Routing Rules 118 | 119 | There are currently 3 types of rules that can be applied to frontends: `path`, `url`, and `header`. 120 | 121 | Path rules support `path`, `path_beg`, and `path_reg` HAProxy operations 122 | 123 | { 124 | "type": "path" 125 | , "operation": "path|path_beg|path_reg" 126 | , "value": "favicon.ico|/ecxd/|^/article/[^/]*$" 127 | , "backend": "foo" // if rule is met, the backend to route the request to 128 | } 129 | 130 | 131 | Url rules support `url`, `url_beg`, and `url_reg` HAProxy operations 132 | 133 | { 134 | "type": "url" 135 | , "operation": "url|url_beg|url_reg" 136 | , "value": "/bar" // value for the operation 137 | , "backend": "bar" // if rule is met, the backend to route the request to 138 | } 139 | 140 | Header rules support `hdr_dom` with a entire value at this point 141 | 142 | { 143 | "type": "header" 144 | , "header": "host" // the name of the HTTP header 145 | , "operation": "hdr_dom" 146 | , "value": "baz.com" 147 | , "backend": "baz" // if rule is met, the backend to route the request to 148 | } 149 | 150 | #### Natives 151 | 152 | The natives property is an end around way to insert raw lines of config for front ends and backends. Use them sparingly but use them if you need them. 153 | 154 | 155 | ### DELETE `/frontends/{key}` 156 | 157 | Delete a specific frontend by `key`. Expect `200` or `404` 158 | 159 | 160 | ### GET `/backends` 161 | 162 | Returns Array of `backend` objects for all of the backends configured for this Aqueduct server. 163 | 164 | For example: 165 | 166 | [{ 167 | "id": "backend/live", 168 | "_type": "backend", 169 | "key": "live", 170 | "type": "dynamic", 171 | "name": "classroom-ui", 172 | "version": "1.0.0", 173 | "balance": "roundrobin", 174 | "host": null, 175 | "mode": "http", 176 | "members": [{ 177 | "name": "myapp", 178 | "version": "1.0.0", 179 | "host": "10.10.240.121", 180 | "port": 8080, 181 | "lastKnown": 1378762056885, 182 | "meta": { 183 | "hostname": "dev-use1b-pr-01-myapp-01x00x00-01", 184 | "pid": 17941, 185 | "registered": 1378740834616 186 | }, 187 | "id": "/myapp/1.0.0/10.10.240.121/8080" 188 | }, 189 | { 190 | "name": "myapp", 191 | "version": "1.0.0", 192 | "host": "10.10.240.80", 193 | "port": 8080, 194 | "lastKnown": 1378762060226, 195 | "meta": { 196 | "hostname": "dev-use1b-pr-01-myapp-01x00x00-02", 197 | "pid": 18020, 198 | "registered": 1378762079883 199 | }, 200 | "id": "/myapp/1.0.0/10.10.240.80/8080" 201 | }], 202 | "natives": [] 203 | }] 204 | 205 | 206 | ### PUT `/backends/{key}` 207 | 208 | Create or update a `backend` by `key`. `PUT` with a `Content-Type` of `application/json` and a body like: 209 | 210 | { 211 | "type" : "dynamic|static" 212 | , "name" : "foo" // only required if type = dynamic 213 | , "version" : "1.0.0" // only required if type = dynamic 214 | , "balance" : "roundrobin|source" // defaults to roundrobin 215 | , "host" : "myapp.com" // default: undefined, if specified request to member will contain this host header 216 | , "health" : { // optional health check 217 | "method": "GET" // HTTP method 218 | , "uri": "/checkity-check" // URI to call 219 | , "httpVersion": "HTTP/1.1" // HTTP/1.0 or HTTP/1.1 `host` required if HTTP/1.1 220 | , "interval": 5000 // period to check, milliseconds 221 | } 222 | , "mode" : "http|tcp" // default: http 223 | , "natives": [] // array of strings of raw config USE SPARINGLY!! 224 | , "members" : [] // if type = dynamic this is dynamically populated based on role/version subscription 225 | // otherwise expects { host: '10.10.10.10', port: 8080} 226 | } 227 | 228 | ### GET `/backends/{key}` 229 | 230 | Gets a specific `backend` by `key`. Expect `200` else `404`. 231 | 232 | 233 | ### DELETE `/backends/{key}` 234 | 235 | Delete a specific `backend` by `key`. Expect `200` or `404` 236 | 237 | 238 | 239 | ### POST `/backends/{key}` 240 | 241 | Update a `backend`s `role` and `version` Subscription with a `Content-Type` of `application/json` and a body like: 242 | 243 | { 244 | "name": "myapp" // app name registered to thalassa 245 | , "version": "1.1.0" // version to route to 246 | } 247 | 248 | `name` is actually optional. You may also just send the `version`: 249 | 250 | { 251 | "version": "1.1.0" 252 | } 253 | 254 | 255 | ### GET `/haproxy/config` 256 | 257 | Return the last know generated HAProxy config file contents that were written to the location of `opts.haproxyCfgPath`. 258 | 259 | 260 | global 261 | log 127.0.0.1 local0 262 | log 127.0.0.1 local1 notice 263 | daemon 264 | maxconn 4096 265 | user haproxy 266 | group haproxy 267 | stats socket /tmp/haproxy.status.sock user appuser level admin 268 | 269 | defaults 270 | log global 271 | option dontlognull 272 | option redispatch 273 | retries 3 274 | maxconn 2000 275 | timeout connect 5000ms 276 | timeout client 50000ms 277 | timeout server 50000ms 278 | 279 | listen stats :1988 280 | mode http 281 | stats enable 282 | stats uri / 283 | stats refresh 2s 284 | stats realm Haproxy\ Stats 285 | stats auth showme:showme 286 | 287 | 288 | frontend myapp 289 | bind *:8080,*:80 290 | mode http 291 | default_backend live 292 | option httplog 293 | option http-server-close 294 | option http-pretend-keepalive 295 | acl header_uv7vi hdr_dom(host) myapp-staged.com 296 | use_backend staged if header_uv7vi 297 | 298 | 299 | 300 | backend live 301 | mode http 302 | balance roundrobin 303 | server live_10.10.240.121:8080 10.10.240.121:8080 check inter 2000 304 | server live_10.10.240.80:8080 10.10.240.80:8080 check inter 2000 305 | 306 | backend staged 307 | mode http 308 | balance roundrobin 309 | server staged_10.10.240.174:8080 10.10.240.174:8080 check inter 2000 310 | server staged_10.10.240.206:8080 10.10.240.206:8080 check inter 2000 311 | 312 | 313 | # Known Limitations and Roadmap 314 | 315 | Thalassa currently doesn't implement any type of authentication or authorization and at this point expects to be running on a trusted private network. This will be addressed in the future. Ultimately auth should be extensible and customizable. Suggestions and pull requests welcome! 316 | 317 | # License 318 | 319 | Licensed under Apache 2.0. See [LICENSE](https://github.com/PearsonEducation/thalassa-aqueduct/blob/master/LICENSE) file. 320 | -------------------------------------------------------------------------------- /old/docker/.bashrc: -------------------------------------------------------------------------------- 1 | # ~/.bashrc: executed by bash(1) for non-login shells. 2 | # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc) 3 | # for examples 4 | 5 | # If not running interactively, don't do anything 6 | [ -z "$PS1" ] && return 7 | 8 | # don't put duplicate lines in the history. See bash(1) for more options 9 | # ... or force ignoredups and ignorespace 10 | HISTCONTROL=ignoredups:ignorespace 11 | 12 | # append to the history file, don't overwrite it 13 | shopt -s histappend 14 | 15 | # for setting history length see HISTSIZE and HISTFILESIZE in bash(1) 16 | HISTSIZE=1000 17 | HISTFILESIZE=2000 18 | 19 | # check the window size after each command and, if necessary, 20 | # update the values of LINES and COLUMNS. 21 | shopt -s checkwinsize 22 | 23 | # set variable identifying the chroot you work in (used in the prompt below) 24 | if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then 25 | debian_chroot=$(cat /etc/debian_chroot) 26 | fi 27 | 28 | # If this is an xterm set the title to user@host:dir 29 | case "$TERM" in 30 | xterm*|rxvt*) 31 | PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1" 32 | ;; 33 | *) 34 | ;; 35 | esac 36 | 37 | 38 | # Colorize grep 39 | if echo hello|grep --color=auto l >/dev/null 2>&1; then 40 | export GREP_OPTIONS="--color=auto" GREP_COLOR="1;31" 41 | fi 42 | 43 | # Shell 44 | export CLICOLOR="1" 45 | if [ -f ~/scripts/git-prompt.sh ]; then 46 | source ~/scripts/git-prompt.sh 47 | export GIT_PS1_SHOWDIRTYSTATE="1" 48 | export PS1="\[\033[40m\]\[\033[33m\][ \u@\H:\[\033[32m\]\w\$(__git_ps1 \" \[\033[35m\]{\[\033[36m\]%s\[\033[35m\]}\")\[\033[33m\] ]$\[\033[0m\] " 49 | else 50 | export PS1="\[\033[40m\]\[\033[33m\][ \u@\H:\[\033[32m\]\w\[\033[33m\] ]$\[\033[0m\] " 51 | fi 52 | export LS_COLORS="di=34:ln=35:so=32:pi=33:ex=1;40:bd=34;40:cd=34;40:su=0;40:sg=0;40:tw=0;40:ow=0;40:" 53 | 54 | # Git 55 | source ~/scripts/git-completion.sh 56 | 57 | # Z 58 | source ~/scripts/z.sh 59 | 60 | 61 | # from my bashrc 62 | 63 | # Don't use ^D to exit 64 | set -o ignoreeof 65 | 66 | # When changing directory small typos can be ignored by bash 67 | # for example, cd /vr/lgo/apaache would find /var/log/apache 68 | shopt -s cdspell 69 | 70 | # Ignore some controlling instructions 71 | # HISTIGNORE is a colon-delimited list of patterns which should be excluded. 72 | # The '&' is a special pattern which suppresses duplicate entries. 73 | # export HISTIGNORE=$'[ \t]*:&:[fb]g:exit' 74 | export HISTIGNORE=$'[ \t]*:&:[fb]g:exit:ls' # Ignore the ls command as well 75 | # 76 | # Whenever displaying the prompt, write the previous line to disk 77 | export PROMPT_COMMAND="history -a" 78 | 79 | export PATH=$PATH:~/bin 80 | 81 | export HISTTIMEFORMAT='%F %T ' 82 | 83 | set -o vi 84 | 85 | #So that we can start node with rlwrap and have vi mode 86 | export NODE_NO_READLINE=1 87 | 88 | # b) function cd_func 89 | # This function defines a 'cd' replacement function capable of keeping, 90 | # displaying and accessing history of visited directories, up to 10 entries. 91 | # To use it, uncomment it, source this file and try 'cd --'. 92 | # acd_func 1.0.5, 10-nov-2004 93 | # Petar Marinov, http:/geocities.com/h2428, this is public domain 94 | cd_func () 95 | { 96 | local x2 the_new_dir adir index 97 | local -i cnt 98 | 99 | if [[ $1 == "--" ]]; then 100 | dirs -v 101 | return 0 102 | fi 103 | 104 | the_new_dir=$1 105 | [[ -z $1 ]] && the_new_dir=$HOME 106 | 107 | if [[ ${the_new_dir:0:1} == '-' ]]; then 108 | # 109 | # Extract dir N from dirs 110 | index=${the_new_dir:1} 111 | [[ -z $index ]] && index=1 112 | adir=$(dirs +$index) 113 | [[ -z $adir ]] && return 1 114 | the_new_dir=$adir 115 | fi 116 | 117 | # 118 | # '~' has to be substituted by ${HOME} 119 | [[ ${the_new_dir:0:1} == '~' ]] && the_new_dir="${HOME}${the_new_dir:1}" 120 | 121 | # 122 | # Now change to the new dir and add to the top of the stack 123 | pushd "${the_new_dir}" > /dev/null 124 | [[ $? -ne 0 ]] && return 1 125 | the_new_dir=$(pwd) 126 | 127 | # 128 | # Trim down everything beyond 11th entry 129 | popd -n +11 2>/dev/null 1>/dev/null 130 | 131 | # 132 | # Remove any other occurence of this dir skipping the top of the stack 133 | for ((cnt=1; cnt <= 10; cnt++)); do 134 | x2=$(dirs +${cnt} 2>/dev/null) 135 | [[ $? -ne 0 ]] && return 0 136 | [[ ${x2:0:1} == '~' ]] && x2="${HOME}${x2:1}" 137 | if [[ "${x2}" == "${the_new_dir}" ]]; then 138 | popd -n +$cnt 2>/dev/null 1>/dev/null 139 | cnt=cnt-1 140 | fi 141 | done 142 | 143 | return 0 144 | } 145 | # 146 | # Alias definitions. 147 | # You may want to put all your additions into a separate file like 148 | # ~/.bash_aliases, instead of adding them here directly. 149 | # See /usr/share/doc/bash-doc/examples in the bash-doc package. 150 | 151 | if [ -f ~/.bash_aliases ]; then 152 | . ~/.bash_aliases 153 | fi 154 | 155 | # enable programmable completion features (you don't need to enable 156 | # this, if it's already enabled in /etc/bash.bashrc and /etc/profile 157 | # sources /etc/bash.bashrc). 158 | #if [ -f /etc/bash_completion ] && ! shopt -oq posix; then 159 | # . /etc/bash_completion 160 | #fi 161 | 162 | # 163 | # .bashrc 164 | # 165 | # Aliases and Functions 166 | # 167 | 168 | alias rm='rm -i' 169 | alias cp='cp -i' 170 | alias mv='mv -i' 171 | 172 | # alias ls='ls -hFG' 173 | alias l='ls -lF' 174 | alias ll='ls -alF' 175 | alias lt='ls -ltrF' 176 | alias ll='ls -alF' 177 | alias lls='ls -alSrF' 178 | alias llt='ls -altrF' 179 | 180 | alias tarc='tar cvf' 181 | alias tarcz='tar czvf' 182 | alias tarx='tar xvf' 183 | alias tarxz='tar xvzf' 184 | 185 | alias g='git' 186 | alias less='less -R' 187 | alias os='lsb_release -a' 188 | alias vi='vim' 189 | 190 | # Colorize directory listing 191 | # alias ls="ls -ph --color=auto" 192 | 193 | alias cd=cd_func 194 | alias ll='ls -l --color' 195 | alias la='ls -a --color' 196 | alias lal='ls -la --color' 197 | alias ls='ls --group-directories-first --color' 198 | alias l='ls' 199 | alias c='. c.sh' 200 | 201 | alias ..='cd ..' 202 | alias ...='cd ../..' 203 | alias ....='cd ../../..' 204 | 205 | alias pg='ps ax | grep' 206 | alias sb='source ~/.bashrc' 207 | 208 | 209 | alias node='rlwrap node' 210 | 211 | 212 | # Default to human readable figures 213 | alias df='df -h' 214 | alias du='du -h' 215 | # 216 | 217 | -------------------------------------------------------------------------------- /old/docker/.gitconfig: -------------------------------------------------------------------------------- 1 | [user] 2 | name = 3 | email = 4 | 5 | [alias] 6 | a = add 7 | b = branch 8 | c = commit 9 | co = checkout 10 | d = diff 11 | e = add --edit 12 | f = fetch 13 | g = grep 14 | h = help 15 | i = init 16 | l = log --pretty=format:\"%h %ad | %s%d [%an]\" --graph --date=short 17 | ll = log --stat 18 | m = merge 19 | r = remote 20 | s = status --branch --short 21 | t = tag 22 | w = whatchanged 23 | 24 | [core] 25 | excludesfile = ~/.gitignore 26 | editor = vim 27 | 28 | [color] 29 | ui = always 30 | status = auto 31 | branch = auto 32 | interactive = auto 33 | diff = auto 34 | 35 | [merge] 36 | tool = vimdiff 37 | 38 | [push] 39 | default = upstream 40 | -------------------------------------------------------------------------------- /old/docker/.z: -------------------------------------------------------------------------------- 1 | /src|1|1404838544 2 | -------------------------------------------------------------------------------- /old/docker/haproxy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Michieljoris/node-haproxy/54d2033461051eca7c445e4599f69db72f9ee004/old/docker/haproxy -------------------------------------------------------------------------------- /old/docker/scripts/git-prompt.sh: -------------------------------------------------------------------------------- 1 | # bash/zsh git prompt support 2 | # 3 | # Copyright (C) 2006,2007 Shawn O. Pearce 4 | # Distributed under the GNU General Public License, version 2.0. 5 | # 6 | # This script allows you to see repository status in your prompt. 7 | # 8 | # To enable: 9 | # 10 | # 1) Copy this file to somewhere (e.g. ~/.git-prompt.sh). 11 | # 2) Add the following line to your .bashrc/.zshrc: 12 | # source ~/.git-prompt.sh 13 | # 3a) Change your PS1 to call __git_ps1 as 14 | # command-substitution: 15 | # Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ ' 16 | # ZSH: setopt PROMPT_SUBST ; PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ ' 17 | # the optional argument will be used as format string. 18 | # 3b) Alternatively, for a slightly faster prompt, __git_ps1 can 19 | # be used for PROMPT_COMMAND in Bash or for precmd() in Zsh 20 | # with two parameters,
 and , which are strings
 21 | #        you would put in $PS1 before and after the status string
 22 | #        generated by the git-prompt machinery.  e.g.
 23 | #        Bash: PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
 24 | #          will show username, at-sign, host, colon, cwd, then
 25 | #          various status string, followed by dollar and SP, as
 26 | #          your prompt.
 27 | #        ZSH:  precmd () { __git_ps1 "%n" ":%~$ " "|%s" }
 28 | #          will show username, pipe, then various status string,
 29 | #          followed by colon, cwd, dollar and SP, as your prompt.
 30 | #        Optionally, you can supply a third argument with a printf
 31 | #        format string to finetune the output of the branch status
 32 | #
 33 | # The repository status will be displayed only if you are currently in a
 34 | # git repository. The %s token is the placeholder for the shown status.
 35 | #
 36 | # The prompt status always includes the current branch name.
 37 | #
 38 | # In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty value,
 39 | # unstaged (*) and staged (+) changes will be shown next to the branch
 40 | # name.  You can configure this per-repository with the
 41 | # bash.showDirtyState variable, which defaults to true once
 42 | # GIT_PS1_SHOWDIRTYSTATE is enabled.
 43 | #
 44 | # You can also see if currently something is stashed, by setting
 45 | # GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed,
 46 | # then a '$' will be shown next to the branch name.
 47 | #
 48 | # If you would like to see if there're untracked files, then you can set
 49 | # GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're untracked
 50 | # files, then a '%' will be shown next to the branch name.  You can
 51 | # configure this per-repository with the bash.showUntrackedFiles
 52 | # variable, which defaults to true once GIT_PS1_SHOWUNTRACKEDFILES is
 53 | # enabled.
 54 | #
 55 | # If you would like to see the difference between HEAD and its upstream,
 56 | # set GIT_PS1_SHOWUPSTREAM="auto".  A "<" indicates you are behind, ">"
 57 | # indicates you are ahead, "<>" indicates you have diverged and "="
 58 | # indicates that there is no difference. You can further control
 59 | # behaviour by setting GIT_PS1_SHOWUPSTREAM to a space-separated list
 60 | # of values:
 61 | #
 62 | #     verbose       show number of commits ahead/behind (+/-) upstream
 63 | #     legacy        don't use the '--count' option available in recent
 64 | #                   versions of git-rev-list
 65 | #     git           always compare HEAD to @{upstream}
 66 | #     svn           always compare HEAD to your SVN upstream
 67 | #
 68 | # By default, __git_ps1 will compare HEAD to your SVN upstream if it can
 69 | # find one, or @{upstream} otherwise.  Once you have set
 70 | # GIT_PS1_SHOWUPSTREAM, you can override it on a per-repository basis by
 71 | # setting the bash.showUpstream config variable.
 72 | #
 73 | # If you would like to see more information about the identity of
 74 | # commits checked out as a detached HEAD, set GIT_PS1_DESCRIBE_STYLE
 75 | # to one of these values:
 76 | #
 77 | #     contains      relative to newer annotated tag (v1.6.3.2~35)
 78 | #     branch        relative to newer tag or branch (master~4)
 79 | #     describe      relative to older annotated tag (v1.6.3.1-13-gdd42c2f)
 80 | #     default       exactly matching tag
 81 | #
 82 | # If you would like a colored hint about the current dirty state, set
 83 | # GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on
 84 | # the colored output of "git status -sb" and are available only when
 85 | # using __git_ps1 for PROMPT_COMMAND or precmd.
 86 | 
 87 | # stores the divergence from upstream in $p
 88 | # used by GIT_PS1_SHOWUPSTREAM
 89 | __git_ps1_show_upstream ()
 90 | {
 91 |   local key value
 92 |   local svn_remote svn_url_pattern count n
 93 |   local upstream=git legacy="" verbose=""
 94 | 
 95 |   svn_remote=()
 96 |   # get some config options from git-config
 97 |   local output="$(git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' 2>/dev/null | tr '\0\n' '\n ')"
 98 |   while read -r key value; do
 99 |     case "$key" in
100 |     bash.showupstream)
101 |       GIT_PS1_SHOWUPSTREAM="$value"
102 |       if [[ -z "${GIT_PS1_SHOWUPSTREAM}" ]]; then
103 |         p=""
104 |         return
105 |       fi
106 |       ;;
107 |     svn-remote.*.url)
108 |       svn_remote[$((${#svn_remote[@]} + 1))]="$value"
109 |       svn_url_pattern+="\\|$value"
110 |       upstream=svn+git # default upstream is SVN if available, else git
111 |       ;;
112 |     esac
113 |   done <<< "$output"
114 | 
115 |   # parse configuration values
116 |   for option in ${GIT_PS1_SHOWUPSTREAM}; do
117 |     case "$option" in
118 |     git|svn) upstream="$option" ;;
119 |     verbose) verbose=1 ;;
120 |     legacy)  legacy=1  ;;
121 |     esac
122 |   done
123 | 
124 |   # Find our upstream
125 |   case "$upstream" in
126 |   git)    upstream="@{upstream}" ;;
127 |   svn*)
128 |     # get the upstream from the "git-svn-id: ..." in a commit message
129 |     # (git-svn uses essentially the same procedure internally)
130 |     local -a svn_upstream
131 |     svn_upstream=($(git log --first-parent -1 \
132 |           --grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null))
133 |     if [[ 0 -ne ${#svn_upstream[@]} ]]; then
134 |       svn_upstream=${svn_upstream[${#svn_upstream[@]} - 2]}
135 |       svn_upstream=${svn_upstream%@*}
136 |       local n_stop="${#svn_remote[@]}"
137 |       for ((n=1; n <= n_stop; n++)); do
138 |         svn_upstream=${svn_upstream#${svn_remote[$n]}}
139 |       done
140 | 
141 |       if [[ -z "$svn_upstream" ]]; then
142 |         # default branch name for checkouts with no layout:
143 |         upstream=${GIT_SVN_ID:-git-svn}
144 |       else
145 |         upstream=${svn_upstream#/}
146 |       fi
147 |     elif [[ "svn+git" = "$upstream" ]]; then
148 |       upstream="@{upstream}"
149 |     fi
150 |     ;;
151 |   esac
152 | 
153 |   # Find how many commits we are ahead/behind our upstream
154 |   if [[ -z "$legacy" ]]; then
155 |     count="$(git rev-list --count --left-right \
156 |         "$upstream"...HEAD 2>/dev/null)"
157 |   else
158 |     # produce equivalent output to --count for older versions of git
159 |     local commits
160 |     if commits="$(git rev-list --left-right "$upstream"...HEAD 2>/dev/null)"
161 |     then
162 |       local commit behind=0 ahead=0
163 |       for commit in $commits
164 |       do
165 |         case "$commit" in
166 |         "<"*) ((behind++)) ;;
167 |         *)    ((ahead++))  ;;
168 |         esac
169 |       done
170 |       count="$behind  $ahead"
171 |     else
172 |       count=""
173 |     fi
174 |   fi
175 | 
176 |   # calculate the result
177 |   if [[ -z "$verbose" ]]; then
178 |     case "$count" in
179 |     "") # no upstream
180 |       p="" ;;
181 |     "0  0") # equal to upstream
182 |       p="=" ;;
183 |     "0  "*) # ahead of upstream
184 |       p=">" ;;
185 |     *"  0") # behind upstream
186 |       p="<" ;;
187 |     *)      # diverged from upstream
188 |       p="<>" ;;
189 |     esac
190 |   else
191 |     case "$count" in
192 |     "") # no upstream
193 |       p="" ;;
194 |     "0  0") # equal to upstream
195 |       p=" u=" ;;
196 |     "0  "*) # ahead of upstream
197 |       p=" u+${count#0 }" ;;
198 |     *"  0") # behind upstream
199 |       p=" u-${count%  0}" ;;
200 |     *)      # diverged from upstream
201 |       p=" u+${count#* }-${count%  *}" ;;
202 |     esac
203 |   fi
204 | 
205 | }
206 | 
207 | # Helper function that is meant to be called from __git_ps1.  It
208 | # injects color codes into the appropriate gitstring variables used
209 | # to build a gitstring.
210 | __git_ps1_colorize_gitstring ()
211 | {
212 |   if [[ -n ${ZSH_VERSION-} ]]; then
213 |     local c_red='%F{red}'
214 |     local c_green='%F{green}'
215 |     local c_lblue='%F{blue}'
216 |     local c_clear='%f'
217 |   else
218 |     # Using \[ and \] around colors is necessary to prevent
219 |     # issues with command line editing/browsing/completion!
220 |     local c_red='\[\e[31m\]'
221 |     local c_green='\[\e[32m\]'
222 |     local c_lblue='\[\e[1;34m\]'
223 |     local c_clear='\[\e[0m\]'
224 |   fi
225 |   local bad_color=$c_red
226 |   local ok_color=$c_green
227 |   local flags_color="$c_lblue"
228 | 
229 |   local branch_color=""
230 |   if [ $detached = no ]; then
231 |     branch_color="$ok_color"
232 |   else
233 |     branch_color="$bad_color"
234 |   fi
235 |   c="$branch_color$c"
236 | 
237 |   z="$c_clear$z"
238 |   if [ "$w" = "*" ]; then
239 |     w="$bad_color$w"
240 |   fi
241 |   if [ -n "$i" ]; then
242 |     i="$ok_color$i"
243 |   fi
244 |   if [ -n "$s" ]; then
245 |     s="$flags_color$s"
246 |   fi
247 |   if [ -n "$u" ]; then
248 |     u="$bad_color$u"
249 |   fi
250 |   r="$c_clear$r"
251 | }
252 | 
253 | # __git_ps1 accepts 0 or 1 arguments (i.e., format string)
254 | # when called from PS1 using command substitution
255 | # in this mode it prints text to add to bash PS1 prompt (includes branch name)
256 | #
257 | # __git_ps1 requires 2 or 3 arguments when called from PROMPT_COMMAND (pc)
258 | # in that case it _sets_ PS1. The arguments are parts of a PS1 string.
259 | # when two arguments are given, the first is prepended and the second appended
260 | # to the state string when assigned to PS1.
261 | # The optional third parameter will be used as printf format string to further
262 | # customize the output of the git-status string.
263 | # In this mode you can request colored hints using GIT_PS1_SHOWCOLORHINTS=true
264 | __git_ps1 ()
265 | {
266 |   local pcmode=no
267 |   local detached=no
268 |   local ps1pc_start='\u@\h:\w '
269 |   local ps1pc_end='\$ '
270 |   local printf_format=' (%s)'
271 | 
272 |   case "$#" in
273 |     2|3)  pcmode=yes
274 |       ps1pc_start="$1"
275 |       ps1pc_end="$2"
276 |       printf_format="${3:-$printf_format}"
277 |     ;;
278 |     0|1)  printf_format="${1:-$printf_format}"
279 |     ;;
280 |     *)  return
281 |     ;;
282 |   esac
283 | 
284 |   local repo_info rev_parse_exit_code
285 |   repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
286 |     --is-bare-repository --is-inside-work-tree \
287 |     --short HEAD 2>/dev/null)"
288 |   rev_parse_exit_code="$?"
289 | 
290 |   if [ -z "$repo_info" ]; then
291 |     if [ $pcmode = yes ]; then
292 |       #In PC mode PS1 always needs to be set
293 |       PS1="$ps1pc_start$ps1pc_end"
294 |     fi
295 |     return
296 |   fi
297 | 
298 |   local short_sha
299 |   if [ "$rev_parse_exit_code" = "0" ]; then
300 |     short_sha="${repo_info##*$'\n'}"
301 |     repo_info="${repo_info%$'\n'*}"
302 |   fi
303 |   local inside_worktree="${repo_info##*$'\n'}"
304 |   repo_info="${repo_info%$'\n'*}"
305 |   local bare_repo="${repo_info##*$'\n'}"
306 |   repo_info="${repo_info%$'\n'*}"
307 |   local inside_gitdir="${repo_info##*$'\n'}"
308 |   local g="${repo_info%$'\n'*}"
309 | 
310 |   local r=""
311 |   local b=""
312 |   local step=""
313 |   local total=""
314 |   if [ -d "$g/rebase-merge" ]; then
315 |     read b 2>/dev/null <"$g/rebase-merge/head-name"
316 |     read step 2>/dev/null <"$g/rebase-merge/msgnum"
317 |     read total 2>/dev/null <"$g/rebase-merge/end"
318 |     if [ -f "$g/rebase-merge/interactive" ]; then
319 |       r="|REBASE-i"
320 |     else
321 |       r="|REBASE-m"
322 |     fi
323 |   else
324 |     if [ -d "$g/rebase-apply" ]; then
325 |       read step 2>/dev/null <"$g/rebase-apply/next"
326 |       read total 2>/dev/null <"$g/rebase-apply/last"
327 |       if [ -f "$g/rebase-apply/rebasing" ]; then
328 |         read b 2>/dev/null <"$g/rebase-apply/head-name"
329 |         r="|REBASE"
330 |       elif [ -f "$g/rebase-apply/applying" ]; then
331 |         r="|AM"
332 |       else
333 |         r="|AM/REBASE"
334 |       fi
335 |     elif [ -f "$g/MERGE_HEAD" ]; then
336 |       r="|MERGING"
337 |     elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
338 |       r="|CHERRY-PICKING"
339 |     elif [ -f "$g/REVERT_HEAD" ]; then
340 |       r="|REVERTING"
341 |     elif [ -f "$g/BISECT_LOG" ]; then
342 |       r="|BISECTING"
343 |     fi
344 | 
345 |     if [ -n "$b" ]; then
346 |       :
347 |     elif [ -h "$g/HEAD" ]; then
348 |       # symlink symbolic ref
349 |       b="$(git symbolic-ref HEAD 2>/dev/null)"
350 |     else
351 |       local head=""
352 |       if ! read head 2>/dev/null <"$g/HEAD"; then
353 |         if [ $pcmode = yes ]; then
354 |           PS1="$ps1pc_start$ps1pc_end"
355 |         fi
356 |         return
357 |       fi
358 |       # is it a symbolic ref?
359 |       b="${head#ref: }"
360 |       if [ "$head" = "$b" ]; then
361 |         detached=yes
362 |         b="$(
363 |         case "${GIT_PS1_DESCRIBE_STYLE-}" in
364 |         (contains)
365 |           git describe --contains HEAD ;;
366 |         (branch)
367 |           git describe --contains --all HEAD ;;
368 |         (describe)
369 |           git describe HEAD ;;
370 |         (* | default)
371 |           git describe --tags --exact-match HEAD ;;
372 |         esac 2>/dev/null)" ||
373 | 
374 |         b="$short_sha..."
375 |         b="($b)"
376 |       fi
377 |     fi
378 |   fi
379 | 
380 |   if [ -n "$step" ] && [ -n "$total" ]; then
381 |     r="$r $step/$total"
382 |   fi
383 | 
384 |   local w=""
385 |   local i=""
386 |   local s=""
387 |   local u=""
388 |   local c=""
389 |   local p=""
390 | 
391 |   if [ "true" = "$inside_gitdir" ]; then
392 |     if [ "true" = "$bare_repo" ]; then
393 |       c="BARE:"
394 |     else
395 |       b="GIT_DIR!"
396 |     fi
397 |   elif [ "true" = "$inside_worktree" ]; then
398 |     if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] &&
399 |        [ "$(git config --bool bash.showDirtyState)" != "false" ]
400 |     then
401 |       git diff --no-ext-diff --quiet --exit-code || w="*"
402 |       if [ -n "$short_sha" ]; then
403 |         git diff-index --cached --quiet HEAD -- || i="+"
404 |       else
405 |         i="#"
406 |       fi
407 |     fi
408 |     if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ] &&
409 |        [ -r "$g/refs/stash" ]; then
410 |       s="$"
411 |     fi
412 | 
413 |     if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
414 |        [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
415 |        git ls-files --others --exclude-standard --error-unmatch -- '*' >/dev/null 2>/dev/null
416 |     then
417 |       u="%${ZSH_VERSION+%}"
418 |     fi
419 | 
420 |     if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
421 |       __git_ps1_show_upstream
422 |     fi
423 |   fi
424 | 
425 |   local z="${GIT_PS1_STATESEPARATOR-" "}"
426 | 
427 |   # NO color option unless in PROMPT_COMMAND mode
428 |   if [ $pcmode = yes ] && [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
429 |     __git_ps1_colorize_gitstring
430 |   fi
431 | 
432 |   local f="$w$i$s$u"
433 |   local gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p"
434 | 
435 |   if [ $pcmode = yes ]; then
436 |     if [[ -n ${ZSH_VERSION-} ]]; then
437 |       gitstring=$(printf -- "$printf_format" "$gitstring")
438 |     else
439 |       printf -v gitstring -- "$printf_format" "$gitstring"
440 |     fi
441 |     PS1="$ps1pc_start$gitstring$ps1pc_end"
442 |   else
443 |     printf -- "$printf_format" "$gitstring"
444 |   fi
445 | }
446 | 


--------------------------------------------------------------------------------
/old/docker/scripts/z.sh:
--------------------------------------------------------------------------------
  1 | # Copyright (c) 2009 rupa deadwyler under the WTFPL license
  2 | 
  3 | # maintains a jump-list of the directories you actually use
  4 | #
  5 | # INSTALL:
  6 | #     * put something like this in your .bashrc/.zshrc:
  7 | #         . /path/to/z.sh
  8 | #     * cd around for a while to build up the db
  9 | #     * PROFIT!!
 10 | #     * optionally:
 11 | #         set $_Z_CMD in .bashrc/.zshrc to change the command (default z).
 12 | #         set $_Z_DATA in .bashrc/.zshrc to change the datafile (default ~/.z).
 13 | #         set $_Z_NO_RESOLVE_SYMLINKS to prevent symlink resolution.
 14 | #         set $_Z_NO_PROMPT_COMMAND if you're handling PROMPT_COMMAND yourself.
 15 | #         set $_Z_EXCLUDE_DIRS to an array of directories to exclude.
 16 | #
 17 | # USE:
 18 | #     * z foo     # cd to most frecent dir matching foo
 19 | #     * z foo bar # cd to most frecent dir matching foo and bar
 20 | #     * z -r foo  # cd to highest ranked dir matching foo
 21 | #     * z -t foo  # cd to most recently accessed dir matching foo
 22 | #     * z -l foo  # list matches instead of cd
 23 | #     * z -c foo  # restrict matches to subdirs of $PWD
 24 | 
 25 | [ -d "${_Z_DATA:-$HOME/.z}" ] && {
 26 |     echo "ERROR: z.sh's datafile (${_Z_DATA:-$HOME/.z}) is a directory."
 27 | }
 28 | 
 29 | _z() {
 30 | 
 31 |     local datafile="${_Z_DATA:-$HOME/.z}"
 32 | 
 33 |     # bail if we don't own ~/.z (we're another user but our ENV is still set)
 34 |     [ -f "$datafile" -a ! -O "$datafile" ] && return
 35 | 
 36 |     # add entries
 37 |     if [ "$1" = "--add" ]; then
 38 |         shift
 39 | 
 40 |         # $HOME isn't worth matching
 41 |         [ "$*" = "$HOME" ] && return
 42 | 
 43 |         # don't track excluded dirs
 44 |         local exclude
 45 |         for exclude in "${_Z_EXCLUDE_DIRS[@]}"; do
 46 |             [ "$*" = "$exclude" ] && return
 47 |         done
 48 | 
 49 |         # maintain the data file
 50 |         local tempfile="$datafile.$RANDOM"
 51 |         while read line; do
 52 |             # only count directories
 53 |             [ -d "${line%%\|*}" ] && echo $line
 54 |         done < "$datafile" | awk -v path="$*" -v now="$(date +%s)" -F"|" '
 55 |             BEGIN {
 56 |                 rank[path] = 1
 57 |                 time[path] = now
 58 |             }
 59 |             $2 >= 1 {
 60 |                 # drop ranks below 1
 61 |                 if( $1 == path ) {
 62 |                     rank[$1] = $2 + 1
 63 |                     time[$1] = now
 64 |                 } else {
 65 |                     rank[$1] = $2
 66 |                     time[$1] = $3
 67 |                 }
 68 |                 count += $2
 69 |             }
 70 |             END {
 71 |                 if( count > 6000 ) {
 72 |                     # aging
 73 |                     for( x in rank ) print x "|" 0.99*rank[x] "|" time[x]
 74 |                 } else for( x in rank ) print x "|" rank[x] "|" time[x]
 75 |             }
 76 |         ' 2>/dev/null >| "$tempfile"
 77 |         # do our best to avoid clobbering the datafile in a race condition
 78 |         if [ $? -ne 0 -a -f "$datafile" ]; then
 79 |             env rm -f "$tempfile"
 80 |         else
 81 |             env mv -f "$tempfile" "$datafile" || env rm -f "$tempfile"
 82 |         fi
 83 | 
 84 |     # tab completion
 85 |     elif [ "$1" = "--complete" ]; then
 86 |         while read line; do
 87 |             [ -d "${line%%\|*}" ] && echo $line
 88 |         done < "$datafile" | awk -v q="$2" -F"|" '
 89 |             BEGIN {
 90 |                 if( q == tolower(q) ) imatch = 1
 91 |                 split(substr(q, 3), fnd, " ")
 92 |             }
 93 |             {
 94 |                 if( imatch ) {
 95 |                     for( x in fnd ) tolower($1) !~ tolower(fnd[x]) && $1 = ""
 96 |                 } else {
 97 |                     for( x in fnd ) $1 !~ fnd[x] && $1 = ""
 98 |                 }
 99 |                 if( $1 ) print $1
100 |             }
101 |         ' 2>/dev/null
102 | 
103 |     else
104 |         # list/go
105 |         while [ "$1" ]; do case "$1" in
106 |             --) while [ "$1" ]; do shift; local fnd="$fnd${fnd:+ }$1";done;;
107 |             -*) local opt=${1:1}; while [ "$opt" ]; do case ${opt:0:1} in
108 |                     c) local fnd="^$PWD $fnd";;
109 |                     h) echo "${_Z_CMD:-z} [-chlrtx] args" >&2; return;;
110 |                     x) sed -i "\:^${PWD}|.*:d" "$datafile";;
111 |                     l) local list=1;;
112 |                     r) local typ="rank";;
113 |                     t) local typ="recent";;
114 |                 esac; opt=${opt:1}; done;;
115 |              *) local fnd="$fnd${fnd:+ }$1";;
116 |         esac; local last=$1; shift; done
117 |         [ "$fnd" -a "$fnd" != "^$PWD " ] || local list=1
118 | 
119 |         # if we hit enter on a completion just go there
120 |         case "$last" in
121 |             # completions will always start with /
122 |             /*) [ -z "$list" -a -d "$last" ] && cd "$last" && return;;
123 |         esac
124 | 
125 |         # no file yet
126 |         [ -f "$datafile" ] || return
127 | 
128 |         local cd
129 |         cd="$(while read line; do
130 |             [ -d "${line%%\|*}" ] && echo $line
131 |         done < "$datafile" | awk -v t="$(date +%s)" -v list="$list" -v typ="$typ" -v q="$fnd" -F"|" '
132 |             function frecent(rank, time) {
133 |                 # relate frequency and time
134 |                 dx = t - time
135 |                 if( dx < 3600 ) return rank * 4
136 |                 if( dx < 86400 ) return rank * 2
137 |                 if( dx < 604800 ) return rank / 2
138 |                 return rank / 4
139 |             }
140 |             function output(files, out, common) {
141 |                 # list or return the desired directory
142 |                 if( list ) {
143 |                     cmd = "sort -n >&2"
144 |                     for( x in files ) {
145 |                         if( files[x] ) printf "%-10s %s\n", files[x], x | cmd
146 |                     }
147 |                     if( common ) {
148 |                         printf "%-10s %s\n", "common:", common > "/dev/stderr"
149 |                     }
150 |                 } else {
151 |                     if( common ) out = common
152 |                     print out
153 |                 }
154 |             }
155 |             function common(matches) {
156 |                 # find the common root of a list of matches, if it exists
157 |                 for( x in matches ) {
158 |                     if( matches[x] && (!short || length(x) < length(short)) ) {
159 |                         short = x
160 |                     }
161 |                 }
162 |                 if( short == "/" ) return
163 |                 # use a copy to escape special characters, as we want to return
164 |                 # the original. yeah, this escaping is awful.
165 |                 clean_short = short
166 |                 gsub(/[\(\)\[\]\|]/, "\\\\&", clean_short)
167 |                 for( x in matches ) if( matches[x] && x !~ clean_short ) return
168 |                 return short
169 |             }
170 |             BEGIN { split(q, words, " "); hi_rank = ihi_rank = -9999999999 }
171 |             {
172 |                 if( typ == "rank" ) {
173 |                     rank = $2
174 |                 } else if( typ == "recent" ) {
175 |                     rank = $3 - t
176 |                 } else rank = frecent($2, $3)
177 |                 matches[$1] = imatches[$1] = rank
178 |                 for( x in words ) {
179 |                     if( $1 !~ words[x] ) delete matches[$1]
180 |                     if( tolower($1) !~ tolower(words[x]) ) delete imatches[$1]
181 |                 }
182 |                 if( matches[$1] && matches[$1] > hi_rank ) {
183 |                     best_match = $1
184 |                     hi_rank = matches[$1]
185 |                 } else if( imatches[$1] && imatches[$1] > ihi_rank ) {
186 |                     ibest_match = $1
187 |                     ihi_rank = imatches[$1]
188 |                 }
189 |             }
190 |             END {
191 |                 # prefer case sensitive
192 |                 if( best_match ) {
193 |                     output(matches, best_match, common(matches))
194 |                 } else if( ibest_match ) {
195 |                     output(imatches, ibest_match, common(imatches))
196 |                 }
197 |             }
198 |         ')"
199 |         [ $? -gt 0 ] && return
200 |         [ "$cd" ] && cd "$cd"
201 |     fi
202 | }
203 | 
204 | alias ${_Z_CMD:-z}='_z 2>&1'
205 | 
206 | [ "$_Z_NO_RESOLVE_SYMLINKS" ] || _Z_RESOLVE_SYMLINKS="-P"
207 | 
208 | if compctl >/dev/null 2>&1; then
209 |     # zsh
210 |     [ "$_Z_NO_PROMPT_COMMAND" ] || {
211 |         # populate directory list, avoid clobbering any other precmds.
212 |         if [ "$_Z_NO_RESOLVE_SYMLINKS" ]; then
213 |             _z_precmd() {
214 |                 _z --add "${PWD:a}"
215 |             }
216 |         else
217 |             _z_precmd() {
218 |                 _z --add "${PWD:A}"
219 |             }
220 |         fi
221 |         [[ -n "${precmd_functions[(r)_z_precmd]}" ]] || {
222 |             precmd_functions[$(($#precmd_functions+1))]=_z_precmd
223 |         }
224 |     }
225 |     _z_zsh_tab_completion() {
226 |         # tab completion
227 |         local compl
228 |         read -l compl
229 |         reply=(${(f)"$(_z --complete "$compl")"})
230 |     }
231 |     compctl -U -K _z_zsh_tab_completion _z
232 | elif complete >/dev/null 2>&1; then
233 |     # bash
234 |     # tab completion
235 |     complete -o filenames -C '_z --complete "$COMP_LINE"' ${_Z_CMD:-z}
236 |     [ "$_Z_NO_PROMPT_COMMAND" ] || {
237 |         # populate directory list. avoid clobbering other PROMPT_COMMANDs.
238 |         grep "_z --add" <<< "$PROMPT_COMMAND" >/dev/null || {
239 |             PROMPT_COMMAND="$PROMPT_COMMAND"$'\n''_z --add "$(pwd '$_Z_RESOLVE_SYMLINKS' 2>/dev/null)" 2>/dev/null;'
240 |         }
241 |     }
242 | fi
243 | 


--------------------------------------------------------------------------------
/old/docker/serf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Michieljoris/node-haproxy/54d2033461051eca7c445e4599f69db72f9ee004/old/docker/serf


--------------------------------------------------------------------------------
/old/docker/snapshot:
--------------------------------------------------------------------------------
 1 | alive: haproxy 172.17.0.125:7946
 2 | alive: agent-two 172.17.42.1:7946
 3 | clock: 1
 4 | clock: 2
 5 | not-alive: agent-two
 6 | alive: agent-two 172.17.42.1:7946
 7 | clock: 3
 8 | leave
 9 | clock: 4
10 | alive: haproxy 172.17.0.125:7946
11 | clock: 5
12 | alive: agent-two 172.17.42.1:7946
13 | clock: 6
14 | not-alive: agent-two
15 | alive: agent-two 172.17.42.1:7946
16 | clock: 7
17 | query-clock: 7
18 | query-clock: 8
19 | query-clock: 9
20 | query-clock: 10
21 | query-clock: 11
22 | query-clock: 14
23 | query-clock: 15
24 | query-clock: 16
25 | query-clock: 17
26 | query-clock: 18
27 | query-clock: 19
28 | query-clock: 20
29 | query-clock: 21
30 | query-clock: 22
31 | query-clock: 23
32 | query-clock: 24
33 | query-clock: 25
34 | query-clock: 26
35 | query-clock: 27
36 | query-clock: 28
37 | query-clock: 29
38 | query-clock: 30
39 | event-clock: 1
40 | event-clock: 2
41 | query-clock: 31
42 | query-clock: 32
43 | event-clock: 3
44 | clock: 8
45 | not-alive: agent-two
46 | leave
47 | 


--------------------------------------------------------------------------------
/old/etc-default-haproxy:
--------------------------------------------------------------------------------
1 | # Set ENABLED to 1 if you want the init script to start haproxy.
2 | ENABLED=1
3 | # Add extra flags here.
4 | #EXTRAOPTS="-de -m 16"
5 | 


--------------------------------------------------------------------------------
/old/etc-init.d-haproxy:
--------------------------------------------------------------------------------
  1 | #!/bin/sh
  2 | ### BEGIN INIT INFO
  3 | # Provides:          haproxy
  4 | # Required-Start:    $local_fs $network $remote_fs
  5 | # Required-Stop:     $local_fs $remote_fs
  6 | # Default-Start:     2 3 4 5
  7 | # Default-Stop:      0 1 6
  8 | # Short-Description: fast and reliable load balancing reverse proxy
  9 | # Description:       This file should be used to start and stop haproxy.
 10 | ### END INIT INFO
 11 | 
 12 | # Author: Arnaud Cornet 
 13 | 
 14 | PATH=/sbin:/usr/sbin:/bin:/usr/bin
 15 | PIDFILE=/var/run/haproxy.pid
 16 | CONFIG=/etc/haproxy/haproxy.cfg
 17 | # PIDFILE=/home/michieljoris/mysrc/javascript/node-haproxy/haproxy.pid
 18 | # CONFIG=/home/michieljoris/mysrc/javascript/node-haproxy/haproxy.cfg
 19 | HAPROXY=/usr/local/sbin/haproxy
 20 | EXTRAOPTS=
 21 | ENABLED=1
 22 | 
 23 | test -x $HAPROXY || exit 0
 24 | test -f "$CONFIG" || exit 0
 25 | 
 26 | if [ -e /etc/default/haproxy ]; then
 27 | 	. /etc/default/haproxy
 28 | fi
 29 | 
 30 | test "$ENABLED" != "0" || exit 0
 31 | 
 32 | [ -f /etc/default/rcS ] && . /etc/default/rcS
 33 | . /lib/lsb/init-functions
 34 | 
 35 | 
 36 | haproxy_start()
 37 | {
 38 | 	start-stop-daemon --start --pidfile "$PIDFILE" \
 39 | 		--exec $HAPROXY -- -f "$CONFIG" -D -p "$PIDFILE" \
 40 | 		$EXTRAOPTS || return 2
 41 | 	return 0
 42 | }
 43 | 
 44 | haproxy_stop()
 45 | {
 46 | 	if [ ! -f $PIDFILE ] ; then
 47 | 		# This is a success according to LSB
 48 | 		return 0
 49 | 	fi
 50 | 	for pid in $(cat $PIDFILE) ; do
 51 | 		/bin/kill $pid || return 4
 52 | 	done
 53 | 	rm -f $PIDFILE
 54 | 	return 0
 55 | }
 56 | 
 57 | haproxy_reload()
 58 | {
 59 | 	$HAPROXY -f "$CONFIG" -p $PIDFILE -D $EXTRAOPTS -sf $(cat $PIDFILE) \
 60 | 		|| return 2
 61 | 	return 0
 62 | }
 63 | 
 64 | haproxy_status()
 65 | {
 66 | 	if [ ! -f $PIDFILE ] ; then
 67 | 		# program not running
 68 | 		return 3
 69 | 	fi
 70 | 
 71 | 	for pid in $(cat $PIDFILE) ; do
 72 | 		if ! ps --no-headers p "$pid" | grep haproxy > /dev/null ; then
 73 | 			# program running, bogus pidfile
 74 | 			return 1
 75 | 		fi
 76 | 	done
 77 | 
 78 | 	return 0
 79 | }
 80 | 
 81 | 
 82 | case "$1" in
 83 | start)
 84 | 	log_daemon_msg "Starting haproxy" "haproxy"
 85 | 	haproxy_start
 86 | 	ret=$?
 87 | 	case "$ret" in
 88 | 	0)
 89 | 		log_end_msg 0
 90 | 		;;
 91 | 	1)
 92 | 		log_end_msg 1
 93 | 		echo "pid file '$PIDFILE' found, haproxy not started."
 94 | 		;;
 95 | 	2)
 96 | 		log_end_msg 1
 97 | 		;;
 98 | 	esac
 99 | 	exit $ret
100 | 	;;
101 | stop)
102 | 	log_daemon_msg "Stopping haproxy" "haproxy"
103 | 	haproxy_stop
104 | 	ret=$?
105 | 	case "$ret" in
106 | 	0|1)
107 | 		log_end_msg 0
108 | 		;;
109 | 	2)
110 | 		log_end_msg 1
111 | 		;;
112 | 	esac
113 | 	exit $ret
114 | 	;;
115 | reload|force-reload)
116 | 	log_daemon_msg "Reloading haproxy" "haproxy"
117 | 	haproxy_reload
118 | 	case "$?" in
119 | 	0|1)
120 | 		log_end_msg 0
121 | 		;;
122 | 	2)
123 | 		log_end_msg 1
124 | 		;;
125 | 	esac
126 | 	;;
127 | restart)
128 | 	log_daemon_msg "Restarting haproxy" "haproxy"
129 | 	haproxy_stop
130 | 	haproxy_start
131 | 	case "$?" in
132 | 	0)
133 | 		log_end_msg 0
134 | 		;;
135 | 	1)
136 | 		log_end_msg 1
137 | 		;;
138 | 	2)
139 | 		log_end_msg 1
140 | 		;;
141 | 	esac
142 | 	;;
143 | status)
144 | 	haproxy_status
145 | 	ret=$?
146 | 	case "$ret" in
147 | 	0)
148 | 		echo "haproxy is running."
149 | 		;;
150 | 	1)
151 | 		echo "haproxy dead, but $PIDFILE exists."
152 | 		;;
153 | 	*)
154 | 		echo "haproxy not running."
155 | 		;;
156 | 	esac
157 | 	exit $ret
158 | 	;;
159 | *)
160 | 	echo "Usage: /etc/init.d/haproxy {start|stop|reload|restart|status}"
161 | 	exit 2
162 | 	;;
163 | esac
164 | 
165 | :
166 | 


--------------------------------------------------------------------------------
/old/server.js:
--------------------------------------------------------------------------------
  1 | #!/usr/bin/env node
  2 | var Aqueduct = require('..')
  3 |   , Hapi = require('hapi')
  4 |   , shoe = require('shoe')
  5 |   , util = require('util')
  6 |   ;
  7 | 
  8 | // require('nodetime').profile({
  9 | //     accountKey: '1765a180c09b73ea0a7d7262ff6dc60d776bf395', 
 10 | //     appName: 'Aqueuct'
 11 | //   });
 12 | 
 13 | var optimist = require('optimist')
 14 |             .options({
 15 |               host: {
 16 |                 default : '0.0.0.0',
 17 |                 describe: 'host to bind to'
 18 |               },
 19 |               port: {
 20 |                 default : 10000,
 21 |                 describe: 'port to bind to'
 22 |               },
 23 |               label: {
 24 |                 describe: 'logical label for this aqueduct'
 25 |               },
 26 |               thalassaHost: {
 27 |                 default : '127.0.0.1',
 28 |                 describe: 'host of the Thalassa server'
 29 |               },
 30 |               thalassaPort: {
 31 |                 default : 5001,
 32 |                 describe: 'socket port of the Thalassa server'
 33 |               },
 34 |               thalassaApiPort: {
 35 |                 default : 9000,
 36 |                 describe: 'http API port of the Thalassa server'
 37 |               },
 38 |               haproxySocketPath: {
 39 |                 default: '/tmp/haproxy.status.sock',
 40 |                 describe: 'path to Haproxy socket file'
 41 |               },
 42 |               haproxyPidPath: {
 43 |                 default: '/var/run/haproxy.pid',
 44 |                 describe: 'path to  Haproxy pid file'
 45 |               },
 46 |               haproxyCfgPath: {
 47 |                 default: '/etc/haproxy/haproxy.cfg',
 48 |                 describe: 'generated Haproxy config location'
 49 |               },
 50 |               templateFile: {
 51 |                 default: __dirname + '/../default.haproxycfg.tmpl',
 52 |                 describe: 'template used to generate Haproxy config'
 53 |               },
 54 |               persistence: {
 55 |                 describe: 'directory to save configuration'
 56 |               },
 57 |               dbPath: {
 58 |                 default : __dirname + '/db',
 59 |                 describe: 'filesystem path for leveldb'
 60 |               },
 61 |               sudo: {
 62 |                 describe: 'use sudo when starting haproxy'
 63 |               },
 64 |               debug: {
 65 |                 boolean: true,
 66 |                 describe: 'enabled debug logging'
 67 |               },
 68 |               help: {
 69 |                 alias: 'h'
 70 |               }
 71 |             });
 72 | 
 73 | var argv = optimist.argv;
 74 | if (argv.h) {
 75 |   optimist.showHelp();
 76 |   process.exit(0);
 77 | }
 78 | 
 79 | var log = argv.log = require('../lib/defaultLogger')( (argv.debug == true) ? 'debug' : 'error' );
 80 | var aqueduct = new Aqueduct(argv);
 81 | var server = new Hapi.Server({
 82 | 	port: argv.port,
 83 | 	host: argv.host
 84 | });
 85 | server.route(aqueduct.apiRoutes());
 86 | 
 87 | // anything at the top level goes to index.html
 88 | server.route({ method: 'GET', path: '/{p}', handler: { file: { path: __dirname+'/../public/index.html' }}});
 89 | 
 90 | server.route({
 91 |     method: 'GET',
 92 |     path: '/{path*}',
 93 |     handler: {
 94 |         directory: { path: __dirname + '/../public', listing: false, index: true }
 95 |     }
 96 | });
 97 | 
 98 | //Setup websocket to the client/browser
 99 | var sock = shoe();
100 | sock.install(server.listener, '/aqueductStreams');
101 | sock.on('connection', function (stream) {
102 |   var s = aqueduct.createMuxStream();
103 |   stream.pipe(s).pipe(stream);
104 | 
105 |   stream.on('end', function () {
106 |     s.destroy();
107 |   })
108 | 
109 | });
110 | 
111 | sock.on('log', function (severity, msg) {
112 |   log(severity, msg);
113 | })
114 | 
115 | server.start(function () {
116 |   log('info', util.format("Thalassa Aqueduct listening on %s:%s", argv.host, argv.port));
117 | });
118 | 
119 | aqueduct.haproxyManager.on('configChanged', function() { log('debug', 'Config changed') });
120 | aqueduct.haproxyManager.on('reloaded', function() { log('debug', 'Haproxy reloaded') });
121 | aqueduct.data.stats.on('changes', function (it) { log('debug', it.state.id, it.state.status )})
122 | 
123 | // var memwatch = require('memwatch');
124 | // memwatch.on('leak', function(info) { log('debug', 'leak', info); });
125 | // memwatch.on('stats', function(stats) { log('debug', 'stats', stats); });
126 | // var hd = new memwatch.HeapDiff();
127 | 
128 | // setInterval(function () {
129 | //   log('debug', 'diff', hd.end().after.size);
130 | //   hd = new memwatch.HeapDiff();
131 | // }, 10000);
132 | 
133 | 


--------------------------------------------------------------------------------
/old/test-api.js:
--------------------------------------------------------------------------------
  1 | 
  2 | // api.validatepostbackendsubscription = function () {
  3 | //   return {
  4 | //     payload: {
  5 | //      key     : hapi.types.string().optional(),
  6 | //      name    : hapi.types.string().optional(),
  7 | //      version : hapi.types.string()
  8 | //     }
  9 | //   };
 10 | // };
 11 | 
 12 | 
 13 | 
 14 | // api.validateputbackend = function () {
 15 | //   return {
 16 | //     payload: {
 17 | //       _type  : hapi.types.string().optional(),
 18 | //      key     : hapi.types.string(),
 19 | //      type    : hapi.types.string().valid(['dynamic', 'static']),
 20 | //      name    : hapi.types.string().optional(),
 21 | //      version : hapi.types.string().optional(),
 22 | //      balance : hapi.types.string().optional(),
 23 | //      host    : hapi.types.string().optional(),
 24 | //      mod    : hapi.types.string().valid(['http', 'tcp']).optional(),
 25 | //      members : hapi.types.array().optional(),
 26 | //      natives : hapi.types.array().optional(),
 27 | //      health  : hapi.types.object({
 28 | //                 method: hapi.types.string().valid(['get','post']).optional(),
 29 | //                 uri: hapi.types.string().optional(),
 30 | //                 httpversion: hapi.types.string().valid(['http/1.0', 'http/1.1']).optional(),
 31 | //                 interval: hapi.types.number().min(1).optional()
 32 | //               }).optional()
 33 | //     }
 34 | //   };
 35 | // };
 36 | 
 37 | 
 38 | // api.validateputfrontend = function () {
 39 | //   return {
 40 | //     payload: {
 41 | //       _type     : hapi.types.string().optional(),
 42 | //       key       : hapi.types.string(),
 43 | //       bind      : hapi.types.string(),
 44 | //       backend   : hapi.types.string(),
 45 | //       mode      : hapi.types.string().valid(['http', 'tcp']).optional(),
 46 | //       keepalive : hapi.types.string().valid(['default','close','server-close']).optional(),
 47 | //       rules     : hapi.types.array().optional(),
 48 | //       natives   : hapi.types.array().optional()
 49 | //     }
 50 | //   };
 51 | // };
 52 | 
 53 | var haproxy = module.exports({ ipc: true });
 54 | 
 55 | // setTimeout(function() {
 56 | //   haproxy.putBackend('backend1', {
 57 | //     // "type" : "dynamic|static" 
 58 | //     "type" : "static" 
 59 | //     // , "name" : "foo" // only required if type = dynamic
 60 | //     // , "version" : "1.0.0" // only required if type = dynamic
 61 | //     // , "balance" : "roundrobin|source" // defaults to roundrobin
 62 | //     // , "host" : "myapp.com"  // default: undefined, if specified request to member will contain this host header
 63 | //     // , "health" : {                 // optional health check
 64 | //     //     "method": "GET"            // HTTP method
 65 | //     //     , "uri": "/checkity-check"   // URI to call
 66 | //     //     , "httpVersion": "HTTP/1.0"  // HTTP/1.0 or HTTP/1.1 `host` required if HTTP/1.1
 67 | //     //     , "interval": 5000           // period to check, milliseconds
 68 | //     // }
 69 | //     // , "mode" : "http|tcp" // default: http
 70 | //     , "natives": []  // array of strings of raw config USE SPARINGLY!!
 71 | //     , "members" : [
 72 | //       {
 73 | //         // "name": "myapp",
 74 | //         // "version": "1.0.0",
 75 | //         "host": "192.168.1.184",
 76 | //         "port": 3000
 77 | //         // "lastKnown": 1378762056885,
 78 | //         // "meta": {
 79 | //         //     "hostname": "dev-use1b-pr-01-myapp-01x00x00-01",
 80 | //         //     "pid": 17941,
 81 | //         //     "registered": 1378740834616
 82 | //         // },
 83 | //         // "id": "/myapp/1.0.0/10.10.240.121/8080"
 84 | //       },
 85 | //       // {
 86 | //       //     // "name": "myapp",
 87 | //       //     // "version": "1.0.0",
 88 | //       //     "host": "192.168.1.184",
 89 | //       //     "port": 8002
 90 | //       //     // "lastKnown": 1378762060226,
 91 | //       //     // "meta": {
 92 | //       //     //     "hostname": "dev-use1b-pr-01-myapp-01x00x00-02",
 93 | //       //     //     "pid": 18020,
 94 | //       //     //     "registered": 1378762079883
 95 | //       //     // },
 96 | //       //     // "id": "/myapp/1.0.0/10.10.240.80/8080"
 97 | //       // }
 98 |         
 99 | //     ] // if type = dynamic this is dynamically populated based on role/version subscription
100 | //     // otherwise expects { host: '10.10.10.10', port: 8080}
101 | //   });
102 | 
103 | //   haproxy.putFrontend('www1', {
104 | //     "bind": "*:10000" // IP and ports to bind to, comma separated, host may be *
105 | //     , "backend": "backend1"      // the default backend to route to, it must be defined already
106 | //     , "mode": "http"         // default: http, expects tcp|http
107 | //     , "keepalive": "close"  // default: "default", expects default|close|server-close
108 | //     , "rules": []           // array of rules, see next section
109 | //     , "natives": []         // array of strings of raw config USE SPARINGLY!!
110 | //   });
111 | 
112 | 
113 | 
114 | //   var r = haproxy.getBackends();
115 | //   var f = haproxy.getFrontends();
116 | 
117 | //   log('BACKENDS-=-------------------:\n', util.inspect(r, { colors: true, depth:10 }));
118 | //   log('FRONTENDS-=-------------------:\n', util.inspect(f, { colors: true, depth:10 }));
119 | 
120 | // }, 5000);
121 | // test
122 | // setInterval(function() {
123 | 
124 | //   log('CONFIG------------------------:\n', haproxy.getHaproxyConfig());
125 | // },3000);
126 | 


--------------------------------------------------------------------------------
/old/test-serf.js:
--------------------------------------------------------------------------------
 1 | var SerfRPC = require("serf-rpc");
 2 | var serf = new SerfRPC();
 3 | 
 4 | serf.connect(function(err){
 5 |     if(err)
 6 |         throw err;
 7 | 
 8 |     serf.event({"Name": "deploy", "Payload": "4f33de567283e4a456539b8dc493ae8a853a93f6", "Coalesce": false}, function(err, response){
 9 |         if(err)
10 |             throw err;
11 |         else
12 |             console.log("Triggered the event!");
13 |     });
14 |     serf.join({"Existing": ["172.17.0.125"], "Replay": false}, function(err, res) {
15 | 	if(err)
16 | 	    throw err;
17 | 	else
18 |             console.log("joined");
19 |     
20 |         
21 |         serf["members-filtered"]({ Name: "hap.*" }, function(err, res) {
22 | 	    if(err)
23 | 	        throw err;
24 | 	    else
25 |                 console.log("Members\n", res);
26 |     
27 |         });
28 |         
29 |     });
30 | });
31 | 


--------------------------------------------------------------------------------
/old/testhap.js:
--------------------------------------------------------------------------------
 1 | require('logthis').config({ _on: true,
 2 |                             'Data': 'debug' ,
 3 |                             'HaproxyManager': 'debug' ,
 4 |                             'HaproxyStats': 'debug',
 5 |                             'Db': 'debug',
 6 |                             'haproxy': 'debug'
 7 |                           });
 8 | 
 9 | var log = require('logthis').logger._create('haproxy');
10 |   
11 | var Haproxy = require('haproxy');
12 | var resolve = require('path').resolve;
13 |   
14 | var defaults = {
15 |     host: '0.0.0.0',
16 |     port: 10000,
17 |     // haproxySocketPath: '/tmp/haproxy.status.sock',
18 |     // haproxyPidPath: '/var/run/haproxy.pid',
19 |     // haproxyCfgPath: '/etc/haproxy/haproxy.cfg',
20 |     haproxySocketPath: __dirname + '/haproxy.status.sock',
21 |     haproxyPidPath: __dirname + '/haproxy.pid',
22 |     haproxyCfgPath: __dirname + '/haproxy.cfg',
23 |     templateFile: __dirname + '/default.haproxycfg.tmpl',
24 |     persistence: __dirname + '/persisted',
25 |     dbPath:  __dirname + '/db',
26 |     // sudo: 'use sudo when starting haproxy'
27 |     sudo: 'use sudo when starting haproxy'
28 | };
29 | 
30 | log('hello', defaults);
31 | 
32 | 
33 | var opts = defaults;
34 | var haproxy = new Haproxy(opts.haproxySocketPath, {
35 |     config:  resolve(opts.haproxyCfgPath),
36 |     pidFile: resolve(opts.haproxyPidPath),
37 |     prefix: (opts.sudo) ? 'sudo' : undefined,
38 |     which: __dirname + '/bin/haproxy'
39 | });
40 | 
41 | haproxy.stop(function(err) {
42 |     haproxy.start(function(err) {
43 |         log(err);
44 |         haproxy.running(function(err, running) {
45 |             log('running:', err, running);
46 |         });
47 | 
48 |         haproxy.stat('-1', '-1', '-1', function (err, stats) {
49 | 
50 |     
51 |         });
52 |     });
53 | });
54 | 
55 | 


--------------------------------------------------------------------------------
/package.js:
--------------------------------------------------------------------------------
  1 | // This file is the source for constructing a `package.json` file.
  2 | // JSON is a wonderful interchange format, but due to the fact that the
  3 | // [JSON Specification](http://json.org) does not allow for comments, I find
  4 | // it horrid for self documenting examples.
  5 | //
  6 | // JavaScript allows for comments and inherently allows JSON. This file will
  7 | // act as the source for building a `package.json` file that also manages this
  8 | // package.
  9 | //
 10 | // It is the closest I can get to a self-documenting `package.json` file.
 11 | 
 12 | 
 13 | 
 14 | // The `package.json` file always consists of one top level object, which is
 15 | // what we export here in a [Node.js](http://nodejs.org) friendly way that
 16 | // will allow us to build our `package.json` file. A real `package.json` file
 17 | // will not contain the `exports = ` definition, nor any of these comments.
 18 | module.exports = {
 19 |   // Many of the following `package.json` parameters are optional depending
 20 |   // on whether or not this package will ever be published, and depending
 21 |   // on how much management we want to delegate to npm. I did not mark
 22 |   // optional vs. not-optional for the parameters, as a `package.json` file
 23 |   // is by its nature always optional.
 24 |     
 25 |   // Our npm package name needs to be unique only if we are going to publish
 26 |   // our package into an npm registry. If we aren't going to publish the 
 27 |   // package the name can be anything we want.
 28 |   //
 29 |   // Leave off redundant affixes like `node-package` or `package-js`. 
 30 |   // We know it is JavaScript for Node.
 31 |   "name": "node-haproxy",
 32 |   // A single line, or sometimes slightly longer, description of our package.
 33 |   "description": "",
 34 |   // [npm](http://npmjs.org) enforces the X.Y.Z semantic version 
 35 |   // scheme that is described at [http://semver.org/](http://semver.org/)
 36 |   // and we should follow this versioning for our package.
 37 |   //Comment out go auto increase version on execution of this file
 38 |   // "version": "0.1.0",
 39 |   // URL to the homepage for this package.
 40 |   "homepage": "https://github.com/michieljoris/node-haproxy",
 41 |   // An array of keywords used to describe this package to search engines,
 42 |   // mainly for people searching within the npm universe.
 43 |   "keywords": [
 44 |         
 45 |   ],
 46 |   // Where is the source of truth for this code, and what type of repo is it?
 47 |   "repository": {
 48 |     "type": "git",
 49 |     "url": "https://github.com/michieljoris/node-haproxy.git"
 50 |   },
 51 |   // Every package should have at least one author. There are a couple of
 52 |   // formats for the author. I prefer the explicit object format as follows:
 53 |   "author": {
 54 |     "name": "Michiel van Oosten",
 55 |     "email": "mail@axion5.net",
 56 |     "url": "http://www.axion5.net/"
 57 |   },
 58 |   // What licenses govern this code, and where is the license associated
 59 |   // with this code?
 60 |   // The complex form, "licenses", is an array of objects.
 61 |   // The simplest form is "license", and may point to just a string that
 62 |   // represents the standard name of the license, like "MIT".
 63 |   "licenses": [
 64 |     {
 65 |       "type": "MIT",
 66 |       "url": "http://github.com/michieljoris/node-haproxy/blob/master/LICENSE.txt"
 67 |     }
 68 |   ],
 69 |   // If there is a file that should be loaded when require()ing this 
 70 |   // folder-as-a-package, declare this file here, relative to our package 
 71 |   // structure.
 72 |   "main": "src/api.js",
 73 |   // Essentially, which Node.js platforms do we support? These are glob
 74 |   // like expressions supported by the 
 75 |   // [npm semantic version parser](https://npmjs.org/doc/semver.html), 
 76 |   // and the below version means what it looks like: 
 77 |   //
 78 |     
 79 |   //Installs a binary script called node-haproxy which is linked to
 80 |   //./bin/node-haproxy in the local package.
 81 | 
 82 |   //If we have installed this package globally using npm install node-haproxy
 83 |   //-g we will be able to call this new command node-haproxy from anywhere on
 84 |   //our system.
 85 |   "bin": {
 86 |     "node-haproxy": "bin/node-haproxy.sh"
 87 |   },
 88 |     
 89 |   // require a Node.js installation that is greater than or equal to version 0.6.0
 90 |   "engines": {
 91 |     "node": ">= 0.6.x"
 92 |   },
 93 |   // What other modules/libraries do we require for our own module?
 94 |   // The beauty of this dependencies block is that these modules will
 95 |   // be downloaded magically when we run npm install from within our
 96 |   // directory. npm itself will sort out any dependency conflicts within
 97 |   // our own dependencies and we can be pretty much assured that the
 98 |   // modules we need will be ready to run.
 99 |   //
100 |   // **NOTE:** We don't have any dependencies for this module. See the
101 |   // `devDependencies` block for the way to include dependencies.
102 |   "dependencies": {
103 |     "dougs_vow": "*",
104 |     "fs-extra": "0.18.x",
105 |     "logthis": "*",
106 |         
107 |     // ,"hapi": "~1.20.0",
108 |     "crdt": "~3.5.1",
109 |     "handlebars": "~1.0.12",
110 |     "debounce": "0.0.2",
111 |     "deep-equal": "0.0.0",
112 |     // "shoe": "~0.0.11",
113 |     // "thalassa": "~0.4.0",
114 |     "haproxy": "0.0.3", //up to 0.0.4 as of July/14
115 |     // "cli-color": "~0.2.2",
116 |     "changeset": "0.0.5",
117 |     "optimist": "~0.6.0",
118 |     // "websocket-stream": "~0.2.0",
119 |     // "ws": "~0.4.27",
120 |     "xtend": "~2.0.6",
121 |     // "mux-demux": "~3.7.8",
122 |     // "through": "~2.3.4",
123 |     "extend": "~1.2.0",
124 |     "level": "~0.18.0",
125 |     "mkdirp": "~0.3.5",
126 |     "flic": "^1.1.2",
127 |     "node-ipc": "^1.1.13"
128 |     // "redis": "^0.12.1"
129 |     // "split": "~0.2.10",
130 |     // "browserify": "~2.25.1",
131 |     // "CBuffer": "~0.1.4"
132 |         
133 |     // "colors": "*",
134 |   },
135 |   // What dependencies are useful only for developers?
136 |   // Installed when we `npm install` in our working directory, but not 
137 |   // when people require our package in their own package.json. This is the 
138 |   // usual and accepted place to put test frameworks and documentation
139 |   // tools.
140 |   //
141 |   // The packages we depend on for development:
142 |   //
143 |   // * **fs-extra**: Mixin for the fs (filesystem) module.
144 |   // * **doccoh**: Documentation utility for this code.
145 |   "devDependencies": {
146 |     // "doccoh": "*"
147 |     "docco": "*"
148 |   },
149 |   // Should this package be prevented from accidental publishing by npm?
150 |   // The default is false (not hidden), but I include this here for doc
151 |   // purposes.
152 |   "private": false,
153 |   // npm has can manage a set of standard and non-standard scripts. The
154 |   // standard set of scripts can be run with: 
155 |   // 
156 |   //     npm standard-script-name
157 |   //
158 |   // The non-standard scripts can be run with:
159 |   // 
160 |   //     npm run-script script-name
161 |   //
162 |   // `docs` is a non-standard script, and can be run with:
163 |   //
164 |   //     npm run-script docs
165 |   "scripts": {
166 |     // "docs": "node node_modules/.bin/doccoh package.js"
167 |     "docs": "node_modules/.bin/docco src/api.js"
168 |   }
169 | };
170 | 
171 | 
172 | // Small script used to write the package.json file out from the package.js
173 | // file.
174 | 
175 | var fs = require("fs-extra");
176 | var packagejs = require("./package.js");
177 | var v = '0.1.0';
178 | if (!packagejs.version) {
179 |     
180 |     try {
181 |         v = require('./package.json').version;
182 |     } catch(e) {
183 |         console.log('Created new package.json. You\'re at version 0.0.0.');
184 |     } 
185 |     var s = v.split('.');
186 |     v = [s[0],s[1],parseInt(s[2]) + 1].join('.');
187 |     packagejs.version = v;
188 | }
189 | 
190 | console.log("Writing the package.json file out from package.js...");
191 | fs.writeJSONFile("package.json", packagejs, function(err){
192 |     if (err) {
193 |         console.log("Error writing package.json");
194 |         console.log(err);
195 |         console.log("");
196 |     }
197 |     else {
198 |         console.log(packagejs);
199 |         console.log("package.json written successfully.");
200 |         console.log("");
201 |     }
202 | });
203 | 


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "node-haproxy",
 3 |   "description": "",
 4 |   "homepage": "https://github.com/michieljoris/node-haproxy",
 5 |   "keywords": [],
 6 |   "repository": {
 7 |     "type": "git",
 8 |     "url": "https://github.com/michieljoris/node-haproxy.git"
 9 |   },
10 |   "author": {
11 |     "name": "Michiel van Oosten",
12 |     "email": "mail@axion5.net",
13 |     "url": "http://www.axion5.net/"
14 |   },
15 |   "licenses": [
16 |     {
17 |       "type": "MIT",
18 |       "url": "http://github.com/michieljoris/node-haproxy/blob/master/LICENSE.txt"
19 |     }
20 |   ],
21 |   "main": "src/api.js",
22 |   "bin": {
23 |     "node-haproxy": "bin/node-haproxy.js"
24 |   },
25 |   "engines": {
26 |     "node": ">= 0.6.x"
27 |   },
28 |   "dependencies": {
29 |     "dougs_vow": "*",
30 |     "fs-extra": "0.18.x",
31 |     "logthis": "*",
32 |     "crdt": "~3.5.1",
33 |     "handlebars": "~1.0.12",
34 |     "debounce": "0.0.2",
35 |     "deep-equal": "0.0.0",
36 |     "haproxy": "0.0.3",
37 |     "changeset": "0.0.5",
38 |     "optimist": "~0.6.0",
39 |     "xtend": "~2.0.6",
40 |     "extend": "~1.2.0",
41 |     "level": "~0.18.0",
42 |     "mkdirp": "~0.3.5",
43 |     "flic": "^1.1.2",
44 |     "node-ipc": "^1.1.13"
45 |   },
46 |   "devDependencies": {
47 |     "docco": "*"
48 |   },
49 |   "private": false,
50 |   "scripts": {
51 |     "docs": "node_modules/.bin/docco src/api.js"
52 |   },
53 |   "version": "0.1.26"
54 | }
55 | 


--------------------------------------------------------------------------------
/src/Data.js:
--------------------------------------------------------------------------------
  1 | var log = require('logthis').logger._create('Data');
  2 | 
  3 | var crdt = require('crdt')
  4 |   , assert = require('assert')
  5 |   , deepEqual = require('deep-equal')
  6 |   , diff = require('changeset')
  7 |   , fs = require('fs')
  8 |   , extend = require('xtend')
  9 |   ;
 10 | 
 11 | 
 12 | var Data = module.exports = function Data (opts) {
 13 |   if (!opts) opts = {};
 14 | 
 15 |   this.doc = new crdt.Doc();
 16 | 
 17 |   this.frontends          = this.doc.createSet('_type', 'frontend');
 18 |   this.backends           = this.doc.createSet('_type', 'backend');
 19 | 
 20 |   if (opts.persistence) {
 21 |     //this._bootstrapLevelDB(opts.persistence);
 22 |     this._bootstrapFileSystemPersistence(opts.persistence);
 23 |   }
 24 | 
 25 |   // Stats are kept separate from the frontends and backends because
 26 |   // change events on those trigger possible reloading of Haproxy
 27 |   // and we don't want to reload Haproxy every time we retreive stats :)
 28 |   // IDEA: reconsider separate stats storage and conditionally reload haproxy
 29 |   this.stats      = this.doc.createSet('_type', 'stat');
 30 | 
 31 |   this.log = log;
 32 | };
 33 | 
 34 | 
 35 | Data.prototype.createStream = function() {
 36 |   return this.doc.createStream();
 37 | };
 38 | 
 39 | Data.prototype.createReadableStream = function() {
 40 |   return this.doc.createStream({writable: false, sendClock: true});
 41 | };
 42 | 
 43 | Data.prototype.setFrontend = function(obj) {
 44 |   assert(typeof obj.key === 'string' && obj.key.length > 0);
 45 |   assert(typeof obj.bind === 'string');
 46 |   var id = this.frontendId(obj.key);
 47 |   if (obj.id) assert.equal(obj.id, id, 'key must correspond with id');
 48 | 
 49 |   var frontend = {
 50 |       _type     : 'frontend'
 51 |     , key       : obj.key
 52 |     , bind      : obj.bind  // TODO validate bind comma separated list of host || * : port
 53 |     , backend   : obj.backend // TODO validate, make sure the backend is defined ?
 54 |     , mode      : obj.mode || 'http'
 55 |     , keepalive : obj.keepalive || 'default' // default|close|server-close, default default
 56 |     , rules     : obj.rules || [] // TODO validate each rule
 57 |     , natives   : obj.natives || []
 58 |     , uuid      : obj.uuid || ''
 59 |   };
 60 | 
 61 |   stripUndefinedProps(frontend);
 62 |   this._updateDifferences(id, this.frontends.get(id), frontend);
 63 | };
 64 | 
 65 | Data.prototype.setBackend = function(obj) {
 66 |   assert(typeof obj.key === 'string' && obj.key.length > 0);
 67 |   obj.type = obj.type || 'static';
 68 |   assert(obj.type === 'dynamic' || obj.type === 'static');
 69 |   var id = this.backendId(obj.key);
 70 |   if (obj.id) assert.equal(obj.id, id, 'key must correspond with id');
 71 |   var existing = this.backends.get(id);
 72 | 
 73 |   var backend = {
 74 |       _type   : 'backend'
 75 |     , key     : obj.key
 76 |     , type    : obj.type 
 77 |     , name    : obj.name // TODO validate
 78 |     , version : obj.version // TODO validate
 79 |     , balance : obj.balance || 'roundrobin' // TODO validate
 80 |     , host    : obj.host || null // for host header override
 81 |     , mode    : obj.mode || 'http'
 82 |     , members : (Array.isArray(obj.members)) ? obj.members : []
 83 |     , natives : obj.natives || []
 84 |     , uuid      : obj.uuid || ''
 85 |   };
 86 | 
 87 |   stripUndefinedProps(backend);
 88 | 
 89 |   // custom health checks, only for http
 90 |   if (backend.mode === 'http' && obj.health) {
 91 |     backend.health = {
 92 |         method: obj.health.method            || 'GET'
 93 |       , uri: obj.health.uri                  || '/'
 94 |       , httpVersion: obj.health.httpVersion  || 'HTTP/1.0'
 95 |       , interval: obj.health.interval        || 2000
 96 |     };
 97 |     // validation - host header required for HTTP/1.1
 98 |     assert(!(backend.health.httpVersion === 'HTTP/1.1' && !backend.host),
 99 |       'host required with health.httpVersion == HTTP/1.1');
100 |   }
101 | 
102 |   this._updateDifferences(id, existing, backend);
103 | };
104 | 
105 | 
106 | Data.prototype.setBackendMembers = function(key, members) {
107 |   var backend = this.backends.get(this.backendId(key));
108 |   if (backend) backend.set({ 'members': members });
109 | };
110 | 
111 | Data.prototype.getFrontends = function() {
112 |   return this.frontends.toJSON();
113 | };
114 | 
115 | Data.prototype.getBackends = function() {
116 |   return this.backends.toJSON();
117 | };
118 | 
119 | Data.prototype.deleteFrontend = function(key) {
120 |   var id = this.frontendId(key);
121 |   this.doc.rm(id);
122 | };
123 | 
124 | Data.prototype.deleteBackend = function(key) {
125 |   var id = this.backendId(key);
126 |   this.doc.rm(id);
127 | };
128 | 
129 | Data.prototype.frontendId = function(key) {
130 |   return "frontend/"+key;
131 | };
132 | 
133 | Data.prototype.backendId = function(key) {
134 |   return "backend/"+key;
135 | };
136 | 
137 | Data.prototype.setFrontendStat = function(stat) {
138 |   // expect { key: 'fontEndName', status: 'UP/DOWN or like UP 2/3' }
139 |   var statId = stat.id;
140 |   var statObj = this._createStatObj(statId, stat.key, 'frontend', stat);
141 |   statObj.frontend = this.frontendId(stat.key);
142 |   this._setStat(statId, statObj);
143 | };
144 | 
145 | Data.prototype.setBackendStat = function(stat) {
146 |   // expect { key: 'key', status: 'UP/DOWN or like UP 2/3' }
147 |   var statId = stat.id;
148 |   var statObj = this._createStatObj(statId, stat.key, 'backend', stat);
149 |   statObj.backend = this.backendId(stat.key);
150 |   this._setStat(statId, statObj);
151 | };
152 | 
153 | Data.prototype.setBackendMemberStat = function(stat) {
154 |   // expect { key: 'key', status: 'UP/DOWN or like UP 2/3' }
155 |   var statId = stat.id;
156 |   var statObj = this._createStatObj(statId, stat.key, 'backendMember', stat);
157 |   statObj.backend = this.backendId(stat.backendName);
158 |   this._setStat(statId, statObj);
159 | };
160 | 
161 | Data.prototype.rmBackendMemberStatsAllBut = function(key, memberNames) {
162 |   var self = this;
163 |   this.stats.toJSON()
164 |       .forEach(function (stat) { 
165 |         if (stat.type === 'backendMember' && 
166 |             stat.key === key &&
167 |             memberNames.indexOf(stat.key) === -1) {
168 |           self.doc.rm(stat.id);
169 |         }
170 |       });
171 | };
172 | 
173 | Data.prototype._setStat = function (statId, statObj) {
174 |   var hasChanged = !deepEqual(this.doc.get(statId).toJSON(), statObj);
175 |   if (hasChanged) this.doc.set(statId, statObj);
176 | };
177 | 
178 | Data.prototype._createStatObj = function(id, key, type, stat) {
179 |   // set just the status and no other stat
180 |   return { id: id, _type: 'stat', type: type, key: key, status: stat.status };
181 |   //return extend(stat, { id: id, _type: 'stat', type: type, key: key});
182 | };
183 | 
184 | Data.prototype._updateDifferences = function (id, existingRow, updatedObj) {
185 |   if (!existingRow) return this.doc.set(id, updatedObj);
186 |   var diffObj = {};
187 |   diff(existingRow.toJSON(), updatedObj).forEach(function (change) {
188 | 
189 |     var key = change.key[0];
190 |     if (key === 'id') return;
191 |     if (!diffObj[key]) {
192 |       if (change.type === 'put') diffObj[key] = updatedObj[key];
193 |       else if (change.type === 'del') {
194 |         if (Array.isArray(updatedObj[key]))
195 |           diffObj[key] = updatedObj[key];
196 |         else diffObj[key] = undefined;
197 |       }
198 |     }
199 |   });
200 | 
201 |   existingRow.set(diffObj);
202 | };
203 | 
204 | 
205 | // Data.prototype.closeDb = function(cb) {
206 | //   if (this.db) this.db.close(cb);
207 | //   else cb(null);
208 | // };
209 | 
210 | 
211 | // This leveldb back storage is not working, sometimes it either failing
212 | // to store some data or read it out. I had to revert back to constantly
213 | // serializing the contents into a flat file
214 | //
215 | // Data.prototype._bootstrapLevelDB = function(dbLocation) {
216 | //   var self = this;
217 | //   var doc = self.doc;
218 | 
219 | //   var levelup = require("levelup");
220 | //   var level_scuttlebutt = require("level-scuttlebutt");
221 | //   var SubLevel = require('level-sublevel');
222 | //   var db = this.db = SubLevel(levelup(dbLocation));
223 | //   var udid = require('udid')('thalassa-aqueduct');
224 | //   var sbDb = db.sublevel('scuttlebutt');
225 | 
226 | //   level_scuttlebutt(sbDb, udid, function (name) {
227 | //     return doc;
228 | //   });
229 | 
230 | //   sbDb.open(udid, function (err, model) {
231 | //     self.log('debug', 'leveldb initialized, storing data at ' + dbLocation);
232 | //     //model.on('change:key', console.log);
233 | //   });
234 | // };
235 | 
236 | Data.prototype._bootstrapFileSystemPersistence = function (fileLocation) {
237 |   var self = this;
238 | 
239 |   var writing = false, queued = false;
240 |   function _syncDown() {
241 |     writing = true;
242 |     queued = false;
243 |     var contents = JSON.stringify({ version: 1, frontends: self.frontends, backends: self.backends });
244 |     fs.writeFile(fileLocation, contents, function (err) {
245 |       if (err)
246 |         self.log('error', 'failed writing serialized configuration ' + fileLocation +', ' + err.message);
247 |       writing = false;
248 |       if (queued) _syncDown();
249 |     });
250 |   }
251 | 
252 |   var syncDown = function () {
253 |     if (writing) queued = true;
254 |     else _syncDown();
255 |   };
256 | 
257 |   fs.exists(fileLocation, function (exists) {
258 |     if (exists) {
259 |       var contents = fs.readFileSync(fileLocation);
260 |       try {
261 |         var data = JSON.parse(contents);
262 |         data.frontends.forEach(function (frontend) {
263 |           self.setFrontend(frontend);
264 |         });
265 |         data.backends.forEach(function (backend) {
266 |           self.setBackend(backend);
267 |         });
268 |       }
269 |       catch (err) {
270 |         self.log('error', 'failed parsing serialized configuration JSON ' + fileLocation +', ' + err.message);
271 |       }
272 | 
273 |     }
274 |     self.frontends.on('changes', syncDown);
275 |     self.backends.on('changes', syncDown);
276 |   });
277 | 
278 | };
279 | 
280 | function stripUndefinedProps(obj) {
281 |   Object.keys(obj).forEach(function(key) {
282 |     if (obj[key] === undefined ) delete obj[key];
283 |   });
284 | }
285 | 


--------------------------------------------------------------------------------
/src/Db.js:
--------------------------------------------------------------------------------
  1 | var log = require('logthis').logger._create('Db');
  2 | 
  3 | var level = require('level')
  4 |   , assert = require('assert')
  5 |   , util = require('util')
  6 |   , path = require('path')
  7 |   , mkdirp = require('mkdirp')
  8 |   ;
  9 | 
 10 | // opts:
 11 | //    - dbPath
 12 | //    - secondsToRetainStats
 13 | //
 14 | var Db = module.exports = function(opts, cb) {
 15 |     var self = this;
 16 |     
 17 |     this.log = log;
 18 |     if (typeof opts !== 'object') opts = {};
 19 |     
 20 |     assert(opts.dbPath, 'Db.js: opts.dbPath, dbPath to leveldb database, must be passed!');
 21 | 
 22 |     var dbPath = self.DBPATH = opts.dbPath;
 23 |     self.SECONDS_TO_RETAIN_STATS = opts.secondsToRetainStats || 300;
 24 | 
 25 |     mkdirp(dbPath, function (err) {
 26 |         if (err) {
 27 |             self.log('error', 'mkdirp ' + dbPath, String(err));
 28 |             throw err;
 29 |         }
 30 | 
 31 |         var statsDbPath = path.join(dbPath, 'statsDb');
 32 |         self.statsDb = level(statsDbPath, { valueEncoding : 'json' });
 33 | 
 34 |         var activityDbPath = path.join(dbPath, 'activityDb');
 35 |         self.activityDb = level(activityDbPath, { valueEncoding : 'json' });
 36 |         if (typeof cb === 'function') cb();
 37 | 
 38 |     });
 39 | 
 40 | };
 41 | 
 42 | Db.prototype.writeStat = function writeStat(statObj) {
 43 |     // log(statObj);
 44 |     var key = [statObj.hostId, statObj.id, statObj.time].join('~');
 45 |     this.statsDb.put(key, statObj);
 46 |     this.trimStats();
 47 | };
 48 | 
 49 | var prev = {};
 50 | Db.prototype.writeActivity = function writeActivity(activityObj) {
 51 |     var key = [activityObj.time, activityObj.object].join('~');
 52 |     this.activityDb.put(key, activityObj);
 53 |     
 54 | };
 55 | 
 56 | Db.prototype.trimStats = function () {
 57 |     var self = this;
 58 | 
 59 |     // if we're already trimming, return
 60 |     if (self.isTrimming) return;
 61 | 
 62 |     self.isTrimming = true;
 63 |     // self.log('trimStats starting');
 64 | 
 65 |     var ws = self.statsDb.createWriteStream();
 66 |     var numKeysDeleted = 0;
 67 |     var numKeysConsidered = 0;
 68 |     var startTime = Date.now();
 69 |     var timeToExpire = Date.now() - (self.SECONDS_TO_RETAIN_STATS * 1000);
 70 | 
 71 |     var rs = self.statsDb.createReadStream({ keys: true, values: false })
 72 |         .on('data', function (key) {
 73 |             numKeysConsidered++;
 74 |             var parts = key.split('~');
 75 |             var epoch = parseInt(parts[2], 10) || 0;  // if the key doesn't contain the time, aggressively delete it
 76 |             if (epoch < timeToExpire) {
 77 |                 //self.log('trimStats deleting (' + (epoch - timeToExpire) + ') ' + key);
 78 |                 ws.write({ type: 'del', key: key });
 79 |                 numKeysDeleted++;
 80 |             }
 81 |         })
 82 |         .on('end', function () {
 83 |             ws.end();
 84 |             var duration = Date.now()-startTime;
 85 |             // self.log(util.format('trimStats trimmed %s of %s in %sms (%s)', numKeysDeleted, numKeysConsidered, duration, (numKeysConsidered/duration)));
 86 |             self.allowTrimmingIn(6000);
 87 |         })
 88 |         .on('error', function (err) {
 89 |             self.log('error', 'trimStats reading keystream from statsDb', String(err));
 90 |             ws.end();
 91 |         });
 92 | 
 93 |     ws.on('error', function (err) {
 94 |         self.log('error', 'trimStats write stream to statsDb', String(err));
 95 |         rs.destroy();
 96 |     });
 97 | };
 98 | 
 99 | Db.prototype.statsValueStream = function(hostId) {
100 |     var opts = (hostId) ? { start: hostId + '~', end: hostId + '~~' } : undefined;
101 |     return this.statsDb.createValueStream(opts);
102 | };
103 | 
104 | Db.prototype.activityValueStream = function(opts) {
105 |     if (!opts) opts = {};
106 |     if (!opts.start) opts.start = Date.now();
107 |     if (!opts.limit) opts.limit = 50;
108 |     opts.reverse = true;
109 |     return this.activityDb.createValueStream(opts);
110 | };
111 | 
112 | Db.prototype.allowTrimmingIn = function (t) {
113 |     var self = this;
114 |     setTimeout(function () {
115 |         self.isTrimming = false;
116 |     }, t);
117 | };
118 | 


--------------------------------------------------------------------------------
/src/HaproxyManager.js:
--------------------------------------------------------------------------------
  1 | var log = require('logthis').logger._create('HaproxyManager');
  2 | 
  3 | var handlebars = require('handlebars')
  4 |   // , HAProxy = require('haproxy')
  5 |   , fs = require('fs')
  6 |   , resolve = require('path').resolve
  7 |   , util = require('util')
  8 |   , f = util.format
  9 |   , assert = require('assert')
 10 |   , EventEmitter  = require('events').EventEmitter
 11 |   , debounce = require('debounce')
 12 |   , deepEqual = require('deep-equal')
 13 |   ;
 14 | 
 15 | 
 16 | // log('hello');
 17 | 
 18 | var HAProxyManager = module.exports = function HAProxyManager (opts) {
 19 |   if (typeof opts !== 'object') opts = {};
 20 | 
 21 |   assert(opts.data, 'opts.data required');
 22 |   assert(opts.haproxy, 'opts.haproxy required');
 23 | 
 24 |   this.config = {};
 25 |   this.config.templateFile = resolve(opts.templateFile || __dirname + '/../default.haproxycfg.tmpl');
 26 |   this.config.haproxyCfgPath = resolve(opts.haproxyCfgPath || '/etc/haproxy/haproxy.cfg');
 27 |   this.config.watchConfigFile = (opts.watchConfigFile !== undefined) ? opts.watchConfigFile : true;
 28 |   this.config.debounceRate = opts.debounceRate || 2000;
 29 |   this.log = (typeof opts.log === 'function') ? opts.log : function (){};
 30 | 
 31 |   this.latestConfig = "";
 32 | 
 33 |   if (!fs.existsSync(this.config.templateFile)) {
 34 |     this.log('error', f("template file %s doesn't exists!", this.config.templateFile));
 35 |   }
 36 |   this.template = handlebars.compile(fs.readFileSync(this.config.templateFile, 'utf-8'));
 37 |   this.writeConfigDebounced = debounce(this.writeConfig.bind(this), this.config.debounceRate, false);
 38 | 
 39 |   this.data = opts.data;
 40 |   this.haproxy = opts.haproxy;
 41 |   this.data.frontends.on( 'changes', this._changeFrontEnd.bind(this)  );
 42 |   this.data.backends.on ( 'changes', this._changeBackEnd.bind(this)   );
 43 | 
 44 |   this.writeConfigDebounced();
 45 | 
 46 | };
 47 | 
 48 | util.inherits(HAProxyManager, EventEmitter);
 49 | 
 50 | HAProxyManager.prototype.writeConfig = function() {
 51 |   var data = {
 52 |     frontends: this.data.frontends.toJSON(),
 53 |     backends: this.data.backends.toJSON(),
 54 |     haproxySocketPath: this.haproxy.socket
 55 |   };
 56 | 
 57 |   var previousConfig = this.latestConfig;
 58 |   this.latestConfig = this.template(data);
 59 | 
 60 |   // only write the config and reload if it actually changed
 61 |   if (!deepEqual(previousConfig, this.latestConfig)) {
 62 |     log('writing config\n', this.latestConfig);
 63 |     fs.writeFileSync(this.config.haproxyCfgPath, this.latestConfig , 'utf-8');
 64 |     this.emit('configChanged');
 65 |     this.reload();
 66 |   }
 67 |   else {
 68 |     this.emit('configNotChanged');
 69 |   }
 70 | };
 71 | 
 72 | HAProxyManager.prototype.reload = function () {
 73 |   var self = this;
 74 |   self.haproxy.running(function (err, running) {
 75 |     if (err) {
 76 |       self.emit('haproxy-error', String(err));
 77 |       return self.log('error', 'HaproxyManager.reload', { error: String(err) });
 78 |     }
 79 | 
 80 |     function handleRestart (err) {
 81 |       if (err) {
 82 |         self.emit('haproxy-error', String(err));
 83 |         self.log('error', 'HaproxyManager.reload', { error: String(err) });
 84 |       }
 85 |       self.emit('reloaded');
 86 |     }
 87 |     if (running) self.haproxy.reload(handleRestart);
 88 |     else self.haproxy.start(handleRestart);
 89 |   });
 90 | };
 91 | 
 92 | HAProxyManager.prototype._changeFrontEnd = function(row, changed) {
 93 |   this.log('HaproxyManager._changeFrontEnd', changed);
 94 |   this.writeConfigDebounced();
 95 | };
 96 | 
 97 | HAProxyManager.prototype._changeBackEnd = function(row, changed) {
 98 |   this.log('debug', 'HaproxyManager_changeBackEnd', changed);
 99 |   this.writeConfigDebounced();
100 | };
101 | 
102 | //
103 | //
104 | //
105 | //
106 | //
107 | //
108 | //
109 | // TODO refactor all these helper, reconsider business logic
110 | //
111 | 
112 | // template helper for outputing FrontEnd acl rules
113 | handlebars.registerHelper('aclRule', function (rule) {
114 |   var rand = Math.random().toString(36).substring(3);
115 |   var name = rule.type + '_' + rand;
116 | 
117 |   if (rule.type === 'path' || rule.type === 'url') {
118 |     return util.format("acl %s %s %s\nuse_backend %s if %s\n", name, rule.operation, rule.value, rule.backend, name);
119 |   }
120 |   else if (rule.type === 'header') {
121 |     return util.format("acl %s %s(%s) %s\nuse_backend %s if %s\n", name, rule.operation, rule.header, rule.value, rule.backend, name);
122 |   }
123 | });
124 | 
125 | handlebars.registerHelper('frontendHelper', function (frontend) {
126 |   var output = [];
127 |   var hasRules = frontend.rules && frontend.rules.length > 0;
128 |   var hasNatives = frontend.natives && frontend.natives.length > 0;
129 | 
130 |   output.push("bind " + frontend.bind);
131 |   output.push("    mode " + frontend.mode);
132 |   output.push("    default_backend " + frontend.backend);
133 | 
134 |   // http only default options
135 |   if (frontend.mode === 'http') {
136 |     output.push("    option httplog");
137 | 
138 |     // The default keep-alive behavior is to use keep-alive if clients and
139 |     // backends support it. However, if haproxy will only process rules when
140 |     // a connection is first established so if any rules are used then server-close
141 |     // should be specified at least and haproxy will let clients use keep-alive
142 |     // to haproxy but close the backend connections each time.
143 |     //
144 |     // If there are any rules, the default behavior is to use http-server-close
145 |     // and http-pretend-keepalive
146 |     if (frontend.keepalive === 'server-close') {
147 |       output.push("    option http-server-close");
148 |       output.push("    option http-pretend-keepalive");
149 |     }
150 |     else if (frontend.keepalive === 'close'){
151 |       output.push("    option forceclose");
152 |     }
153 |     // the default if there are rules is to use server close
154 |     else if (hasRules) {
155 |       output.push("    option http-server-close");
156 |       output.push("    option http-pretend-keepalive");
157 |     }
158 |   }
159 | 
160 |   if (hasRules) {
161 |     frontend.rules.forEach(function (rule) {
162 |       var rand = Math.random().toString(36).substring(13);
163 |       var name = rule.type + '_' + rand;
164 | 
165 |       if (rule.type === 'path' || rule.type === 'url') {
166 |         output.push(util.format("acl %s %s %s\nuse_backend %s if %s",
167 |           name, rule.operation, rule.value, rule.backend, name));
168 |       }
169 |       else if (rule.type === 'header') {
170 |         output.push(util.format("acl %s %s(%s) %s\nuse_backend %s if %s",
171 |           name, rule.operation, rule.header, rule.value, rule.backend, name));
172 |       }
173 |     });
174 |   }
175 | 
176 |   if (hasNatives) {
177 |     frontend.natives.forEach(function (native) {
178 |       output.push(native);
179 |     });
180 |   }
181 | 
182 |   return output.join('\n');
183 | });
184 | 
185 | 
186 | // helper to output http check and servers block
187 | handlebars.registerHelper('backendHelper', function (backend) {
188 |   var host = backend.host;
189 |   var health = backend.health;
190 |   var members = backend.members;
191 |   var output = [];
192 |   var hasNatives = backend.natives && backend.natives.length > 0;
193 | 
194 |   // output mode and balance options
195 |   output.push("mode " + backend.mode);
196 |   output.push("    balance " + backend.balance);
197 | 
198 |   // host header propagation
199 |   if (backend.host) {
200 |     output.push("    reqirep ^Host:\\ .*  Host:\\ " + backend.host);
201 |   }
202 | 
203 |   // option httpchk
204 |   if (backend.mode === 'http' && health) {
205 |     var httpVersion = (health.httpVersion === 'HTTP/1.1') ?
206 |                       ('HTTP/1.1\\r\\nHost:\\ ' + backend.host) :
207 |                       health.httpVersion;
208 |     output.push(util.format("    option httpchk %s %s %s", health.method, health.uri, httpVersion));
209 |   }
210 | 
211 |   if (hasNatives) {
212 |     backend.natives.forEach(function (native) {
213 |       output.push(native);
214 |     });
215 |   }
216 | 
217 |   if (members) {
218 |     // server lines for each member
219 |     members.forEach(function (member) {
220 |       var name = util.format("%s_%s:%s", backend.key, member.host, member.port);
221 |       var interval = (health) ? health.interval : 2000;
222 |       output.push(util.format("    server %s %s:%s check inter %s", name, member.host, member.port, interval));
223 |     });
224 |   }
225 | 
226 |   return output.join('\n');
227 | });
228 | 


--------------------------------------------------------------------------------
/src/HaproxyStats.js:
--------------------------------------------------------------------------------
  1 | var log = require('logthis').logger._create('HaproxyStats');
  2 | 
  3 | var handlebars = require('handlebars')
  4 |   // , HAProxy = require('haproxy')
  5 |   , fs = require('fs')
  6 |   , norm = require('path').normalize
  7 |   , util = require('util')
  8 |   , assert = require('assert')
  9 |   , EventEmitter  = require('events').EventEmitter
 10 |   , deepEqual = require('deep-equal')
 11 |   ;
 12 | 
 13 | var HaproxyStats = module.exports = function HaproxyStats (opts) {
 14 |   if (typeof opts !== 'object') opts = {};
 15 | 
 16 |   assert(opts.data, 'opts.data required');
 17 |   assert(opts.haproxy, 'opts.haproxy required');
 18 | 
 19 |   // TODO normalize paths
 20 | 
 21 |   this.config = {};
 22 |   this.data = opts.data;
 23 |   this.haproxy = opts.haproxy;
 24 |   this.config.haproxySocketPath = norm(opts.haproxySocketPath || '/tmp/haproxy.sock');
 25 |   this.config.statsIntervalRate = opts.statsIntervalRate || 6000;
 26 |   this.log = (typeof opts.log === 'function') ? opts.log : function (){};
 27 | 
 28 |   this.createStatsInterval(this.config.statsIntervalRate);
 29 | };
 30 | 
 31 | util.inherits(HaproxyStats, EventEmitter);
 32 | 
 33 | 
 34 | HaproxyStats.prototype.createStatsInterval = function(period) {
 35 |   var self = this;
 36 |   self.statsTimer = setTimeout(function() {
 37 |     self.haproxy.stat('-1', '-1', '-1', function (err, stats) {
 38 |       if (err) {
 39 |         self.log('error', 'HaproxyStats: ' + err.message);
 40 |       }
 41 |       else if (!stats) {
 42 |         self.log('error', 'HaproxyStats: connected but received no stats');
 43 |       }
 44 |       else {
 45 |         //console.log(stats);
 46 | 
 47 |         // frontend stats
 48 |         stats.filter(isFrontend).forEach(function (it) {
 49 |           var statsObj = {
 50 |             id: 'stat/frontend/' + it.pxname,
 51 |             key: it.pxname,
 52 |             type: 'frontend',
 53 |             time: Date.now(),
 54 |             status: it.status,
 55 |             connections: {
 56 |               current: it.scur,
 57 |               max: it.smax
 58 |             },
 59 |             weight: it.weight
 60 |             // responses: {
 61 |             //   '100': it.hrsp_1xx,
 62 |             //   '200': it.hrsp_2xx,
 63 |             //   '300': it.hrsp_3xx,
 64 |             //   '400': it.hrsp_4xx,
 65 |             //   '500': it.hrsp_5xx,
 66 |             //   total: it.req_tot
 67 |             // }
 68 |           };
 69 |           self.emit('stat', statsObj);
 70 |         });
 71 | 
 72 |         // backend stats
 73 |         stats.filter(isBackend).forEach(function (it) {
 74 |           var statsObj = {
 75 |             id: 'stat/backend/' + it.pxname,
 76 |             key: it.pxname,
 77 |             type: 'backend',
 78 |             time: Date.now(),
 79 |             status: it.status,
 80 |             connections: {
 81 |               current: it.scur,
 82 |               max: it.smax
 83 |             }
 84 |           };
 85 |           self.emit('stat', statsObj);
 86 | 
 87 |           // backend members stats
 88 |           var backendName = it.pxname;
 89 |           var backendStats = stats.filter(isBackendMember(it.pxname));
 90 |           backendStats.forEach(function (it) {
 91 |             var statsObj = {
 92 |               id: 'stat/backend/' + backendName + '/' + it.svname,
 93 |               key: it.svname,
 94 |               type: 'backendMember',
 95 |               time: Date.now(),
 96 |               backendName: it.pxname,
 97 |               status: it.status
 98 |             };
 99 |             self.emit('stat', statsObj);
100 |           });
101 | 
102 |           // TODO clean up members and frontends and backend stats that don't exist anymore?
103 |           //self.data.rmBackendMemberStatsAllBut(backendName, backendStats.map(function (it) { return it.svname; }));
104 | 
105 |         });
106 |       }
107 |       self.createStatsInterval(period);
108 |     });
109 |   }, period);
110 | };
111 | 
112 | function isFrontend (it) { return it.svname === 'FRONTEND' && it.pxname != 'stats';}
113 | function isBackend(it) { return it.svname === 'BACKEND' && it.pxname != 'stats';}
114 | function isBackendMember (backendName) { return function (it) { 
115 |     return it.pxname === backendName && it.svname.indexOf(backendName) === 0; 
116 |   };
117 | }
118 | 


--------------------------------------------------------------------------------
/src/api.js:
--------------------------------------------------------------------------------
  1 | var logLevel = 'info';
  2 | 
  3 | require('logthis').config({ _on: true,
  4 |                             'Data': logLevel ,
  5 |                             'HaproxyManager': logLevel ,
  6 |                             'HaproxyStats': logLevel,
  7 |                             'Db': logLevel,
  8 |                             'api': logLevel
  9 |                           });
 10 | 
 11 | var log = require('logthis').logger._create('api');
 12 | 
 13 | var assert = require('assert')
 14 | , resolve = require('path').resolve
 15 | , Haproxy = require('haproxy')
 16 | , Data = require('../src/Data')
 17 | , Db = require('../src/Db')
 18 | , HaproxyManager = require('../src/HaproxyManager')
 19 | , HaproxyStats = require('../src/HaproxyStats')
 20 | , extend = require('extend')
 21 | , Path = require('path')
 22 | , fs = require('fs-extra')
 23 | , util = require('util');
 24 | 
 25 | var tempDir = Path.join(__dirname, '../temp');
 26 | 
 27 | fs.ensureDirSync(tempDir);
 28 |                  
 29 | var firstStart = true;
 30 | 
 31 | var defaults = {
 32 |   //Alternative configuration:
 33 | 
 34 |   //     haproxySocketPath: '/tmp/haproxy.status.sock',
 35 |   //     haproxyPidPath: '/var/run/haproxy.pid',
 36 |   //     haproxyCfgPath: '/etc/haproxy/haproxy.cfg',
 37 |   //     sudo: 'use sudo when starting haproxy',
 38 | 
 39 |   haproxySocketPath: Path.join(tempDir, 'haproxy.status.sock'),
 40 |   haproxyPidPath: Path.join(tempDir, 'haproxy.pid'),
 41 |   haproxyCfgPath: Path.join(tempDir, 'haproxy.cfg'),
 42 | 
 43 |   templateFile: Path.join(__dirname, '../haproxycfg.tmpl'),
 44 |   persistence: Path.join(tempDir, 'persisted'),
 45 |   dbPath:  Path.join(tempDir, 'db'),
 46 |   which: Path.join(__dirname, '../haproxy'), //if undefined tries to find haproxy on system 
 47 |   ipc: false //whether to enable the ipc server
 48 | };
 49 | 
 50 | var PACKAGEJSON = Path.resolve(__dirname,  '../package.json');
 51 | 
 52 | function getUuid() {
 53 |   return 'xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx'.replace(/[x]/g, function(c) {
 54 |     return (Math.random()*16|0).toString(16);
 55 |   });
 56 | }
 57 | 
 58 | var response;
 59 | var infoFunctions = ['getHaproxyConfig', 'getFrontend', 'getBackend', 'getBackendMembers', 'getFrontends', 'getBackends'];
 60 | 
 61 | function version() {
 62 |   var packageJson = fs.readJsonSync(PACKAGEJSON);
 63 |   return packageJson.version;
 64 | }
 65 | 
 66 | function ipc(api) {
 67 | 
 68 |   var ipc=require('node-ipc');
 69 | 
 70 |   ipc.config.id   = 'haproxy';
 71 |   ipc.config.retry= 1500;
 72 |   ipc.config.silent = true;
 73 |   var timeoutId;
 74 |   ipc.serve(
 75 |     function(){
 76 |       ipc.server.on(
 77 |         'api',
 78 |         function(data,socket){
 79 |           console.log(data.call);
 80 |           var error, result;
 81 |           if (response) {
 82 |             ipc.server.emit(
 83 |               socket,
 84 |               data.uuid,
 85 |               {
 86 |                 id   : ipc.config.id,
 87 |                 error: "Call in progress.."
 88 |               }
 89 |             );
 90 |           }
 91 |           else {
 92 |             response = function(error) {
 93 |               ipc.server.emit(
 94 |                 socket,
 95 |                 data.uuid,
 96 |                 {
 97 |                   id   : ipc.config.id,
 98 |                   data : result,
 99 |                   error: error
100 |                 }
101 |               );
102 |               response = null;
103 |               clearTimeout(timeoutId);
104 |             };
105 |             if (!api[data.call]) {
106 |               error = "No such function: " + data.call;
107 |               response(error);
108 |             }
109 |             else {
110 |               data.args = data.args || [];
111 |               data.args = Array.isArray(data.args) ? data.args : [data.args];
112 |               result = api[data.call].apply(null, data.args || []);
113 |             }
114 |             if (infoFunctions.indexOf(data.call) !== -1) {
115 |               response();
116 |             }
117 |             else {
118 |               timeoutId = setTimeout(function() {
119 |                 if (response) response('timout');
120 |               }, 10000);
121 | 
122 |             }
123 |           }
124 |         }
125 |       );
126 |     }
127 |   );
128 | 
129 |   ipc.server.start();
130 |   console.log('ipc server started');
131 | }
132 | 
133 | 
134 | module.exports =  function(opts) {
135 |   console.log('Version:', version());
136 |   var data, haproxyManager;
137 |   opts = extend(defaults, opts);
138 |   if (opts.which === 'system') delete opts.which;
139 |     
140 |   data = new Data( {
141 |     persistence: opts.persistence, //file location
142 |     log: log
143 |   });
144 | 
145 |   var haproxy = new Haproxy(opts.haproxySocketPath, {
146 |     config:  resolve(opts.haproxyCfgPath),
147 |     pidFile: resolve(opts.haproxyPidPath),
148 |     prefix: (opts.sudo) ? 'sudo' : undefined,
149 |     which: opts.which
150 |   });
151 | 
152 |   haproxyManager = new HaproxyManager({
153 |     haproxy: haproxy,
154 |     data: data,
155 |     haproxyCfgPath: opts.haproxyCfgPath,
156 | 
157 |     templateFile: opts.templateFile,
158 |     sudo: opts.sudo,
159 |     log: log
160 |   });
161 | 
162 |   var haproxyStats = new HaproxyStats({
163 |     haproxy: haproxy,
164 |     data: data,
165 |     log: log
166 |   });
167 |     
168 |   // Stream stats into a leveldb
169 |   var db = new Db(opts, function () {
170 |     db.writeActivity({ type: 'activity',  time: Date.now(), verb: 'started'});
171 |   });
172 | 
173 |   // Wire up stats to write to stats db
174 |   haproxyStats.on('stat', function (statObj) {
175 |     db.writeStat(statObj);
176 |     
177 |     if (statObj.type === 'frontend') {
178 |       data.setFrontendStat(statObj);
179 |     }
180 |     else if (statObj.type === 'backend') {
181 |       data.setBackendStat(statObj);
182 |     }
183 |     else if (statObj.type === 'backendMember') {
184 |       data.setBackendMemberStat(statObj);
185 |     }
186 |   });
187 | 
188 |   haproxyManager.on('haproxy-error', function (error) {
189 |     log._e('Haproxy error:\n', error);
190 |     if (response) response(error);
191 |   });
192 | 
193 |   haproxyManager.on('configNotChanged', function (statObj) {
194 |     log._i('Config not changed');
195 |     if (response) response();
196 |   });
197 | 
198 |   // Wire up haproxy changes to write to activity db
199 |   haproxyManager.on('configChanged', function (statObj) {
200 |     var activityObj = { type: 'activity',  time: Date.now(), verb: 'haproxyConfigChanged'};
201 |     log('configChanged\n', activityObj);
202 |     db.writeActivity(activityObj);
203 |   });
204 | 
205 |   haproxyManager.on('reloaded', function (statObj) {
206 |     var activityObj = { type: 'activity',  time: Date.now(), verb: 'haproxyRestarted' };
207 |     log('reloaded\n', activityObj);
208 |     if (firstStart) {
209 |       console.log('Running..');
210 |       firstStart = false;
211 |       if (opts.ipc) ipc(api);
212 |     }
213 |     db.writeActivity(activityObj); 
214 |     if (response) response();
215 |   });
216 |     
217 |   var api = {};
218 |   api.getFrontend = function (key) {
219 |     var id = data.frontendId(key);
220 |     var row = data.frontends.get(id);
221 |     return row ? row.toJSON() : null;
222 |   };
223 | 
224 |   api.getBackend = function (key) {
225 |     var id = data.backendId(key);
226 |     var row = data.backends.get(id);
227 |     return row ? row.toJSON() : null;
228 |   };
229 | 
230 |   api.getBackendMembers = function (key) {
231 |     var id = data.backendId(key);
232 |     var row = data.backends.get(id);
233 |     return row ?  row.toJSON().members : null;
234 |   };
235 | 
236 |   api.getFrontends = function () {
237 |     return data.frontends.toJSON();
238 |   };
239 | 
240 |   api.getBackends = function () {
241 |     return data.backends.toJSON();
242 |   };
243 | 
244 |   api.putFrontend = function (key, obj) {
245 |     // var id = data.frontendId(key);
246 |     obj.key = key;
247 |     obj.uuid = getUuid(); //to mark it as changed..
248 |     data.setFrontend(obj);
249 |   };
250 | 
251 |   api.putFrontends = function(array) {
252 |     array.forEach(function(e) {
253 |       api.putFrontend(e.key, e.obj);
254 |     });
255 |   },
256 | 
257 |   api.putBackend = function (key, obj) {
258 |     // var id = data.backendId(key);
259 |     obj.key = key;
260 |     obj.uuid = getUuid(); //to mark it as changed..
261 |     if (obj.health && obj.health.httpVersion === 'http/1.1' && !obj.host) {
262 |       throw Error('host is required with health check with httpversion=http/1.1');
263 |     }
264 |     data.setBackend(obj);
265 |   };
266 | 
267 |   api.putBackends = function(array) {
268 |     array.forEach(function(e) {
269 |       api.putBackend(e.key, e.obj);
270 |     });
271 |   },
272 | 
273 |   api.deleteFrontend = function (key) {
274 |     var id = data.frontendId(key);
275 |     var row = data.frontends.get(id);
276 |     if (row) data.frontends.rm(id);
277 |     else if (response) response('Frontend not found: ' + key);
278 |   };
279 | 
280 |   var deleteFrontends = function (keys) {
281 |     var touched;
282 |     keys.forEach(function(key) {
283 |       var id = data.frontendId(key);
284 |       var row = data.frontends.get(id);
285 |       if (row) {
286 |         data.frontends.rm(id);
287 |         touched = true;
288 |       }
289 |     });
290 |     return touched;
291 |   };
292 | 
293 |   api.deleteFrontends = function(keys) {
294 |     var touched = deleteFrontends(keys);
295 |     if (!touched && response) response();
296 |   },
297 | 
298 |   api.deleteBackend = function (key) {
299 |     var id = data.backendId(key);
300 |     var row = data.backends.get(id);
301 |     if (row) data.backends.rm(id);
302 |     else if (response) response();
303 |   };
304 | 
305 |   var deleteBackends = function (keys) {
306 |     var touched;
307 |     keys.forEach(function(key) {
308 |       var id = data.backendId(key);
309 |       var row = data.backends.get(id);
310 |       if (row) {
311 |         data.backends.rm(id);
312 |         touched = true;
313 |       }
314 |     });
315 |     return touched;
316 |   };
317 | 
318 |   api.deleteBackends = function(keys) {
319 |     var touched = deleteBackends(keys);
320 |     if (!touched && response) response();
321 |   };
322 | 
323 |   // function inspect(arg) {
324 |   //   return util.inspect(arg, { depth: 10, colors: true });
325 |   // }
326 |   
327 |   // api.updateFrontend = function (key, obj) {
328 |   //   var id = data.frontendId(key);
329 |   //   var row = data.frontends.get(id);
330 |   //   var oldFrontend = {};
331 |   //   obj = obj || {};
332 |   //   if (row) {
333 |   //     row = row.toJSON();
334 |   //     oldFrontend = extend(true, {}, row); //deep copy row
335 |   //   }
336 |   //   console.log(inspect(oldFrontend));
337 |   //   console.log(inspect(obj));
338 |   //   var frontend = oldFrontend ? extend(true, oldFrontend, obj) : obj; 
339 |   //   frontend.rules = obj.rules;
340 |   //   frontend.uuid = getUuid(); //to mark it as changed..
341 |   //   frontend.key = key;
342 |   //   console.log(inspect(frontend));
343 |   //   console.log(response);
344 |   //   data.setFrontend(frontend);
345 |   // };
346 | 
347 |   // api.updateBackend = function (key, obj) {
348 |   //   var id = data.backendId(key);
349 |   //   var row = data.backends.get(id);
350 |   //   var oldBackend = {};;
351 |   //   obj = obj || {};
352 |   //   if (row) {
353 |   //     row = row.toJSON();
354 |   //     oldBackend = extend(true, {}, row); //deep copy row
355 |   //     }
356 |   //   var backend = oldBackend ? extend(true, oldBackend, obj) : obj; 
357 |   //   backend.uuid = getUuid(); //to mark it as changed..
358 |   //   backend.key = key;
359 |   //   data.setBackend(backend);
360 |   // };
361 | 
362 |   api.bulkSet = function(ops) {
363 |     ops = ops || {};
364 |     var touched;
365 |     if (ops.delete) {
366 |       if (ops.delete.backends) touched = touched || deleteBackends(ops.delete.backends);
367 |       if (ops.delete.frontends) touched = touched || deleteFrontends(ops.delete.frontends);
368 |     }
369 |     if (ops.put) {
370 |       if (ops.put.backends && ops.put.backends.length) {
371 |         touched = true;
372 |         api.putBackends(ops.put.backends);
373 |       }
374 |       if (ops.put.frontend) {
375 |         touched = true;
376 |         api.putFrontend(ops.put.frontend.key, ops.put.frontend.obj);
377 |       }
378 |     }
379 |     if (!touched && response) response();
380 |   },
381 | 
382 |   api.getHaproxyConfig = function () {
383 |     return haproxyManager.latestConfig;
384 |   };
385 | 
386 |   return api;
387 | };
388 | 
389 | // module.exports({ ipc: true});
390 | // console.log(process._arguments);
391 | // console.log(extend(true, {a:1}, {a:2, b:2}));
392 | // module.exports({ ipc: true });
393 | 
394 | // var p = fs.readJsonSync('/home/michieljoris/src/node-haproxy/temp/persisted');
395 | // console.log(p);
396 | 


--------------------------------------------------------------------------------
/src/ipc-client.js:
--------------------------------------------------------------------------------
 1 | var ipc = require('node-ipc');
 2 | var util = require('util');
 3 | var VOW = require('dougs_vow');
 4 | 
 5 | ipc.config.id = 'haproxy-client';
 6 | ipc.config.retry = 1000;
 7 | ipc.config.silent = true;
 8 | 
 9 | function getUuid() {
10 |   return 'xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx'.replace(/[x]/g, function(c) {
11 |     return (Math.random()*16|0).toString(16);
12 |   });
13 | }
14 | 
15 | var haproxy = function(call, args) {
16 |   var vow = VOW.make();
17 |   var uuid = getUuid();
18 |   ipc.connectTo(
19 |     'haproxy',
20 |     function(){
21 |       ipc.of.haproxy.emit(
22 |         'api',
23 |         {
24 |           id : ipc.config.id,
25 |           call : call,
26 |           args: args,
27 |           uuid: uuid 
28 |         }
29 |       );
30 |       ipc.of.haproxy.on(
31 |         'disconnect',
32 |         function(){
33 |           console.log('disconnected from haproxy'.notice);
34 |           vow.break('node-haproxy is not running..');
35 |         }
36 |       );
37 |       ipc.of.haproxy.on(
38 |         uuid,
39 |         function(result){
40 |           if (result.error) vow.break(result.error); //callback(result.error, null);
41 |           else vow.keep(result.data); //callback(null, result.data);
42 |           // console.log('got a message from haproxy : ', util.inspect(result, {depth: 10, colors: true }));
43 |         }
44 |       );
45 |     }
46 |   );
47 |   return vow.promise;
48 | };
49 | 
50 | haproxy.close = function() {
51 |   ipc.config.maxRetries = 0;
52 |   ipc.disconnect('haproxy');
53 |   
54 | };
55 | 
56 | module.exports = haproxy;
57 | 
58 | // test
59 | // module.exports('getFrontends', [], function(error, result) {
60 | //   console.log(error, result);
61 | // });
62 | 


--------------------------------------------------------------------------------
/test/ipc-server.js:
--------------------------------------------------------------------------------
 1 | var ipc=require('node-ipc');
 2 | 
 3 | ipc.config.id   = 'haproxy';
 4 | ipc.config.retry= 1500;
 5 | ipc.config.silent = true;
 6 | 
 7 | ipc.serve(
 8 |   function(){
 9 |     ipc.server.on(
10 |       'api',
11 |       function(data,socket){
12 |         console.log(data);
13 |         //ipc.log('got a message from'.debug, (data.id).variable, (data.message).data);
14 |         ipc.server.emit(
15 |           socket,
16 |           'result',
17 |           {
18 |             id      : ipc.config.id,
19 |             data : data
20 |           }
21 |         );
22 |       }
23 |     );
24 |   }
25 | );
26 | 
27 | // ipc.server.define.listen['app.message']='This event type listens for message strings as value of data key.';
28 | 
29 | ipc.server.start();
30 | 


--------------------------------------------------------------------------------
/test/test-flic.js:
--------------------------------------------------------------------------------
 1 | var flic = require('flic');
 2 | var Node = flic.node;
 3 | 
 4 | 
 5 | var node = new Node(function(err){
 6 |   if (err)  {
 7 |     console.log(err);
 8 |     return;
 9 |   }
10 |   console.log('client is online!');
11 | 
12 |   node.tell('haproxy:call', 'getBackends', null, null, function(err, result){
13 |     if (err)  return console.log(err);
14 |     console.log(result); 
15 | 
16 |   });
17 |     node.tell('haproxy:call', 'getFrontends', null, null, function(err, result){
18 |       if (err)  return console.log(err);
19 |       console.log(result); 
20 |     });
21 | });
22 |  
23 | 
24 | //From api.js
25 | // function flic(api, port) {
26 | //   var flic = require('flic');
27 | //   var Bridge = flic.bridge;
28 | //   var Node = flic.node;
29 |  
30 | //   // Default port is 8221 
31 | //   port = typeof port === 'number' ? port : 8221;
32 | 
33 | //   // Bridge can be in any process, and nodes can be in any process 
34 | //   var bridge = new Bridge();
35 | //   // var bridge = new Bridge(port); //not working???
36 |  
37 | //   var node = new Node('haproxy', function(err){
38 | //     if (err)  log._e(err);
39 | //     else log._i('node-haproxy is online!');
40 | //   });
41 | 
42 | //   node.on('call', function(fn, param1, param2,  callback){
43 | //     console.log(fn, param1, param2); 
44 | //     if (api[fn]) callback(null, api[fn](param1, param2));
45 | //     else callback('No such function: ' + fn, null);
46 | //   });
47 |  
48 | // }
49 | 
50 | 
51 | 
52 | 


--------------------------------------------------------------------------------