stubby 0.0.10 → 0.0.11

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.
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