stubby 0.0.10 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,17 +1,55 @@
1
1
  # Stubby
2
2
 
3
- A local DNS and HTTP server combo that provides a package manager
4
- solution to configuring network systems on a development machine.
5
-
6
- Use it to:
7
-
8
- * manage your .dev domains
9
-
10
- * distribute a spec for your API so developers can run basic tests without
11
- hitting your dev server.
3
+ Stubby makes your development environment act more like your
4
+ production environment.
5
+
6
+ A local DNS and HTTP server combo that provides a declarative
7
+ environment based configuration for talking to servers that
8
+ act exactly like production but aren't.
9
+
10
+ ## Philosophy
11
+
12
+ Centralized configuration systems instruct your application how to
13
+ function on the host environment. Stubby prefers that your application ask
14
+ the environment to mold itself to the needs of the application.
15
+
16
+ Consider your app is a manager sent to Germany to lead an automotive
17
+ operation. The manager has a language dictionary and translates each order
18
+ to German before giving it to the team. Consider the following interaction:
19
+
20
+ "Where's the screwdriver?"
21
+ "Wo ist der Schraubenzieher?"
22
+
23
+ =>
24
+
25
+ "Im roten Feld über die Straße"
26
+ "In the red box down the street"
27
+
28
+ # Get screwdriver from red box,
29
+ # screw bolt into metal
30
+
31
+ This is what we tell our applications to do when we use a centralized
32
+ configuration:
33
+
34
+ "Where's the database?"
35
+ ENV["DATABASE_URI"]
36
+ "mysql://blah/"
37
+
38
+ # Connect to database
39
+ # Execute query
40
+
41
+ Stubby is a translator in this instance. Since Stubby knows that you need
42
+ a screwdriver, and it knows where you look for it, Stubby will make sure
43
+ that the screwdriver your manager needs is where your manager expects it to be.
44
+
45
+ ## Uses
46
+
47
+ * manage your .dev domains (or any random old TLD)
48
+
49
+ * stub APIs so you can run tests locally
12
50
 
13
51
  * get your team on the right system with the proper hosts settings.
14
-
52
+
15
53
  ## Development Status
16
54
 
17
55
  The Stubfile.json format and the extension / adapter
data/lib/stubby.rb CHANGED
@@ -2,8 +2,10 @@ STUBBY_MASTER="172.16.123.1"
2
2
 
3
3
  require 'rubygems'
4
4
 
5
+
5
6
  require 'oj'
6
7
  require 'multi_json'
8
+
7
9
  require 'stubby/extensions/dns/osx'
8
10
  require 'stubby/extensions/dns'
9
11
  require 'stubby/extensions/http'
@@ -50,6 +50,37 @@ module Stubby
50
50
  master.run!
51
51
  end
52
52
 
53
+ desc "halt", "Shut down if running, restore if not"
54
+ def halt
55
+ if master_running?
56
+ stop
57
+ else
58
+ restore
59
+ end
60
+ end
61
+
62
+ desc "stop", "Stops a running stubby process"
63
+ def stop
64
+ if master_running?
65
+ Process.kill("INT", File.read(pidfile).to_i)
66
+
67
+ while master_running?
68
+ puts "." and sleep 1
69
+ end
70
+ end
71
+ end
72
+
73
+ desc "restore", "Restore defaults"
74
+ def restore
75
+ if master_running?
76
+ puts "[ERROR] Stubby needs to be shut down first"
77
+ exit
78
+ end
79
+
80
+ master = Stubby::Master.new({})
81
+ master.restore!
82
+ end
83
+
53
84
  desc "env NAME", "Switch stubby environment"
54
85
  long_desc <<-LONGDESC
55
86
  > $ sudo stubby env test
@@ -95,7 +126,11 @@ module Stubby
95
126
 
96
127
  private
97
128
  def pidfile
98
- @pidfile ||= File.expand_path("~/.stubby/pid")
129
+ @pidfile ||= (
130
+ home = File.expand_path("~/.stubby")
131
+ FileUtils.mkdir_p(home) unless File.exists?(home)
132
+ File.join(home, "pid")
133
+ )
99
134
  end
100
135
 
101
136
  def master_running?
@@ -1,72 +1,78 @@
1
- module Extensions
2
- class Default
3
- def initialize
1
+ module Stubby
2
+ module Extensions
3
+ class Default
4
+ def initialize
4
5
 
