engineyard-jenkins 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.
Files changed (51) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +87 -0
  4. data/History.md +32 -0
  5. data/README.md +186 -0
  6. data/Rakefile +24 -0
  7. data/bin/ey-jenkins +7 -0
  8. data/engineyard-jenkins.gemspec +33 -0
  9. data/features/install.feature +53 -0
  10. data/features/install_server.feature +69 -0
  11. data/features/step_definitions/api_steps.rb +10 -0
  12. data/features/step_definitions/common_steps.rb +211 -0
  13. data/features/step_definitions/fixture_project_steps.rb +14 -0
  14. data/features/step_definitions/jenkins_steps.rb +9 -0
  15. data/features/support/common.rb +51 -0
  16. data/features/support/engineyard.rb +24 -0
  17. data/features/support/env.rb +14 -0
  18. data/features/support/matchers.rb +10 -0
  19. data/fixtures/cookbooks/main/recipes/default.rb +1 -0
  20. data/fixtures/cookbooks/redis/recipes/default.rb +0 -0
  21. data/fixtures/jenkins_boot_sequence/jenkins_booting.html +1 -0
  22. data/fixtures/jenkins_boot_sequence/jenkins_ready.html +1 -0
  23. data/fixtures/jenkins_boot_sequence/pre_jenkins_booting.html +1 -0
  24. data/fixtures/projects/rails/Gemfile +3 -0
  25. data/fixtures/projects/rails/Gemfile.lock +10 -0
  26. data/fixtures/projects/rails/Rakefile +4 -0
  27. data/lib/engineyard-jenkins.rb +4 -0
  28. data/lib/engineyard-jenkins/appcloud_env.rb +49 -0
  29. data/lib/engineyard-jenkins/cli.rb +134 -0
  30. data/lib/engineyard-jenkins/cli/install_generator.rb +55 -0
  31. data/lib/engineyard-jenkins/cli/install_generator/templates/attributes.rb.tt +17 -0
  32. data/lib/engineyard-jenkins/cli/install_generator/templates/cookbooks/main/attributes/recipe.rb +3 -0
  33. data/lib/engineyard-jenkins/cli/install_generator/templates/cookbooks/main/definitions/ey_cloud_report.rb +6 -0
  34. data/lib/engineyard-jenkins/cli/install_generator/templates/cookbooks/main/libraries/ruby_block.rb +40 -0
  35. data/lib/engineyard-jenkins/cli/install_generator/templates/cookbooks/main/libraries/run_for_app.rb +12 -0
  36. data/lib/engineyard-jenkins/cli/install_generator/templates/recipes.rb +95 -0
  37. data/lib/engineyard-jenkins/cli/install_server_generator.rb +25 -0
  38. data/lib/engineyard-jenkins/cli/install_server_generator/templates/attributes.rb.tt +3 -0
  39. data/lib/engineyard-jenkins/cli/install_server_generator/templates/cookbooks/jenkins_master/recipes/default.rb +95 -0
  40. data/lib/engineyard-jenkins/cli/install_server_generator/templates/cookbooks/jenkins_master/templates/default/init.sh.erb +26 -0
  41. data/lib/engineyard-jenkins/cli/install_server_generator/templates/cookbooks/jenkins_master/templates/default/proxy.conf.erb +20 -0
  42. data/lib/engineyard-jenkins/cli/install_server_generator/templates/cookbooks/main/attributes/recipe.rb +3 -0
  43. data/lib/engineyard-jenkins/cli/install_server_generator/templates/cookbooks/main/definitions/ey_cloud_report.rb +6 -0
  44. data/lib/engineyard-jenkins/cli/install_server_generator/templates/cookbooks/main/libraries/ruby_block.rb +40 -0
  45. data/lib/engineyard-jenkins/cli/install_server_generator/templates/cookbooks/main/libraries/run_for_app.rb +12 -0
  46. data/lib/engineyard-jenkins/cli/install_server_generator/templates/cookbooks/main/recipes/default.rb +1 -0
  47. data/lib/engineyard-jenkins/thor-ext/actions/directory.rb +33 -0
  48. data/lib/engineyard-jenkins/version.rb +5 -0
  49. data/spec/appcloud_env_spec.rb +75 -0
  50. data/spec/spec_helper.rb +4 -0
  51. metadata +254 -0
