synapse-aurora 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. data/.gitignore +23 -0
  2. data/.mailmap +3 -0
  3. data/.nix/Gemfile.nix +141 -0
  4. data/.nix/rubylibs.nix +42 -0
  5. data/.rspec +2 -0
  6. data/.travis.yml +5 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/Makefile +6 -0
  10. data/README.md +339 -0
  11. data/Rakefile +8 -0
  12. data/bin/synapse +62 -0
  13. data/config/hostheader_test.json +71 -0
  14. data/config/svcdir_test.json +46 -0
  15. data/config/synapse.conf.json +90 -0
  16. data/config/synapse_services/service1.json +24 -0
  17. data/config/synapse_services/service2.json +24 -0
  18. data/default.nix +66 -0
  19. data/lib/synapse.rb +85 -0
  20. data/lib/synapse/base.rb +5 -0
  21. data/lib/synapse/haproxy.rb +797 -0
  22. data/lib/synapse/log.rb +24 -0
  23. data/lib/synapse/service_watcher.rb +36 -0
  24. data/lib/synapse/service_watcher/base.rb +109 -0
  25. data/lib/synapse/service_watcher/dns.rb +109 -0
  26. data/lib/synapse/service_watcher/docker.rb +120 -0
  27. data/lib/synapse/service_watcher/ec2tag.rb +133 -0
  28. data/lib/synapse/service_watcher/zookeeper.rb +153 -0
  29. data/lib/synapse/service_watcher/zookeeper_aurora.rb +76 -0
  30. data/lib/synapse/service_watcher/zookeeper_dns.rb +232 -0
  31. data/lib/synapse/version.rb +3 -0
  32. data/spec/lib/synapse/haproxy_spec.rb +32 -0
  33. data/spec/lib/synapse/service_watcher_base_spec.rb +55 -0
  34. data/spec/lib/synapse/service_watcher_docker_spec.rb +152 -0
  35. data/spec/lib/synapse/service_watcher_ec2tags_spec.rb +220 -0
  36. data/spec/spec_helper.rb +22 -0
  37. data/spec/support/configuration.rb +9 -0
  38. data/spec/support/minimum.conf.yaml +27 -0
  39. data/synapse.gemspec +33 -0
  40. metadata +227 -0
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :test => :spec
7
+ task :default => :spec
8
+
data/bin/synapse ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+ require 'optparse'
5
+
6
+ require 'synapse'
7
+
8
+ options={}
9
+
10
+ # set command line options
11
+ optparse = OptionParser.new do |opts|
12
+ opts.banner =<<-EOB
13
+ Welcome to synapse
14
+
15
+ Usage: synapse --config /path/to/synapse/config
16
+ EOB
17
+
18
+ options[:config] = ENV['SYNAPSE_CONFIG']
19
+ opts.on('-c config','--config config', String, 'path to synapse config') do |key,value|
20
+ options[:config] = key
21
+ end
22
+
23
+ opts.on( '-h', '--help', 'Display this screen' ) do
24
+ puts opts
25
+ exit
26
+ end
27
+ end
28
+
29
+ # parse command line arguments
30
+ optparse.parse!
31
+
32
+ def parseconfig(filename)
33
+ # parse synapse config file
34
+ begin
35
+ c = YAML::parse(File.read(filename))
36
+ rescue Errno::ENOENT => e
37
+ raise ArgumentError, "config file does not exist:\n#{e.inspect}"
38
+ rescue Errno::EACCES => e
39
+ raise ArgumentError, "could not open config file:\n#{e.inspect}"
40
+ rescue YAML::SyntaxError => e
41
+ raise "config file #{filename} is not yaml:\n#{e.inspect}"
42
+ end
43
+ return c.to_ruby
44
+ end
45
+
46
+ config = parseconfig(options[:config])
47
+ config['services'] ||= {}
48
+
49
+ if config.has_key?('service_conf_dir')
50
+ cdir = File.expand_path(config['service_conf_dir'])
51
+ if ! Dir.exists?(cdir)
52
+ raise "service conf dir does not exist:#{cdir}"
53
+ end
54
+ cfiles = Dir.glob(File.join(cdir, '*.{json,yaml}'))
55
+ cfiles.each { |x| config['services'][File.basename(x[/(.*)\.(json|yaml)$/, 1])] = parseconfig(x) }
56
+ end
57
+
58
+ # run synapse
59
+ s = Synapse::Synapse.new(config)
60
+ s.run
61
+
62
+ puts "synapse has exited"
@@ -0,0 +1,71 @@
1
+ {
2
+ "services": {
3
+ "service1": {
4
+ "default_servers": [
5
+ { "name": "default1", "host": "localhost", "port": 8080 }
6
+ ],
7
+ "discovery": {
8
+ "method": "dns",
9
+ "nameserver": "127.0.0.1",
10
+ "servers": [
11
+ "0.www.example.com",
12
+ "1.www.example.com"
13
+ ]
14
+ },
15
+ "haproxy": {
16
+ "server_options": "check inter 2s rise 3 fall 2",
17
+ "listen": [
18
+ "mode http",
19
+ "option httplog"
20
+ ],
21
+ "backend": [
22
+ "mode http",
23
+ "option httpchk GET /health HTTP/1.0"
24
+ ]
25
+ }
26
+ }
27
+ },
28
+ "haproxy": {
29
+ "reload_command": "sudo service haproxy reload",
30
+ "config_file_path": "/etc/haproxy/haproxy.cfg",
31
+ "socket_file_path": "/var/haproxy/stats.sock",
32
+ "do_writes": true,
33
+ "do_reloads": true,
34
+ "do_socket": true,
35
+ "global": [
36
+ "daemon",
37
+ "user haproxy",
38
+ "group haproxy",
39
+ "maxconn 4096",
40
+ "log 127.0.0.1 local0",
41
+ "log 127.0.0.1 local1 notice",
42
+ "stats socket /var/haproxy/stats.sock mode 666 level admin"
43
+ ],
44
+ "defaults": [
45
+ "log global",
46
+ "option dontlognull",
47
+ "maxconn 2000",
48
+ "retries 3",
49
+ "timeout connect 5s",
50
+ "timeout client 1m",
51
+ "timeout server 1m",
52
+ "option redispatch",
53
+ "balance roundrobin"
54
+ ],
55
+ "extra_sections": {
56
+ "listen stats :3212": [
57
+ "mode http",
58
+ "stats enable",
59
+ "stats uri /",
60
+ "stats refresh 5s"
61
+ ],
62
+ "frontend http-generic-in": [
63
+ "bind 127.0.0.1:80",
64
+ "acl is_service1 hdr_dom(host) -i service1.lb",
65
+ "acl is_cache hdr_dom(host) -i cache.lb",
66
+ "use_backend service1 if is_service1",
67
+ "use_backend cache if is_cache"
68
+ ]
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "service_conf_dir": "config/synapse_services",
3
+ "haproxy": {
4
+ "reload_command": "sudo service haproxy reload",
5
+ "config_file_path": "/etc/haproxy/haproxy.cfg",
6
+ "socket_file_path": "/var/haproxy/stats.sock",
7
+ "do_writes": true,
8
+ "do_reloads": true,
9
+ "do_socket": true,
10
+ "global": [
11
+ "daemon",
12
+ "user haproxy",
13
+ "group haproxy",
14
+ "maxconn 4096",
15
+ "log 127.0.0.1 local0",
16
+ "log 127.0.0.1 local1 notice",
17
+ "stats socket /var/haproxy/stats.sock mode 666 level admin"
18
+ ],
19
+ "defaults": [
20
+ "log global",
21
+ "option dontlognull",
22
+ "maxconn 2000",
23
+ "retries 3",
24
+ "timeout connect 5s",
25
+ "timeout client 1m",
26
+ "timeout server 1m",
27
+ "option redispatch",
28
+ "balance roundrobin"
29
+ ],
30
+ "extra_sections": {
31
+ "listen stats :3212": [
32
+ "mode http",
33
+ "stats enable",
34
+ "stats uri /",
35
+ "stats refresh 5s"
36
+ ],
37
+ "frontend http-generic-in": [
38
+ "bind 127.0.0.1:80",
39
+ "acl is_service1 hdr_dom(host) -i service1.lb",
40
+ "acl is_cache hdr_dom(host) -i cache.lb",
41
+ "use_backend service1 if is_service1",
42
+ "use_backend cache if is_cache"
43
+ ]
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,90 @@
1
+ {
2
+ "services": {
3
+ "service1": {
4
+ "default_servers": [
5
+ {
6
+ "name": "default1",
7
+ "host": "localhost",
8
+ "port": 8423
9
+ }
10
+ ],
11
+ "discovery": {
12
+ "method": "zookeeper",
13
+ "path": "/services/service1",
14
+ "hosts": [
15
+ "localhost:2181"
16
+ ]
17
+ },
18
+ "haproxy": {
19
+ "port": 3213,
20
+ "server_options": "check inter 2s rise 3 fall 2",
21
+ "listen": [
22
+ "mode http",
23
+ "option httpchk /health",
24
+ "http-check expect string OK"
25
+ ]
26
+ }
27
+
28
+ },
29
+ "service2": {
30
+ "default_servers": [
31
+ {
32
+ "name": "default1",
33
+ "host": "localhost",
34
+ "port": 8422
35
+ }
36
+ ],
37
+ "discovery": {
38
+ "method": "zookeeper",
39
+ "path": "/services/service2",
40
+ "hosts": [
41
+ "localhost:2181"
42
+ ]
43
+ },
44
+ "haproxy": {
45
+ "port": 3214,
46
+ "server_options": "check inter 2s rise 3 fall 2",
47
+ "listen": [
48
+ "mode http",
49
+ "option httpchk /health"
50
+ ]
51
+ }
52
+ }
53
+ },
54
+ "haproxy": {
55
+ "reload_command": "sudo service haproxy reload",
56
+ "config_file_path": "/etc/haproxy/haproxy.cfg",
57
+ "socket_file_path": "/var/haproxy/stats.sock",
58
+ "do_writes": false,
59
+ "do_reloads": false,
60
+ "do_socket": false,
61
+ "global": [
62
+ "daemon",
63
+ "user haproxy",
64
+ "group haproxy",
65
+ "maxconn 4096",
66
+ "log 127.0.0.1 local0",
67
+ "log 127.0.0.1 local1 notice",
68
+ "stats socket /var/haproxy/stats.sock mode 666 level admin"
69
+ ],
70
+ "defaults": [
71
+ "log global",
72
+ "option dontlognull",
73
+ "maxconn 2000",
74
+ "retries 3",
75
+ "timeout connect 5s",
76
+ "timeout client 1m",
77
+ "timeout server 1m",
78
+ "option redispatch",
79
+ "balance roundrobin"
80
+ ],
81
+ "extra_sections": {
82
+ "listen stats :3212": [
83
+ "mode http",
84
+ "stats enable",
85
+ "stats uri /",
86
+ "stats refresh 5s"
87
+ ]
88
+ }
89
+ }
90
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "default_servers": [
3
+ { "name": "default1", "host": "localhost", "port": 8080 }
4
+ ],
5
+ "discovery": {
6
+ "method": "dns",
7
+ "nameserver": "10.10.1.13",
8
+ "servers": [
9
+ "0.www.example.com",
10
+ "1.www.example.com"
11
+ ]
12
+ },
13
+ "haproxy": {
14
+ "server_options": "check inter 2s rise 3 fall 2",
15
+ "listen": [
16
+ "mode http",
17
+ "option httplog"
18
+ ],
19
+ "backend": [
20
+ "mode http",
21
+ "option httpchk GET /health HTTP/1.0"
22
+ ]
23
+ }
24
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "default_servers": [
3
+ { "name": "default1", "host": "localhost", "port": 8080 }
4
+ ],
5
+ "discovery": {
6
+ "method": "dns",
7
+ "nameserver": "10.10.1.13",
8
+ "servers": [
9
+ "0.www.example.com",
10
+ "1.www.example.com"
11
+ ]
12
+ },
13
+ "haproxy": {
14
+ "server_options": "check inter 2s rise 3 fall 2",
15
+ "listen": [
16
+ "mode http",
17
+ "option httplog"
18
+ ],
19
+ "backend": [
20
+ "mode http",
21
+ "option httpchk GET /health HTTP/1.0"
22
+ ]
23
+ }
24
+ }
data/default.nix ADDED
@@ -0,0 +1,66 @@
1
+ # Build a snapshot of synapse from the current git working copy.
2
+
3
+ with import <nixpkgs> { };
4
+
5
+ let
6
+ myGems = recurseIntoAttrs (callPackage ./.nix/rubylibs.nix { });
7
+ synapse_requirements = [
8
+ rubyLibs.aws_sdk
9
+ myGems.zk
10
+ myGems.docker_api
11
+ ];
12
+ in
13
+ stdenv.mkDerivation rec {
14
+ name = "synapse-git";
15
+ version = "0.11.1";
16
+
17
+ src = ./.;
18
+
19
+ meta = {
20
+ description = "Development build of synapse from git";
21
+ homepage = "https://github.com/benley/synapse";
22
+ };
23
+
24
+ buildInputs = [ ruby rubygems git makeWrapper ];
25
+ propagatedBuildInputs = synapse_requirements;
26
+
27
+ buildPhase = "gem build synapse.gemspec";
28
+
29
+ installPhase = ''
30
+ export HOME=$TMP/home; mkdir -pv "$HOME"
31
+
32
+ gem install \
33
+ --ignore-dependencies \
34
+ --install-dir "$out/${ruby.gemPath}" \
35
+ --bindir "$out/bin" \
36
+ "synapse-${version}.gem" $gemFlags -- $buildFlags
37
+
38
+ # Don't keep the .gem file in the finished package
39
+ rm -frv "$out/${ruby.gemPath}/cache"
40
+
41
+ addToSearchPath GEM_PATH "$out/${ruby.gemPath}"
42
+
43
+ for prog in $out/bin/*; do
44
+ wrapProgram "$prog" \
45
+ --prefix GEM_PATH : "$GEM_PATH" \
46
+ --prefix RUBYLIB : "${rubygems}/lib" \
47
+ --set RUBYOPT rubygems \
48
+ $extraWrapperFlags ''${extraWrapperFlagsArray[@]}
49
+ done
50
+
51
+ # Make sure all the executables are actually in the package
52
+ for prog in $out/gems/*/bin/*; do
53
+ [[ -e "$out/bin/$(basename $prog)" ]]
54
+ done
55
+
56
+ # looks like useless files which break build repeatability and consume space
57
+ rm $out/${ruby.gemPath}/doc/*/*/created.rid \
58
+ $out/${ruby.gemPath}/gems/*/ext/*/mkmf.log || true
59
+
60
+ runHook postInstall
61
+ '';
62
+
63
+ propagatedUserEnvPkgs = synapse_requirements;
64
+
65
+ passthru.isRubyGem = true;
66
+ }
data/lib/synapse.rb ADDED
@@ -0,0 +1,85 @@
1
+ require "synapse/version"
2
+ require "synapse/service_watcher/base"
3
+ require "synapse/haproxy"
4
+ require "synapse/service_watcher"
5
+ require "synapse/log"
6
+
7
+ require 'logger'
8
+ require 'json'
9
+
10
+ include Synapse
11
+
12
+ module Synapse
13
+ class Synapse
14
+ include Logging
15
+ def initialize(opts={})
16
+ # create the service watchers for all our services
17
+ raise "specify a list of services to connect in the config" unless opts.has_key?('services')
18
+ @service_watchers = create_service_watchers(opts['services'])
19
+
20
+ # create the haproxy object
21
+ raise "haproxy config section is missing" unless opts.has_key?('haproxy')
22
+ @haproxy = Haproxy.new(opts['haproxy'])
23
+
24
+ # configuration is initially enabled to configure on first loop
25
+ @config_updated = true
26
+
27
+ # Any exceptions in the watcher threads should wake the main thread so
28
+ # that we can fail fast.
29
+ Thread.abort_on_exception = true
30
+
31
+ log.debug "synapse: completed init"
32
+ end
33
+
34
+ # start all the watchers and enable haproxy configuration
35
+ def run
36
+ log.info "synapse: starting..."
37
+
38
+ # start all the watchers
39
+ @service_watchers.map { |watcher| watcher.start }
40
+
41
+ # main loop
42
+ loops = 0
43
+ loop do
44
+ @service_watchers.each do |w|
45
+ raise "synapse: service watcher #{w.name} failed ping!" unless w.ping?
46
+ end
47
+
48
+ if @config_updated
49
+ @config_updated = false
50
+ log.info "synapse: regenerating haproxy config"
51
+ @haproxy.update_config(@service_watchers)
52
+ else
53
+ sleep 1
54
+ end
55
+
56
+ loops += 1
57
+ log.debug "synapse: still running at #{Time.now}" if (loops % 60) == 0
58
+ end
59
+
60
+ rescue StandardError => e
61
+ log.error "synapse: encountered unexpected exception #{e.inspect} in main thread"
62
+ raise e
63
+ ensure
64
+ log.warn "synapse: exiting; sending stop signal to all watchers"
65
+
66
+ # stop all the watchers
67
+ @service_watchers.map(&:stop)
68
+ end
69
+
70
+ def reconfigure!
71
+ @config_updated = true
72
+ end
73
+
74
+ private
75
+ def create_service_watchers(services={})
76
+ service_watchers =[]
77
+ services.each do |service_name, service_config|
78
+ service_watchers << ServiceWatcher.create(service_name, service_config, self)
79
+ end
80
+
81
+ return service_watchers
82
+ end
83
+
84
+ end
85
+ end