5
- end
6
+ end
6
7
 
7
- def run!(*args)
8
+ def run!(*args)
8
9
 
9
- end
10
+ end
10
11
 
11
- def stop!(*args)
12
+ def stop!(*args)
12
13
 
13
- end
14
+ end
15
+
16
+ def restore!(*args)
14
17
 
15
- def expand_rule(trigger, instruction)
16
- # Default expansion:
17
- # "example.com": "localhost:3000"
18
- #
19
- # =>
20
- # "dns://example.com": "@"
21
- # "http://example.com": "http-redirect://blank?to=https://example.com&code=302"
22
- # "https://example.com": "http-proxy://localhost:3000"
23
- #
24
- # "example.com:4000": "localhost:3000"
25
- #
26
- # =>
27
- # ERROR: port in trigger unsupported
28
- #
29
- # "dns://example.com": "@"
30
- # "http://example.com:4000"
31
- #
32
- # "example.com": "http-redirect://localhost:3000"
33
- #
34
- # =>
35
- # "dns://example.com": "@"
36
- # "http://example.com": "http-redirect://?blank?to=http://localhost:3000&code=302"
37
- # =====================================
38
- #
39
- # ".*\\.stubby.dev": "file:///var/www/tmp
40
- #
41
- # =>
42
- #
43
- # "dns://.*\\.stubby.dev": "@",
44
- # "http://.*\\.stubby.dev": "file:///var/www/tmp",
45
- # "https://.*\\.stubby.dev": "file:///var/www/tmp",
46
- scheme, remains = instruction.split("://")
47
- scheme, remains = remains, scheme if remains.nil?
48
-
49
- if scheme.nil?
50
- expand_bare(trigger, instruction)
51
- else
52
- expand_with_protocol(trigger, instruction)
53
18
  end
54
- end
55
19
 
56
- def expand_bare(trigger, instruction)
57
- {
58
- "dns://#{trigger}/a" => "dns-a://#{STUBBY_MASTER}",
59
- "http://#{trigger}" => "http-redirect://blank?to=https://#{trigger}&code=302",
60
- "https://#{trigger}" => "http-proxy://#{instruction}"
61
- }
62
- end
20
+ def expand_rule(trigger, instruction)
21
+ # Default expansion:
22
+ # "example.com": "localhost:3000"
23
+ #
24
+ # =>
25
+ # "dns://example.com": "@"
26
+ # "http://example.com": "http-redirect://blank?to=https://example.com&code=302"
27
+ # "https://example.com": "http-proxy://localhost:3000"
28
+ #
29
+ # "example.com:4000": "localhost:3000"
30
+ #
31
+ # =>
32
+ # ERROR: port in trigger unsupported
33
+ #
34
+ # "dns://example.com": "@"
35
+ # "http://example.com:4000"
36
+ #
37
+ # "example.com": "http-redirect://localhost:3000"
38
+ #
39
+ # =>
40
+ # "dns://example.com": "@"
41
+ # "http://example.com": "http-redirect://?blank?to=http://localhost:3000&code=302"
42
+ # =====================================
43
+ #
44
+ # ".*\\.stubby.dev": "file:///var/www/tmp
45
+ #
46
+ # =>
47
+ #
48
+ # "dns://.*\\.stubby.dev": "@",
49
+ # "http://.*\\.stubby.dev": "file:///var/www/tmp",
50
+ # "https://.*\\.stubby.dev": "file:///var/www/tmp",
51
+ scheme, remains = instruction.split("://")
52
+ scheme, remains = remains, scheme if remains.nil?
53
+
54
+ if scheme.nil?
55
+ expand_bare(trigger, instruction)
56
+ else
57
+ expand_with_protocol(trigger, instruction)
58
+ end
59
+ end
63
60
 
64
- def expand_with_protocol(trigger, instruction)
65
- {
66
- "dns://#{trigger}/a" => "dns-a://#{STUBBY_MASTER}",
67
- "http://#{trigger}" => instruction,
68
- "https://#{trigger}" => instruction
69
- }
61
+ def expand_bare(trigger, instruction)
62
+ {
63
+ "dns://#{trigger}/a" => "dns-a://#{STUBBY_MASTER}",
64
+ "http://#{trigger}" => "http-redirect://blank?to=https://#{trigger}&code=302",
65
+ "https://#{trigger}" => "http-proxy://#{instruction}"
66
+ }
67
+ end
68
+
69
+ def expand_with_protocol(trigger, instruction)
70
+ {
71
+ "dns://#{trigger}/a" => "dns-a://#{STUBBY_MASTER}",
72
+ "http://#{trigger}" => instruction,
73
+ "https://#{trigger}" => instruction
74
+ }
75
+ end
70
76
  end