@@ -0,0 +1,10 @@
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"$/ do
9
+ api_scenario "two accounts, two apps, two environments, ambiguous"
10
+ end
@@ -0,0 +1,211 @@
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( project path|) "(.*)"/ do |env_var, path, value|
6
+ in_project_folder {
7
+ value = File.expand_path(value)
8
+ } unless path.empty?
9
+ ENV[env_var] = value
10
+ end
11
+
12
+ Given /"(.*)" folder is deleted/ do |folder|
13
+ in_project_folder { FileUtils.rm_rf folder }
14
+ end
15
+
16
+ When /^I invoke "(.*)" generator with arguments "(.*)"$/ do |generator, arguments|
17
+ @stdout = StringIO.new
18
+ in_project_folder do
19
+ if Object.const_defined?("APP_ROOT")
20
+ APP_ROOT.replace(FileUtils.pwd)
21
+ else
22
+ APP_ROOT = FileUtils.pwd
23
+ end
24
+ run_generator(generator, arguments.split(' '), SOURCES, :stdout => @stdout)
25
+ end
26
+ File.open(File.join(@tmp_root, "generator.out"), "w") do |f|
27
+ @stdout.rewind
28
+ f << @stdout.read
29
+ end
30
+ end
31
+
32
+ When /^I run executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
33
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
34
+ in_project_folder do
35
+ system "#{executable.inspect} #{arguments} > #{@stdout.inspect} 2> #{@stdout.inspect}"
36
+ end
37
+ end
38
+
39
+ When /^I run project executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
40
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
41
+ in_project_folder do
42
+ system "ruby -rubygems #{executable.inspect} #{arguments} > #{@stdout.inspect} 2> #{@stdout.inspect}"
43
+ end
44
+ end
45
+
46
+ When /^I run local executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
47
+ if executable == "ey-jenkins"
48
+ require 'engineyard-jenkins'
49
+ require 'engineyard-jenkins/cli'
50
+ in_project_folder do
51
+ stdout, stderr = capture_stdios do
52
+ Engineyard::Jenkins::CLI.start(arguments.split(/ /))
53
+ end
54
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
55
+ File.open(@stdout, "w") {|f| f << stdout; f << stderr}
56
+ end
57
+ else
58
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
59
+ executable = File.expand_path(File.join(File.dirname(__FILE__), "/../../bin", executable))
60
+ in_project_folder do
61
+ system "ruby -rubygems #{executable.inspect} #{arguments} > #{@stdout.inspect} 2> #{@stdout.inspect}"
62
+ end
63
+ end
64
+ end
65
+
66
+ When /^I invoke task "rake (.*)"/ do |task|
67
+ @stdout = File.expand_path(File.join(@tmp_root, "tests.out"))
68
+ in_project_folder do
69
+ system "bundle exec rake #{task} --trace > #{@stdout.inspect} 2> #{@stdout.inspect}"
70
+ end
71
+ end
72
+
73
+ Then /^folder "(.*)" (is|is not) created/ do |folder, is|
74
+ in_project_folder do
75
+ File.exists?(folder).should(is == 'is' ? be_true : be_false)
76
+ end
77
+ end
78
+
79
+ Then /^file "(.*)" (is|is not) created/ do |file, is|
80
+ in_project_folder do
81
+ File.exists?(file).should(is == 'is' ? be_true : be_false)
82
+ end
83
+ end
84
+
85
+ Then /^file with name matching "(.*)" is created/ do |pattern|
86
+ in_project_folder do
87
+ Dir[pattern].should_not be_empty
88
+ end
89
+ end
90
+
91
+ Then /^file "(.*)" contents (does|does not) match \/(.*)\// do |file, does, regex|
92
+ in_project_folder do
93
+ actual_output = File.read(file)
94
+ (does == 'does') ?
95
+ actual_output.should(match(/#{regex}/)) :
96
+ actual_output.should_not(match(/#{regex}/))
97
+ end
98
+ end
99
+
100
+ Then /^file "([^"]*)" contains "([^"]*)"$/ do |file, text|
101
+ in_project_folder do
102
+ actual_output = File.read(file)
103
+ actual_output.should contain(text)
104
+ end
105
+ end
106
+
107
+
108
+ Then /gem file "(.*)" and generated file "(.*)" should be the same/ do |gem_file, project_file|
109
+ File.exists?(gem_file).should be_true
110
+ File.exists?(project_file).should be_true
111
+ gem_file_contents = File.read(File.dirname(__FILE__) + "/../../#{gem_file}")
112
+ project_file_contents = File.read(File.join(@active_project_folder, project_file))
113
+ project_file_contents.should == gem_file_contents
114
+ end
115
+
116
+ Then /^(does|does not) invoke generator "(.*)"$/ do |does_invoke, generator|
117
+ actual_output = get_command_output
118
+ does_invoke == "does" ?
119
+ actual_output.should(match(/dependency\s+#{generator}/)) :
120
+ actual_output.should_not(match(/dependency\s+#{generator}/))
121
+ end
122
+
123
+ Then /help options "(.*)" and "(.*)" are displayed/ do |opt1, opt2|
124
+ actual_output = get_command_output
125
+ actual_output.should match(/#{opt1}/)
126
+ actual_output.should match(/#{opt2}/)
127
+ end
128
+
129
+ Then /^I should see "([^\"]*)"$/ do |text|
130
+ actual_output = get_command_output
131
+ actual_output.should contain(text)
132
+ end
133
+
134
+ Then /^I should not see "([^\"]*)"$/ do |text|
135
+ actual_output =
136
+ actual_output.should_not contain(text)
137
+ end
138
+
139
+ Then /^I should see$/ do |text|
140
+ actual_output = get_command_output
141
+ actual_output.should contain(text)
142
+ end
143
+
144
+ Then /^I should not see$/ do |text|
145
+ actual_output = get_command_output
146
+ actual_output.should_not contain(text)
147
+ end
148
+
149
+ Then /^I should see exactly$/ do |text|
150
+ actual_output = get_command_output
151
+ actual_output.should == text
152
+ end
153
+
154
+ Then /^I should see all (\d+) tests pass/ do |expected_test_count|
155
+ expected = %r{^#{expected_test_count} tests, \d+ assertions, 0 failures, 0 errors}
156
+ actual_output = get_command_output
157
+ actual_output.should match(expected)
158
+ end
159
+
160
+ Then /^I should see all (\d+) examples pass/ do |expected_test_count|
161
+ expected = %r{^#{expected_test_count} examples?, 0 failures}
162
+ actual_output = get_command_output
163
+ actual_output.should match(expected)
164
+ end
165
+
166
+ Then /^yaml file "(.*)" contains (\{.*\})/ do |file, yaml|
167
+ in_project_folder do
168
+ yaml = eval yaml
169
+ YAML.load(File.read(file)).should == yaml
170
+ end
171
+ end
172
+
173
+ Then /^Rakefile can display tasks successfully/ do
174
+ @stdout = File.expand_path(File.join(@tmp_root, "rakefile.out"))
175
+ in_project_folder do
176
+ system "rake -T > #{@stdout.inspect} 2> #{@stdout.inspect}"
177
+ end
178
+ actual_output = get_command_output
179
+ actual_output.should match(/^rake\s+\w+\s+#\s.*/)
180
+ end
181
+
182
+ Then /^task "rake (.*)" is executed successfully/ do |task|
183
+ @stdout.should_not be_nil
184
+ actual_output = get_command_output
185
+ actual_output.should_not match(/^Don't know how to build task '#{task}'/)
186
+ actual_output.should_not match(/Error/i)
187
+ end
188
+
189
+ Then /^gem spec key "(.*)" contains \/(.*)\// do |key, regex|
190
+ in_project_folder do
191
+ gem_file = Dir["pkg/*.gem"].first
192
+ gem_spec = Gem::Specification.from_yaml(`gem spec #{gem_file}`)
193
+ spec_value = gem_spec.send(key.to_sym)
194
+ spec_value.to_s.should match(/#{regex}/)
195
+ end
196
+ end
197
+
198
+ Then /^the file "([^\"]*)" is a valid gemspec$/ do |filename|
199
+ spec = eval(File.read(filename))
200
+ spec.validate
201
+ end
202
+
203
+ When /^I create a new node with the following options on "http:\/\/(.+?):(\d+)":$/ do |host, port, table|
204
+ options = table.raw.inject({}) do |options, (key, value)|
205
+ options[(key.to_sym rescue key) || key] = value
206
+ options
207
+ end
208
+
209
+ Jenkins::Api.setup_base_url(:host => host, :port => port.to_i)
210
+ Jenkins::Api.add_node(options)
211
+ end
@@ -0,0 +1,14 @@
1
+ Given /^I am in the "([^\"]*)" project folder$/ do |project|
2
+ project_folder = File.expand_path(File.join(@fixtures_path, "projects", project))
3
+ in_tmp_folder do
4
+ FileUtils.cp_r(project_folder, project)
5
+ setup_active_project_folder(project)
6
+ end
7
+ end
8
+
9
+ Given /^I already have cookbooks installed$/ do
10
+ cookbooks_folder = File.expand_path(File.join(@fixtures_path, "cookbooks"))
11
+ in_project_folder do
12
+ FileUtils.cp_r(cookbooks_folder, ".")
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ Given /^I want to fake out the boot sequence of Jenkins$/ do
2
+ base_path = File.join(File.dirname(__FILE__) + "/../../fixtures/jenkins_boot_sequence/")
3
+ FakeWeb.register_uri(:get, "http://app-master-hostname.compute-1.amazonaws.com/", [
4
+ {:body => File.read(base_path + "pre_jenkins_booting.html")},
5
+ {:body => File.read(base_path + "jenkins_booting.html")},
6
+ {:body => File.read(base_path + "jenkins_ready.html")}
7
+ ])
8
+ end
9
+
@@ -0,0 +1,51 @@
1
+ module CommonHelpers
2
+ def get_command_output
3
+ strip_color_codes(File.read(@stdout)).chomp
4
+ end
5
+
6
+ def strip_color_codes(text)
7
+ text.gsub(/\e\[\d+m/, '')
8
+ end
9
+
10
+ def in_tmp_folder(&block)
11
+ FileUtils.chdir(@tmp_root, &block)
12
+ end
13
+
14
+ def in_project_folder(&block)
15
+ project_folder = @active_project_folder || @tmp_root
16
+ FileUtils.chdir(project_folder, &block)
17
+ end
18
+
19
+ def in_home_folder(&block)
20
+ FileUtils.chdir(@home_path, &block)
21
+ end
22
+
23
+ def force_local_lib_override(project_name = @project_name)
24
+ rakefile = File.read(File.join(project_name, 'Rakefile'))
25
+ File.open(File.join(project_name, 'Rakefile'), "w+") do |f|
26
+ f << "$:.unshift('#{@lib_path}')\n"
27
+ f << rakefile
28
+ end
29
+ end
30
+
31
+ def setup_active_project_folder project_name
32
+ @active_project_folder = File.join(@tmp_root, project_name)
33
+ @project_name = project_name
34
+ end
35
+
36
+ # capture both [stdout, stderr] as well as stdin
37
+ def capture_stdios(input = nil, &block)
38
+ require 'stringio'
39
+ org_stdin, $stdin = $stdin, StringIO.new(input) if input
40
+ org_stdout, $stdout = $stdout, StringIO.new
41
+ org_stderr, $stderr = $stdout, StringIO.new
42
+ yield
43
+ return [$stdout.string, $stderr.string]
44
+ ensure
45
+ $stderr = org_stderr
46
+ $stdout = org_stdout
47
+ $stdin = org_stdin
48
+ end
49
+ end
50
+
51
+ 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,14 @@
1
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../../lib'))
2
+ require 'bundler/setup'
3
+ require 'engineyard-jenkins'
4
+
5
+ Before do
6
+ @tmp_root = File.dirname(__FILE__) + "/../../tmp"
7
+ @active_project_folder = @tmp_root
8
+ @home_path = File.expand_path(File.join(@tmp_root, "home"))
9
+ @lib_path = File.expand_path(File.dirname(__FILE__) + "/../../lib")
10
+ @fixtures_path = File.expand_path(File.dirname(__FILE__) + "/../../fixtures")
11
+ FileUtils.rm_rf @tmp_root
12
+ FileUtils.mkdir_p @home_path
13
+ ENV['HOME'] = @home_path
14
+ end
@@ -0,0 +1,10 @@
1
+
2
+ module Matchers
3
+ RSpec::Matchers.define :contain do |expected_text|
4
+ match do |text|
5
+ text.index expected_text
6
+ end
7
+ end
8
+ end
9
+
10
+ World(Matchers)
@@ -0,0 +1 @@
1
+ require_recipe 'redis'
File without changes
@@ -0,0 +1 @@
1
+ Please wait while Jenkins is getting ready to work
@@ -0,0 +1 @@
1
+ Jenkins is up!
@@ -0,0 +1 @@
1
+ This is some page before the instance is launched.
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.rb"
2
+
3
+ gem "rake"
@@ -0,0 +1,10 @@
1
+ GEM
2
+ remote: http://rubygems.rb/
3
+ specs:
4
+ rake (0.8.7)
5
+
6
+ PLATFORMS
7
+ ruby
8
+
9
+ DEPENDENCIES
10
+ rake
@@ -0,0 +1,4 @@
1
+ desc "Default task runs tests"
2
+ task :default do
3
+ puts "Tests ran successfully!"
4
+ end
@@ -0,0 +1,4 @@
1
+ module Engineyard
2
+ module Jenkins
3
+ end
4
+ end
@@ -0,0 +1,49 @@
1
+ require "engineyard"
2
+ require "engineyard/thor"
3
+ require "engineyard/cli"
4
+ require "engineyard/cli/ui"
5
+ require "engineyard/error"
6
+ module Engineyard
7
+ module Jenkins
8
+ class AppcloudEnv
9
+ include EY::UtilityMethods
10
+
11
+ # Returns [environment, account] based on current .eyrc credentials and/or CLI options
12
+ # Returns [nil, nil] if no unique environment can be selected
13
+ def find_environments(options = {})
14
+ Thor::Base.shell = EY::CLI::UI
15
+ EY.ui = EY::CLI::UI.new
16
+ query_environments = options[:environment] ? [options[:environment]] : default_query_environments
17
+ query_environments.inject([]) do |envs, env_name|
18
+ begin
19
+ if environment = fetch_environment(env_name, options[:account])
20
+ clean_host_name(environment)
21
+ envs << [env_name, environment.account.name, environment]
22
+ end
23
+ rescue EY::NoEnvironmentError
24
+ rescue EY::MultipleMatchesError => e
25
+ # e.message looks something like:
26
+ # Multiple environments possible, please be more specific:
27
+ #
28
+ # jenkins # ey <command> --environment='jenkins' --account='drnic-demo'
29
+ # jenkins # ey <command> --environment='jenkins' --account='rails-jenkins'
30
+ e.message.scan(/--environment='([^']+)' --account='([^']+)'/) do
31
+ envs << [$1, $2, nil]
32
+ end
33
+ end
34
+ envs
35
+ end
36
+ end
37
+
38
+ def default_query_environments
39
+ %w[jenkins jenkins_server jenkins_production jenkins_server_production]
40
+ end
41
+
42
+ # Currently the engineyard gem has badly formed URLs in its same data
43
+ # This method cleans app_master_hostname.compute-1.amazonaws.com -> app-master-hostname.compute-1.amazonaws.com
44
+ def clean_host_name(environment)
45
+ environment.instances.first.public_hostname.gsub!(/_/,'-') if environment.instances.first
46
+ end
47
+ end
48
+ end
49
+ end