engineyard-dns 0.4.0

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.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format=progress
2
+ --colour
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - rbx
5
+ - ree
@@ -0,0 +1,29 @@
1
+ # ChangeLog
2
+
3
+ ## v0.4.0 - 2011/5/23
4
+
5
+ * supporting many DNS providers via fog
6
+ * automatic discovery of which DNS provider hosts your domain
7
+ * see README for setting up fog credentials
8
+
9
+ ## v0.3.0 - 2011/5/22
10
+
11
+ * ey-dns assign DOMAIN NAME - can pass a subdomain name (only that record is created)
12
+
13
+ ## v0.2.0 - 2011/5/22
14
+
15
+ * If [domain, name] pair already exists then prompt for override
16
+
17
+ ## v0.1.2 - 2011/5/22
18
+
19
+ * Cucumber scenario is now working against http://test.dnsimple.com
20
+ * Worth rereleasing gem just to celebrate
21
+
22
+ ## v0.1.1 - 2011/5/22
23
+
24
+ * Create .myapp.com and www.myapp.com A records
25
+
26
+ ## v0.1.0 - 2011/5/22
27
+
28
+ * Initial release
29
+ * Basic implementation of `ey-dns assign DOMAIN`
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in engineyard-dns.gemspec
4
+ gemspec
@@ -0,0 +1,106 @@
1
+ # Simple DNS for Engine Yard AppCloud environments
2
+
3
+ Currently creates A records for a domain (.myapp.com and www.myapp.com) to
4
+ point to the public IP of an AppCloud environment.
5
+
6
+ You can use any DNS provider supported by fog, including AWS Route 53, Blue Box, DNSimple, Linode, Slicehost and Zerigo.
7
+
8
+ For example:
9
+
10
+ <img src="https://img.skitch.com/20110523-x5mhutfr8r79parhuq7r44sqma.png">
11
+
12
+ NOTE: This image is an artist's impression. Probably an artist who doesn't know how
13
+ HTTP requests and DNS work. HTTP reqests don't go through DNSimple (or any DNS nameserver).
14
+ The host in the URL is translated from myapp.com into an IP address (e.g. 1.2.3.4) by DNS.
15
+ The HTTP request then goes to the IP address. But the picture looks nice.
16
+
17
+ ## Usage
18
+
19
+ First, setup DNS credentials in `~/.fog` (see below).
20
+
21
+ Basic usage is very simple. Assuming you have registered `myapp.com`
22
+ with one of your DNS providers, the following will automatically work:
23
+
24
+ $ ey-dns assign myapp.com
25
+ Found AppCloud environment giblets on account main with IP 1.2.3.4
26
+ Found myapp.com in DNSimple account
27
+
28
+ Assigning myapp.com --> 1.2.3.4 (drnic/myapp_production)
29
+ Assigning www.myapp.com --> 1.2.3.4 (drnic/myapp_production)
30
+
31
+ If an AppCloud environment cannot be automatically detected, explicitly pass -e or -a flags
32
+ like the `ey` CLI itself:
33
+
34
+ $ ey-dns assign myapp.com -e myapp_production
35
+
36
+ If .myapp.com or www.myapp.com already exist you will be prompted to override them.
37
+ You can force the override with the `--override` or `-o` flag.
38
+
39
+ ## Setup
40
+
41
+ $ gem install engineyard-dns
42
+
43
+ To setup credentials for AppCloud, run the following command for the first time and
44
+ you will be prompted for credentials:
45
+
46
+ $ ey environments --all
47
+
48
+ To setup credentials for DNSimple, create a file `~/.fog` to look like:
49
+
50
+ :default:
51
+ :dnsimple_email: EMAIL
52
+ :dnsimple_password: PASSWORD
53
+
54
+ To setup credentials for AWS Route 53, create a file `~/.fog` to look like:
55
+
56
+ :default:
57
+ :aws_access_key_id: ACCESS_KEY
58
+ :aws_secret_access_key: SECRET_ACCESS_KEY
59
+
60
+ If you have multiple DNS providers then add as many credentials within `:default`
61
+ as you like.
62
+
63
+ On Unix, make this file readable to you only:
64
+
65
+ $ chmod 600 ~/.fog
66
+
67
+ Test you have fog working with your DNS provider:
68
+
69
+ $ fog
70
+ >> provider = 'DNSimple' # or 'AWS' or 'Slicehost' etc
71
+ >> Fog::DNS.new({:provider => provider}).zones.all.map(&:domain)
72
+ ["drnicwilliams.com", "emrubyconf.com", ...]
73
+
74
+
75
+ ## Development
76
+
77
+ [![Build Status](http://travis-ci.org/engineyard/engineyard-dns.png)](http://travis-ci.org/engineyard/engineyard-dns)
78
+
79
+ The test suite is purely cucumber scenarios. No rspec tests are being used. There are credentials for http://test.dnsimple.com built into the test suite. You should not have to do anything to run the tests except:
80
+
81
+ git clone git://github.com/engineyard/engineyard-dns.git
82
+ cd engineyard-dns
83
+ bundle
84
+ bundle exec rake
85
+
86
+ ## License
87
+
88
+ Copyright (c) 2010 Engine Yard, Inc
89
+
90
+ Permission is hereby granted, free of charge, to any person obtaining a copy
91
+ of this software and associated documentation files (the "Software"), to deal
92
+ in the Software without restriction, including without limitation the rights
93
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
94
+ copies of the Software, and to permit persons to whom the Software is
95
+ furnished to do so, subject to the following conditions:
96
+
97
+ The above copyright notice and this permission notice shall be included in
98
+ all copies or substantial portions of the Software.
99
+
100
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
101
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
102
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
103
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
104
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
105
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
106
+ THE SOFTWARE.
@@ -0,0 +1,24 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+
6
+ desc "Run all examples"
7
+ RSpec::Core::RakeTask.new
8
+
9
+ namespace :cucumber do
10
+ require 'cucumber/rake/task'
11
+ Cucumber::Rake::Task.new(:wip, 'Run features that are being worked on') do |t|
12
+ t.cucumber_opts = "--tags @wip"
13
+ end
14
+ Cucumber::Rake::Task.new(:ok, 'Run features that should be working') do |t|
15
+ t.cucumber_opts = "--tags ~@wip"
16
+ end
17
+ task :all => [:ok, :wip]
18
+ end
19
+
20
+ desc 'Alias for cucumber:ok'
21
+ task :cucumber => 'cucumber:ok'
22
+
23
+ desc "Specs and Cucumber features by default"
24
+ task :default => ["spec", "cucumber"]
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
4
+ require 'engineyard-dns'
5
+ require 'engineyard-dns/cli'
6
+
7
+ begin
8
+ EngineYard::DNS::CLI.start
9
+ rescue EY::Error => e
10
+ EY.ui.print_exception(e)
11
+ exit(1)
12
+ rescue Interrupt => e
13
+ puts
14
+ EY.ui.print_exception(e)
15
+ EY.ui.say("Quitting...")
16
+ exit(1)
17
+ end
@@ -0,0 +1,35 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "engineyard-dns/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "engineyard-dns"
7
+ s.version = EngineYard::DNS::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Dr Nic Williams"]
10
+ s.email = ["drnicwilliams@gmail.com"]
11
+ s.homepage = "https://github.com/engineyard/engineyard-dns#readme"
12
+ s.summary = %q{Configure your Engine Yard AppCloud environment and your DNSimple domain.}
13
+ s.description = %q{Easily configure your DNS with Engine Yard AppCloud via DNSimple.}
14
+
15
+ s.rubyforge_project = "engineyard-dns"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency("thor")
23
+ s.add_dependency("engineyard")
24
+ s.add_dependency("fog")
25
+
26
+ s.add_development_dependency("rake", ["~> 0.9.0"])
27
+ s.add_development_dependency("cucumber", ["~> 0.10"])
28
+ s.add_development_dependency("rspec", ["~> 2.5"])
29
+ s.add_development_dependency("json", ["~>1.4.0"])
30
+ s.add_development_dependency("awesome_print")
31
+ s.add_development_dependency("realweb", '~>0.1.6')
32
+ s.add_development_dependency("open4")
33
+ s.add_development_dependency("sinatra")
34
+ s.add_development_dependency("fakeweb", "~>1.3.0")
35
+ end
@@ -0,0 +1,74 @@
1
+ Feature: Assign DNS to environment IP address via DNSimple
2
+ I want to assign DNS record to an AppCloud environment IP address
3
+
4
+ Background:
5
+ Given I have setup my engineyard email/password for API access
6
+ And I have "two apps" in AppCloud
7
+ And I have setup my fog credentials for "DNSimple"
8
+ And I have DNS domain "myapp.com" with provider "DNSimple"
9
+
10
+ Scenario: Assign new DNS A Record to an environment
11
+ When I run local executable "ey-dns" with arguments "assign myapp.com --account main --environment giblets"
12
+ Then I should see exactly
13
+ """
14
+ Fetching AppCloud environment information...
15
+ Found AppCloud environment giblets on account main with IP 174.129.7.113
16
+
17
+ Searching for myapp.com amongst your DNS providers...
18
+ Found myapp.com in DNSimple account
19
+
20
+ Assigning myapp.com --> 174.129.7.113 (main/giblets)
21
+ Created A record for myapp.com
22
+ Assigning www.myapp.com --> 174.129.7.113 (main/giblets)
23
+ Created A record for www.myapp.com
24
+ Complete!
25
+
26
+ """
27
+ # Found 2 records for myapp.com
28
+ # .myapp.com (A)-> 174.129.7.113 (ttl:60, id:\d+)
29
+ # www.myapp.com (A)-> 174.129.7.113 (ttl:60, id:\d+)
30
+
31
+ Scenario: Resssign DNS A Record to an environment
32
+ When I run local executable "ey-dns" with arguments "assign myapp.com --account main --environment giblets"
33
+ And I run local executable "ey-dns" with arguments "assign myapp.com --account main --environment giblets --override"
34
+ Then I should see matching
35
+ """
36
+ Fetching AppCloud environment information...
37
+ Found AppCloud environment giblets on account main with IP 174.129.7.113
38
+
39
+ Searching for myapp.com amongst your DNS providers...
40
+ Found myapp.com in DNSimple account
41
+
42
+ Deleted myapp.com
43
+ Assigning myapp.com --> 174.129.7.113 (main/giblets)
44
+ Created A record for myapp.com
45
+ Deleted www.myapp.com
46
+ Assigning www.myapp.com --> 174.129.7.113 (main/giblets)
47
+ Created A record for www.myapp.com
48
+ Complete!
49
+
50
+ """
51
+ # Found 2 records for myapp.com
52
+ # .myapp.com (A)-> 174.129.7.113 (ttl:60, id:\d+)
53
+ # www.myapp.com (A)-> 174.129.7.113 (ttl:60, id:\d+)
54
+
55
+ Scenario: Assign subdomain A Record to an environment
56
+ When I run local executable "ey-dns" with arguments "assign myapp.com staging --account main --environment giblets"
57
+ Then I should see exactly
58
+ """
59
+ Fetching AppCloud environment information...
60
+ Found AppCloud environment giblets on account main with IP 174.129.7.113
61
+
62
+ Searching for myapp.com amongst your DNS providers...
63
+ Found myapp.com in DNSimple account
64
+
65
+ Assigning staging.myapp.com --> 174.129.7.113 (main/giblets)
66
+ Created A record for staging.myapp.com
67
+ Complete!
68
+
69
+ """
70
+ # Found 1 records for myapp.com
71
+ # staging.myapp.com (A)-> 174.129.7.113 (ttl:60, id:\d+)
72
+
73
+
74
+
@@ -0,0 +1,26 @@
1
+ Feature: Assign DNS to environment IP address
2
+ I want to assign DNS record to an AppCloud environment IP address
3
+
4
+ Background:
5
+ Given I have setup my engineyard email/password for API access
6
+ And I have "two apps" in AppCloud
7
+
8
+ Scenario: Assign new DNS A Record to an environment via fog to DNSimple
9
+ Given I have setup my fog credentials for "DNSimple"
10
+ And I have DNS domain "myapp.com" with provider "DNSimple"
11
+ When I run local executable "ey-dns" with arguments "assign myapp.com --account main --environment giblets"
12
+ Then I should see exactly
13
+ """
14
+ Fetching AppCloud environment information...
15
+ Found AppCloud environment giblets on account main with IP 174.129.7.113
16
+
17
+ Searching for myapp.com amongst your DNS providers...
18
+ Found myapp.com in DNSimple account
19
+
20
+ Assigning myapp.com --> 174.129.7.113 (main/giblets)
21
+ Created A record for myapp.com
22
+ Assigning www.myapp.com --> 174.129.7.113 (main/giblets)
23
+ Created A record for www.myapp.com
24
+ Complete!
25
+
26
+ """
@@ -0,0 +1,175 @@
1
+ Given /^this project is active project folder/ do
2
+ @active_project_folder = File.expand_path(File.dirname(__FILE__) + "/../..")
3
+ end
4
+
5
+ Given /^env variable \$([\w_]+) set to "(.*)"/ do |env_var, value|
6
+ ENV[env_var] = value
7
+ end
8
+
9
+ Given /"(.*)" folder is deleted/ do |folder|
10
+ in_project_folder { FileUtils.rm_rf folder }
11
+ end
12
+
13
+ When /^I invoke "(.*)" generator with arguments "(.*)"$/ do |generator, arguments|
14
+ @stdout = StringIO.new
15
+ in_project_folder do
16
+ if Object.const_defined?("APP_ROOT")
17
+ APP_ROOT.replace(FileUtils.pwd)
18
+ else
19
+ APP_ROOT = FileUtils.pwd
20
+ end
21
+ run_generator(generator, arguments.split(' '), SOURCES, :stdout => @stdout)
22
+ end
23
+ File.open(File.join(@tmp_root, "generator.out"), "w") do |f|
24
+ @stdout.rewind
25
+ f << @stdout.read
26
+ end
27
+ end
28
+
29
+ When /^I run executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
30
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
31
+ in_project_folder do
32
+ system "#{executable} #{arguments} > #{@stdout} 2> #{@stdout}"
33
+ end
34
+ end
35
+
36
+ When /^I run project executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
37
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
38
+ in_project_folder do
39
+ system "ruby #{executable} #{arguments} > #{@stdout} 2> #{@stdout}"
40
+ end
41
+ end
42
+
43
+ When /^I run local executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
44
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
45
+ executable = File.expand_path(File.join(File.dirname(__FILE__), "/../../bin", executable))
46
+ in_project_folder do
47
+ system "ruby #{executable} #{arguments} > #{@stdout} 2> #{@stdout}"
48
+ end
49
+ end
50
+
51
+ When /^I invoke task "rake (.*)"/ do |task|
52
+ @stdout = File.expand_path(File.join(@tmp_root, "tests.out"))
53
+ in_project_folder do
54
+ system "rake #{task} --trace > #{@stdout} 2> #{@stdout}"
55
+ end
56
+ end
57
+
58
+ Then /^folder "(.*)" (is|is not) created/ do |folder, is|
59
+ in_project_folder do
60
+ File.exists?(folder).should(is == 'is' ? be_true : be_false)
61
+ end
62
+ end
63
+
64
+ Then /^file "(.*)" (is|is not) created/ do |file, is|
65
+ in_project_folder do
66
+ File.exists?(file).should(is == 'is' ? be_true : be_false)
67
+ end
68
+ end
69
+
70
+ Then /^file with name matching "(.*)" is created/ do |pattern|
71
+ in_project_folder do
72
+ Dir[pattern].should_not be_empty
73
+ end
74
+ end
75
+
76
+ Then /^file "(.*)" contents (does|does not) match \/(.*)\// do |file, does, regex|
77
+ in_project_folder do
78
+ actual_output = File.read(file)
79
+ (does == 'does') ?
80
+ actual_output.should(match(/#{regex}/)) :
81
+ actual_output.should_not(match(/#{regex}/))
82
+ end
83
+ end
84
+
85
+ Then /gem file "(.*)" and generated file "(.*)" should be the same/ do |gem_file, project_file|
86
+ File.exists?(gem_file).should be_true
87
+ File.exists?(project_file).should be_true
88
+ gem_file_contents = File.read(File.dirname(__FILE__) + "/../../#{gem_file}")
89
+ project_file_contents = File.read(File.join(@active_project_folder, project_file))
90
+ project_file_contents.should == gem_file_contents
91
+ end
92
+
93
+ Then /^(does|does not) invoke generator "(.*)"$/ do |does_invoke, generator|
94
+ actual_output = File.read(@stdout)
95
+ does_invoke == "does" ?
96
+ actual_output.should(match(/dependency\s+#{generator}/)) :
97
+ actual_output.should_not(match(/dependency\s+#{generator}/))
98
+ end
99
+
100
+ Then /help options "(.*)" and "(.*)" are displayed/ do |opt1, opt2|
101
+ actual_output = File.read(@stdout)
102
+ actual_output.should match(/#{opt1}/)
103
+ actual_output.should match(/#{opt2}/)
104
+ end
105
+
106
+ Then /^I should see "([^\"]*)"$/ do |text|
107
+ actual_output = File.read(@stdout)
108
+ actual_output.should contain(text)
109
+ end
110
+
111
+ Then /^I should see$/ do |text|
112
+ actual_output = File.read(@stdout)
113
+ actual_output.should contain(text)
114
+ end
115
+
116
+ Then /^I should not see$/ do |text|
117
+ actual_output = File.read(@stdout)
118
+ actual_output.should_not contain(text)
119
+ end
120
+
121
+ Then /^I should see exactly$/ do |text|
122
+ actual_output = File.read(@stdout)
123
+ actual_output.should == text
124
+ end
125
+
126
+ Then /^I should see matching$/ do |text|
127
+ regexp = Regexp.new(text.gsub("(", '\(').gsub(")", '\)'))
128
+ actual_output = File.read(@stdout)
129
+ actual_output.should match(regexp)
130
+ end
131
+
132
+
133
+ Then /^I should see all (\d+) tests pass/ do |expected_test_count|
134
+ expected = %r{^#{expected_test_count} tests, \d+ assertions, 0 failures, 0 errors}
135
+ actual_output = File.read(@stdout)
136
+ actual_output.should match(expected)
137
+ end
138
+
139
+ Then /^I should see all (\d+) examples pass/ do |expected_test_count|
140
+ expected = %r{^#{expected_test_count} examples?, 0 failures}
141
+ actual_output = File.read(@stdout)
142
+ actual_output.should match(expected)
143
+ end
144
+
145
+ Then /^yaml file "(.*)" contains (\{.*\})/ do |file, yaml|
146
+ in_project_folder do
147
+ yaml = eval yaml
148
+ YAML.load(File.read(file)).should == yaml
149
+ end
150
+ end
151
+
152
+ Then /^Rakefile can display tasks successfully/ do
153
+ @stdout = File.expand_path(File.join(@tmp_root, "rakefile.out"))
154
+ in_project_folder do
155
+ system "rake -T > #{@stdout} 2> #{@stdout}"
156
+ end
157
+ actual_output = File.read(@stdout)
158
+ actual_output.should match(/^rake\s+\w+\s+#\s.*/)
159
+ end
160
+
161
+ Then /^task "rake (.*)" is executed successfully/ do |task|
162
+ @stdout.should_not be_nil
163
+ actual_output = File.read(@stdout)
164
+ actual_output.should_not match(/^Don't know how to build task '#{task}'/)
165
+ actual_output.should_not match(/Error/i)
166
+ end
167
+
168
+ Then /^gem spec key "(.*)" contains \/(.*)\// do |key, regex|
169
+ in_project_folder do
170
+ gem_file = Dir["pkg/*.gem"].first
171
+ gem_spec = Gem::Specification.from_yaml(`gem spec #{gem_file}`)
172
+ spec_value = gem_spec.send(key.to_sym)
173
+ spec_value.to_s.should match(/#{regex}/)
174
+ end
175
+ end
@@ -0,0 +1,9 @@
1
+ Given /^I have setup my dnsimple credentials$/ do
2
+ setup_dnsimple_credentials
3
+ end
4
+
5
+ Given /^I have DNSimple domain "([^"]*)"$/ do |domain|
6
+ create_dnsimple_domain domain
7
+ end
8
+
9
+
@@ -0,0 +1,15 @@
1
+ Given /^I have setup my engineyard email\/password for API access$/ do
2
+ ENV['EYRC'] = File.join(@home_path, ".eyrc")
3
+ token = { ENV['CLOUD_URL'] => {
4
+ "api_token" => "f81a1706ddaeb148cfb6235ddecfc1cf"} }
5
+ File.open(ENV['EYRC'], "w"){|f| YAML.dump(token, f) }
6
+ end
7
+
8
+ When /^I have "two accounts, two apps, two environments, ambiguous" in AppCloud$/ do
9
+ api_scenario "two accounts, two apps, two environments, ambiguous"
10
+ end
11
+
12
+ # has a known public IP in its hostname ec2-174-129-7-113.compute-1.amazonaws.com
13
+ When /^I have "two apps" in AppCloud$/ do
14
+ api_scenario "two apps"
15
+ end
@@ -0,0 +1,7 @@
1
+ Given /^I have setup my fog credentials for "([^"]*)"$/ do |provider|
2
+ setup_dns_credentials(provider)
3
+ end
4
+
5
+ Given /^I have DNS domain "([^"]*)" with provider "([^"]*)"$/ do |domain, provider|
6
+ setup_domain(provider, domain)
7
+ end
@@ -0,0 +1,29 @@
1
+ module CommonHelpers
2
+ def in_tmp_folder(&block)
3
+ FileUtils.chdir(@tmp_root, &block)
4
+ end
5
+
6
+ def in_project_folder(&block)
7
+ project_folder = @active_project_folder || @tmp_root
8
+ FileUtils.chdir(project_folder, &block)
9
+ end
10
+
11
+ def in_home_folder(&block)
12
+ FileUtils.chdir(@home_path, &block)
13
+ end
14
+
15
+ def force_local_lib_override(project_name = @project_name)
16
+ rakefile = File.read(File.join(project_name, 'Rakefile'))
17
+ File.open(File.join(project_name, 'Rakefile'), "w+") do |f|
18
+ f << "$:.unshift('#{@lib_path}')\n"
19
+ f << rakefile
20
+ end
21
+ end
22
+
23
+ def setup_active_project_folder project_name
24
+ @active_project_folder = File.join(@tmp_root, project_name)
25
+ @project_name = project_name
26
+ end
27
+ end
28
+
29
+ World(CommonHelpers)
@@ -0,0 +1,24 @@
1
+ engineyard_loaded_path = $:.select { |path| path =~ %r|gems/engineyard-\d+| }.first
2
+ EY_ROOT = engineyard_loaded_path.gsub(%r|/\w+$|,'')
3
+
4
+ # helper to be stubbed out from engineyard spec_helper.rb
5
+ def shared_examples_for(title)
6
+ end
7
+
8
+ support = Dir[File.join(EY_ROOT,'/spec/support/*.rb')]
9
+ support.each{|helper| require helper }
10
+ World(Spec::Helpers)
11
+
12
+ require "fakeweb"
13
+
14
+ Before do
15
+ ENV["NO_SSH"] = "true"
16
+ ENV['CLOUD_URL'] = EY.fake_awsm
17
+ FakeWeb.allow_net_connect = true
18
+ end
19
+
20
+ After do
21
+ ENV.delete('CLOUD_URL')
22
+ ENV.delete('EYRC')
23
+ ENV.delete('NO_SSH')
24
+ end
@@ -0,0 +1,21 @@
1
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../../lib'))
2
+ require 'bundler/setup'
3
+ require 'engineyard-dns'
4
+ require 'dnsimple'
5
+ require 'dnsimple/cli'
6
+ require 'fog'
7
+
8
+ path = ENV['PATH']
9
+
10
+ Before do
11
+ @tmp_root = File.dirname(__FILE__) + "/../../tmp"
12
+ @active_project_folder = @tmp_root
13
+ @home_path = File.expand_path(File.join(@tmp_root, "home"))
14
+ @lib_path = File.expand_path(File.dirname(__FILE__) + "/../../lib")
15
+ @fixtures_path = File.expand_path(File.dirname(__FILE__) + "/../../fixtures")
16
+ FileUtils.rm_rf @tmp_root
17
+ FileUtils.mkdir_p @home_path
18
+ ENV['HOME'] = @home_path
19
+ fixture_bin_path = File.expand_path('../../../fixtures/bin', __FILE__)
20
+ ENV['PATH'] = fixture_bin_path + ":" + path
21
+ end
@@ -0,0 +1,26 @@
1
+ module FogHelpers
2
+ def setup_dns_credentials(provider)
3
+ email, password = "ossgrants+dns@engineyard.com", "ossgrants1"
4
+ File.open("#{ENV['HOME']}/.fog", "w") do |file|
5
+ @credentials = case provider.to_sym
6
+ when :DNSimple
7
+ { :dnsimple_email => email, :dnsimple_password => password}
8
+ else
9
+ raise "No credentials available for provider '#{provider}'"
10
+ end
11
+ file << YAML::dump(:default => @credentials)
12
+ end
13
+ end
14
+
15
+ def setup_domain(provider, domain)
16
+ dns_provider(provider).zones.select { |z| z.domain == domain }.each do |domain|
17
+ domain.destroy
18
+ end
19
+ dns_provider(provider).zones.create(:domain => domain)
20
+ end
21
+
22
+ def dns_provider(provider)
23
+ @dns_provider ||= Fog::DNS.new({:provider => provider})
24
+ end
25
+ end
26
+ World(FogHelpers)
@@ -0,0 +1,11 @@
1
+ module Matchers
2
+ def contain(expected)
3
+ simple_matcher("contain #{expected.inspect}") do |given, matcher|
4
+ matcher.failure_message = "expected #{given.inspect} to contain #{expected.inspect}"
5
+ matcher.negative_failure_message = "expected #{given.inspect} not to contain #{expected.inspect}"
6
+ given.index expected
7
+ end
8
+ end
9
+ end
10
+
11
+ World(Matchers)
@@ -0,0 +1,5 @@
1
+ module EngineYard
2
+ module DNS
3
+ # Your code goes here...
4
+ end
5
+ end
@@ -0,0 +1,166 @@
1
+ require "engineyard"
2
+ require "engineyard/thor"
3
+ require "engineyard/cli"
4
+ require "engineyard/cli/ui"
5
+ require "engineyard/error"
6
+ require "fog"
7
+ require "fog/bin"
8
+
9
+ module EngineYard
10
+ module DNS
11
+ class CLI < Thor
12
+ include EY::UtilityMethods
13
+ # include Thor::Actions
14
+
15
+ def self.start(*)
16
+ Thor::Base.shell = EY::CLI::UI
17
+ EY.ui = EY::CLI::UI.new
18
+ super
19
+ end
20
+
21
+
22
+ desc "assign DOMAIN [NAME]", "Assign DNS domain/tld (or name.tld) to your AppCloud environment"
23
+ method_option :verbose, :aliases => ["-V"], :desc => "Display more output"
24
+ method_option :environment, :aliases => ["-e"], :desc => "Environment in which to deploy this application", :type => :string
25
+ method_option :account, :aliases => ["-c"], :desc => "Name of the account you want to deploy in"
26
+ method_option :override, :aliases => ["-o"], :type => :boolean, :desc => "Override DNSimple records if they already exist"
27
+ def assign(domain_name, name = "")
28
+ say "Fetching AppCloud environment information..."; $stdout.flush
29
+
30
+ environment = fetch_environment(options[:environment], options[:account])
31
+ account_name, env_name = environment.account.name, environment.name
32
+ unless environment.instances.first
33
+ error "Environment #{account_name}/#{env_name} has no booted instances."
34
+ end
35
+ public_hostname = environment.instances.first.public_hostname
36
+ status = environment.instances.first.status
37
+
38
+ # TODO - use DNS client to convert public_hostname into IP address
39
+ unless public_hostname =~ /ec2-(\d+)-(\d+)-(\d+)-(\d+)/
40
+ error "Cannot determine public IP from current hostname #{public_hostname}"
41
+ end
42
+ public_ip = "#{$1}.#{$2}.#{$3}.#{$4}"
43
+
44
+ say "Found AppCloud environment #{env_name} on account #{account_name} with IP #{public_ip}"
45
+
46
+ say ""
47
+ say "Searching for #{domain_name} amongst your DNS providers..."; $stdout.flush
48
+
49
+ domain, provider_name = find_domain(domain_name)
50
+ unless domain
51
+ error "Please register domain #{domain_name} with your DNS provider"
52
+ end
53
+ say "Found #{domain_name} in #{provider_name} account"
54
+ say ""; $stdout.flush
55
+
56
+ assign_dns(domain, account_name, env_name, public_ip, name, options[:override])
57
+ assign_dns(domain, account_name, env_name, public_ip, "www", options[:override]) if name == ""
58
+
59
+ say "Complete!", :green
60
+
61
+ # ::DNSimple::Commands::ListRecords.new.execute([domain])
62
+ end
63
+
64
+ desc "version", "show version information"
65
+ def version
66
+ require 'engineyard-jenkins/version'
67
+ shell.say Engineyard::Jenkins::VERSION
68
+ end
69
+
70
+ map "-v" => :version, "--version" => :version, "-h" => :help, "--help" => :help
71
+
72
+ private
73
+ def say(msg, color = nil)
74
+ color ? shell.say(msg, color) : shell.say(msg)
75
+ end
76
+
77
+ def display(text)
78
+ shell.say text
79
+ exit
80
+ end
81
+
82
+ def error(text)
83
+ shell.say "ERROR: #{text}", :red
84
+ exit
85
+ end
86
+
87
+ def watch_page_while(host, port, path)
88
+ waiting = true
89
+ while waiting
90
+ begin
91
+ Net::HTTP.start(host, port) do |http|
92
+ req = http.get(path)
93
+ waiting = yield req
94
+ end
95
+ sleep 1; print '.'; $stdout.flush
96
+ rescue SocketError => e
97
+ sleep 1; print 'x'; $stdout.flush
98
+ rescue Exception => e
99
+ puts e.message
100
+ sleep 1; print '.'; $stdout.flush
101
+ end
102
+ end
103
+ end
104
+
105
+ # Discover which DNS provider (DNSimple, etc) is controlling +domain_name+ (a zone)
106
+ # and return [domain/zone, provider name]
107
+ #
108
+ # TODO remove hard-wiring for dnsimple; and discover which provider hosts domain/zone
109
+ # TODO how to get provider name (2nd result) from fog's Zone class? (no need to return 2nd arg)
110
+ def find_domain(domain_name)
111
+ dns_provider_names.each do |provider|
112
+ dns_provider = ::Fog::DNS.new({:provider => provider})
113
+ if domain = dns_provider.zones.select {|z| z.domain == domain_name}.first
114
+ return [domain, provider]
115
+ end
116
+ end
117
+ [nil, nil]
118
+ end
119
+
120
+ def assign_dns(domain, account_name, env_name, public_ip, name = "", override = false)
121
+ if record = domain.records.select {|r| r.name == name}.first
122
+ if override || ask_override_dns?(domain, name)
123
+ record.destroy
124
+ say "Deleted #{domain_name domain, name}"
125
+ else
126
+ error "Cannot replace existing #{domain_name domain, name} DNS"
127
+ end
128
+ end
129
+ say "Assigning "; say "#{domain_name domain, name} ", :green; say "--> "; say "#{public_ip} ", :green; say "(#{account_name}/#{env_name})"
130
+ $stdout.flush
131
+
132
+ record = domain.records.create(:ip => public_ip, :name => name, :type => "A", :ttl => "60")
133
+ say "Created A record for #{domain_name domain, name}"
134
+ end
135
+
136
+ def ask_override_dns?(domain, name)
137
+ ui = EY::CLI::UI::Prompter.backend
138
+ ui.agree("Replace #{domain_name domain, name}: ", "y")
139
+ end
140
+
141
+ # "myapp.com", "name" => "name.myapp.com"
142
+ # "myapp.com", "" => "myapp.com"
143
+ def domain_name(domain, name = nil)
144
+ if name && name.length > 0
145
+ "#{name}.#{domain.domain}"
146
+ else
147
+ domain.domain
148
+ end
149
+ end
150
+
151
+ # Returns the list of DNS providers that the current user has access to
152
+ # Includes the +fog_dns_providers+ list
153
+ # TODO find credentials in alternate locations (e.g. ~/.dnsimple)
154
+ def dns_provider_names
155
+ fog_dns_provider_names
156
+ end
157
+
158
+ # Returns the list of DNS providers that the current user has fog credentials
159
+ # TODO how do I get the base list from fog?
160
+ def fog_dns_provider_names
161
+ ['AWS', 'Bluebox', 'DNSimple', 'Linode', 'Slicehost', 'Zerigo'] & Fog.providers
162
+ end
163
+
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,5 @@
1
+ module EngineYard
2
+ module DNS
3
+ VERSION = "0.4.0"
4
+ end
5
+ end
@@ -0,0 +1,34 @@
1
+ # Simple DNS for AppCloud with DNSimple
2
+
3
+ For me, one of the things I liked to do with a new AppCloud application is to attached a pretty domain. The default AWS EC2 URL doesn't glorify my fine efforts.
4
+
5
+ I've started using the new [DNSimple](http://dnsimple.com/) as my DNS registrar and name server, and I believe an increasing number of AppCloud customers are too.
6
+
7
+ To make setting up DNS easier with AppCloud, we've released the `ey-dns` command line application.
8
+
9
+ It's really quite easy to use:
10
+
11
+ 1. Register your application's domain with [DNSimple](http://dnsimple.com/)
12
+ 2. Transfer your domain to DNSimple or change the name servers to ns1.dnsimple.com (ns2, ns3, etc)
13
+ 3. Install and run the command line application:
14
+
15
+ $ gem install engineyard-dns
16
+ $ cd path/to/my/app
17
+ $ ey-dns apply myapp.com
18
+ Assigning myapp.com --> 1.2.3.4 (drnic/myapp_production)
19
+ Assigning www.myapp.com --> 1.2.3.4 (drnic/myapp_production)
20
+
21
+ Found 2 records for myapp.com
22
+ .myapp.com (A)-> 1.2.3.4 (ttl:, id:12345)
23
+ www.myapp.com (A)-> 1.2.3.4 (ttl:, id:12346)
24
+
25
+ If you have previously assigned the domain records to another host, it will prompt you to change them.
26
+
27
+ If there is any confusion about which AppCloud environment is hosting your application, it will show you your options and then you can use the `--environment` and `--account` options to be more specific:
28
+
29
+ $ ey-dns apply myapp.com --environment myapp_production
30
+ $ ey-dns apply myapp.com staging --environment myapp_staging
31
+
32
+ Hopefully this tool makes it much easier to setup or change DNS for your AppCloud environments. Let us know in the comments or in the project's [Issues](https://github.com/engineyard/engineyard-dns/issues) if you love it, find bugs or have feature requests.
33
+
34
+ The source and instructions for the project is [available on GitHub](https://github.com/engineyard/engineyard-dns#readme).
@@ -0,0 +1,11 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+ require 'rspec'
4
+
5
+ Dir[File.dirname(__FILE__) + '/support/*'].each{|path| require path}
6
+
7
+ require 'engineyard-dns'
8
+
9
+ RSpec.configure do |config|
10
+
11
+ end
metadata ADDED
@@ -0,0 +1,223 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: engineyard-dns
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.4.0
6
+ platform: ruby
7
+ authors:
8
+ - Dr Nic Williams
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-05-23 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: thor
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "0"
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: engineyard
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: "0"
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: fog
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: rake
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ version: 0.9.0
58
+ type: :development
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: cucumber
62
+ prerelease: false
63
+ requirement: &id005 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: "0.10"
69
+ type: :development
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: rspec
73
+ prerelease: false
74
+ requirement: &id006 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ~>
78
+ - !ruby/object:Gem::Version
79
+ version: "2.5"
80
+ type: :development
81
+ version_requirements: *id006
82
+ - !ruby/object:Gem::Dependency
83
+ name: json
84
+ prerelease: false
85
+ requirement: &id007 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ version: 1.4.0
91
+ type: :development
92
+ version_requirements: *id007
93
+ - !ruby/object:Gem::Dependency
94
+ name: awesome_print
95
+ prerelease: false
96
+ requirement: &id008 !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: "0"
102
+ type: :development
103
+ version_requirements: *id008
104
+ - !ruby/object:Gem::Dependency
105
+ name: realweb
106
+ prerelease: false
107
+ requirement: &id009 !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ~>
111
+ - !ruby/object:Gem::Version
112
+ version: 0.1.6
113
+ type: :development
114
+ version_requirements: *id009
115
+ - !ruby/object:Gem::Dependency
116
+ name: open4
117
+ prerelease: false
118
+ requirement: &id010 !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: "0"
124
+ type: :development
125
+ version_requirements: *id010
126
+ - !ruby/object:Gem::Dependency
127
+ name: sinatra
128
+ prerelease: false
129
+ requirement: &id011 !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: "0"
135
+ type: :development
136
+ version_requirements: *id011
137
+ - !ruby/object:Gem::Dependency
138
+ name: fakeweb
139
+ prerelease: false
140
+ requirement: &id012 !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ~>
144
+ - !ruby/object:Gem::Version
145
+ version: 1.3.0
146
+ type: :development
147
+ version_requirements: *id012
148
+ description: Easily configure your DNS with Engine Yard AppCloud via DNSimple.
149
+ email:
150
+ - drnicwilliams@gmail.com
151
+ executables:
152
+ - ey-dns
153
+ extensions: []
154
+
155
+ extra_rdoc_files: []
156
+
157
+ files:
158
+ - .gitignore
159
+ - .rspec
160
+ - .travis.yml
161
+ - ChangeLog.md
162
+ - Gemfile
163
+ - README.md
164
+ - Rakefile
165
+ - bin/ey-dns
166
+ - engineyard-dns.gemspec
167
+ - features/assign_dns_to_environment.feature
168
+ - features/assign_dns_to_environment_via_different_dns_providers.feature
169
+ - features/step_definitions/common_steps.rb
170
+ - features/step_definitions/dnsimple_steps.rb
171
+ - features/step_definitions/ey_api_steps.rb
172
+ - features/step_definitions/fog_steps.rb
173
+ - features/support/common.rb
174
+ - features/support/engineyard.rb
175
+ - features/support/env.rb
176
+ - features/support/fog_helpers.rb
177
+ - features/support/matchers.rb
178
+ - lib/engineyard-dns.rb
179
+ - lib/engineyard-dns/cli.rb
180
+ - lib/engineyard-dns/version.rb
181
+ - posts/engineyard-2011-05-24.md
182
+ - spec/spec_helper.rb
183
+ has_rdoc: true
184
+ homepage: https://github.com/engineyard/engineyard-dns#readme
185
+ licenses: []
186
+
187
+ post_install_message:
188
+ rdoc_options: []
189
+
190
+ require_paths:
191
+ - lib
192
+ required_ruby_version: !ruby/object:Gem::Requirement
193
+ none: false
194
+ requirements:
195
+ - - ">="
196
+ - !ruby/object:Gem::Version
197
+ version: "0"
198
+ required_rubygems_version: !ruby/object:Gem::Requirement
199
+ none: false
200
+ requirements:
201
+ - - ">="
202
+ - !ruby/object:Gem::Version
203
+ version: "0"
204
+ requirements: []
205
+
206
+ rubyforge_project: engineyard-dns
207
+ rubygems_version: 1.6.2
208
+ signing_key:
209
+ specification_version: 3
210
+ summary: Configure your Engine Yard AppCloud environment and your DNSimple domain.
211
+ test_files:
212
+ - features/assign_dns_to_environment.feature
213
+ - features/assign_dns_to_environment_via_different_dns_providers.feature
214
+ - features/step_definitions/common_steps.rb
215
+ - features/step_definitions/dnsimple_steps.rb
216
+ - features/step_definitions/ey_api_steps.rb
217
+ - features/step_definitions/fog_steps.rb
218
+ - features/support/common.rb
219
+ - features/support/engineyard.rb
220
+ - features/support/env.rb
221
+ - features/support/fog_helpers.rb
222
+ - features/support/matchers.rb
223
+ - spec/spec_helper.rb