71
77
  end
72
78
  end
@@ -1,131 +1,136 @@
1
1
  require 'rubydns'
2
2
  require 'ipaddress'
3
3
  require 'uri'
4
- require 'pry'
5
-
6
- module Extensions
7
- module DNS
8
- class UnsupportedOS < Exception; end
9
-
10
- class Server < RubyDNS::Server
11
- UPSTREAM = RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
12
- IN = Resolv::DNS::Resource::IN
13
-
14
- case RbConfig::CONFIG['host_os']
15
- when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
16
- raise UnsupportedOS, "Sorry, Windows is not currently supported"
17
- when /darwin|mac os/
18
- include Extensions::DNS::OSX
19
- when /linux/
20
- raise UnsupportedOS, "Sorry, Linux is not currently supported"
21
- else
22
- raise UnsupportedOS, "Sorry, #{RbConfig::CONFIG['host_os']} wasn't recognized"
23
- end
24
4
 
25
- def process(name, resource_class, transaction)
26
- body = HTTPI.post("http://#{STUBBY_MASTER}:9000/rules/search.json",
27
- trigger: "dns://#{name}/#{symbol_from_resource_class(resource_class)}").body
5
+ module Stubby
6
+ module Extensions
7
+ module DNS
8
+ class UnsupportedOS < Exception; end
9
+
10
+ class Server < RubyDNS::Server
11
+ UPSTREAM = RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
12
+ IN = Resolv::DNS::Resource::IN
13
+
14
+ case RbConfig::CONFIG['host_os']
15
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
16
+ raise UnsupportedOS, "Sorry, Windows is not currently supported"
17
+ when /darwin|mac os/
18
+ include Stubby::Extensions::DNS::OSX
19
+ when /linux/
20
+ raise UnsupportedOS, "Sorry, Linux is not currently supported"
21
+ else
22
+ raise UnsupportedOS, "Sorry, #{RbConfig::CONFIG['host_os']} wasn't recognized"
23
+ end
28
24
 
29
- instruction = MultiJson.load(body)
25
+ def process(name, resource_class, transaction)
26
+ body = HTTPI.post("http://#{STUBBY_MASTER}:9000/rules/search.json",
27
+ trigger: "dns://#{name}/#{symbol_from_resource_class(resource_class)}").body
30
28
 
31
- if instruction.nil? or instruction == "@"
32
- transaction.passthrough!(UPSTREAM)
33
- return
34
- end
29
+ instruction = MultiJson.load(body)
35
30
 
36
- url = URI.parse(instruction)
31
+ if instruction.nil? or instruction == "@"
32
+ transaction.passthrough!(UPSTREAM)
33
+ return
34
+ end
37
35
 
38
- response_resource_class = resource url.scheme.gsub('dns-', '')
36
+ url = URI.parse(instruction)
39
37
 
40
- if !IPAddress.valid?(url.host) and response_resource_class == IN::A
41
- response_resource_class = IN::CNAME
42
- end
38
+ response_resource_class = resource url.scheme.gsub('dns-', '')
43
39
 
44
- response = url.host
40
+ if !IPAddress.valid?(url.host) and response_resource_class == IN::A
41
+ response_resource_class = IN::CNAME
42
+ end
45
43
 
46
- if [IN::CNAME, IN::MX].include? response_resource_class
47
- response = Resolv::DNS::Name.create(url.host)
48
- end
44
+ response = url.host
49
45
 
50
- puts "DNS: #{name} => #{response}-#{resource_class.name})"
46
+ if [IN::CNAME, IN::MX].include? response_resource_class
47
+ response = Resolv::DNS::Name.create(url.host)
48
+ end
51
49
 
52
- if response_resource_class == IN::MX
53
- transaction.respond!(10, response,
54
- :resource_class => response_resource_class,
55
- :ttl => 0)
56
- else
57
- transaction.respond!(response,
58
- :resource_class => response_resource_class,
59
- :ttl => 0)
60
- end
61
- end
50
+ puts "DNS: #{name} => #{response}-#{resource_class.name})"
62
51
 
63
- def run!(session, options)
64
- return if options[:dns] == false
65
- trap("INT"){ stop! }
52
+ if response_resource_class == IN::MX
53
+ transaction.respond!(10, response,
54
+ :resource_class => response_resource_class,
55
+ :ttl => 0)
56
+ else
57
+ transaction.respond!(response,
58
+ :resource_class => response_resource_class,
59
+ :ttl => 0)
60
+ end
61
+ end
66
62
 
67
- @session = session
68
- setup_references and run_dns_server
69
- end
63
+ def run!(session, options)
64
+ return if options[:dns] == false
65
+ trap("INT"){ stop! }
70
66
 
71
- def stop!
72
- teardown_references and stop_dns_server
73
- end
67
+ @session = session
68
+ setup_references and run_dns_server
69
+ end
74
70
 
75
- def expand_rule(trigger, instruction)
76
- i = URI.parse(instruction)
77
- t = URI.parse(trigger)
71
+ def stop!
72
+ teardown_references and stop_dns_server
73
+ end
78
74
 
79
- # If not specifying a record type, match a
80
- t.path = "/a" if t.path.empty?
81
-
82
- if i.scheme.nil?
83
- { t.to_s => "dns-a://#{instruction}" }
84
- else
85
- { t.to_s => instruction }
75
+ def restore!
76
+ restore_references
86
77
  end
87
- end
78
+
79
+ def expand_rule(trigger, instruction)
80
+ i = URI.parse(instruction)
81
+ t = URI.parse(trigger)
82
+
83
+ # If not specifying a record type, match a
84
+ t.path = "/a" if t.path.empty?
88
85
 
89
- private
86
+ if i.scheme.nil?
87
+ { t.to_s => "dns-a://#{instruction}" }
88
+ else
89
+ { t.to_s => instruction }
90
+ end
91
+ end
92
+
93
+ private
90
94
 
91
- def resource(pattern)
92
- return IN::A unless pattern.respond_to? :to_sym
93
- symbol_to_resource_class[pattern.to_sym] || IN::A
94
- end
95
+ def resource(pattern)
96
+ return IN::A unless pattern.respond_to? :to_sym
97
+ symbol_to_resource_class[pattern.to_sym] || IN::A
98
+ end
95
99
 
96
- def symbol_from_resource_class(klass)
97
- symbol_to_resource_class.invert[klass] || :a
98
- end
100
+ def symbol_from_resource_class(klass)
101
+ symbol_to_resource_class.invert[klass] || :a
102
+ end
99
103
 
100
- def symbol_to_resource_class
101
- {
102
- a: IN::A,
103
- aaaa: IN::AAAA,
104
- srv: IN::SRV,
105
- wks: IN::WKS,
106
- minfo: IN::MINFO,
107
- mx: IN::MX,
108
- ns: IN::NS,
109
- ptr: IN::PTR,
110
- soa: IN::SOA,
111
- txt: IN::TXT,
112
- cname: IN::CNAME
113
- }
114
- end
104
+ def symbol_to_resource_class
105
+ {
106
+ a: IN::A,
107
+ aaaa: IN::AAAA,
108
+ srv: IN::SRV,
109
+ wks: IN::WKS,
110
+ minfo: IN::MINFO,
111
+ mx: IN::MX,
112
+ ns: IN::NS,
113
+ ptr: IN::PTR,
114
+ soa: IN::SOA,
115
+ txt: IN::TXT,
116
+ cname: IN::CNAME
117
+ }
118
+ end
115
119
 
116
- def run_dns_server
117
- logger.level = Logger::INFO
120
+ def run_dns_server
121
+ logger.level = Logger::INFO
118
122
 
119
- EventMachine.run do
120
- run(:listen => [[:tcp, STUBBY_MASTER, 53],
121
- [:udp, STUBBY_MASTER, 53]])
123
+ EventMachine.run do
124
+ run(:listen => [[:tcp, STUBBY_MASTER, 53],
125
+ [:udp, STUBBY_MASTER, 53]])
126
+ end
122
127
  end
123
- end
124
128
 
125
- def stop_dns_server
126
- fire :stop
127
- EventMachine::stop_event_loop
128
- rescue
129
+ def stop_dns_server
130
+ fire :stop
131
+ EventMachine::stop_event_loop
132
+ rescue
133
+ end
129
134
  end
130
135
  end
131
136
  end