lobot 1.0.pre → 2.0.0pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +3 -0
  2. data/Gemfile +0 -1
  3. data/Guardfile +10 -0
  4. data/README.md +52 -72
  5. data/Rakefile +2 -6
  6. data/Vagrantfile +15 -0
  7. data/bin/lobot +7 -0
  8. data/chef/cookbooks/pivotal_ci/.gitignore +4 -0
  9. data/chef/cookbooks/pivotal_ci/Gemfile +3 -0
  10. data/chef/cookbooks/pivotal_ci/README.md +3 -0
  11. data/chef/cookbooks/pivotal_ci/attributes/git.rb +1 -1
  12. data/chef/cookbooks/pivotal_ci/attributes/jenkins.rb +2 -3
  13. data/chef/cookbooks/pivotal_ci/attributes/nginx.rb +1 -2
  14. data/chef/cookbooks/pivotal_ci/attributes/ssl.rb +1 -1
  15. data/chef/cookbooks/pivotal_ci/files/default/tests/minitest/default_test.rb +37 -0
  16. data/chef/cookbooks/pivotal_ci/metadata.rb +10 -0
  17. data/chef/cookbooks/pivotal_ci/recipes/id_rsa.rb +5 -2
  18. data/chef/cookbooks/pivotal_ci/recipes/jenkins.rb +1 -1
  19. data/chef/cookbooks/pivotal_ci/recipes/jenkins_config.rb +10 -17
  20. data/chef/cookbooks/pivotal_ci/recipes/nginx.rb +2 -0
  21. data/chef/cookbooks/pivotal_ci/templates/default/jenkins-job-config.xml.erb +11 -3
  22. data/chef/cookbooks/pivotal_ci/templates/default/nginx-htaccess.erb +3 -3
  23. data/chef/cookbooks/pivotal_ci/templates/default/org.jenkinsci.plugins.xvfb.XvfbBuildWrapper.xml.erb +11 -0
  24. data/chef/cookbooks/pivotal_ci/test/kitchen/Kitchenfile +4 -0
  25. data/chef/cookbooks/pivotal_ci/test/kitchen/cookbooks/pivotal_ci_test/attributes/default.rb +3 -0
  26. data/chef/cookbooks/pivotal_ci/test/kitchen/cookbooks/pivotal_ci_test/metadata.rb +10 -0
  27. data/chef/cookbooks/pivotal_ci/test/kitchen/cookbooks/pivotal_ci_test/recipes/default.rb +41 -0
  28. data/lib/generators/lobot/templates/soloistrc +2 -1
  29. data/lib/lobot.rb +1 -12
  30. data/lib/lobot/amazon.rb +91 -0
  31. data/lib/lobot/cli.rb +148 -0
  32. data/lib/lobot/config.rb +80 -0
  33. data/lib/lobot/jenkins.rb +17 -0
  34. data/lib/lobot/sobo.rb +35 -0
  35. data/lib/lobot/tasks/ci.rake +0 -178
  36. data/lib/lobot/version.rb +1 -1
  37. data/lobot.gemspec +19 -15
  38. data/script/bootstrap_server.sh +4 -1
  39. data/spec/lib/lobot/amazon_spec.rb +127 -0
  40. data/spec/lib/lobot/cli_spec.rb +212 -0
  41. data/spec/lib/lobot/config_spec.rb +95 -0
  42. data/spec/lib/lobot/jenkins_spec.rb +14 -0
  43. data/spec/spec_helper.rb +5 -10
  44. metadata +87 -31
  45. data/chef/cookbooks/pivotal_ci/libraries/ci_config.rb +0 -2
  46. data/spec/install_spec.rb +0 -80
@@ -1,3 +1,3 @@
1
- <% node["nginx_settings"]["basic_auth_users"].each do |user| %>
2
- <%= "#{user["username"]}:#{user["password"].crypt("AA")}" %>
3
- <% end %>
1
+ <% if node['nginx']['basic_auth_user'] && node['nginx']['basic_auth_password'] %>
2
+ <%= "#{node['nginx']['basic_auth_user']}:#{node['nginx']['basic_auth_password'].crypt("AA")}" %>
3
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <?xml version='1.0' encoding='UTF-8'?>
2
+ <org.jenkinsci.plugins.xvfb.XvfbBuildWrapper_-XvfbBuildWrapperDescriptor plugin="xvfb@1.0.4">
3
+ <helpRedirect/>
4
+ <installations>
5
+ <org.jenkinsci.plugins.xvfb.XvfbInstallation>
6
+ <name>system</name>
7
+ <home>/usr/bin</home>
8
+ <properties/>
9
+ </org.jenkinsci.plugins.xvfb.XvfbInstallation>
10
+ </installations>
11
+ </org.jenkinsci.plugins.xvfb.XvfbBuildWrapper_-XvfbBuildWrapperDescriptor>
@@ -0,0 +1,4 @@
1
+ cookbook "pivotal_ci" do
2
+ configuration "default"
3
+ lint(:ignore => ["FC007", "FC009"])
4
+ end
@@ -0,0 +1,3 @@
1
+ node['nginx']['basic_auth_user'] = "wheelbarrow"
2
+ node['nginx']['basic_auth_password'] = "full-of-garbage"
3
+ node['jenkins']['builds'] = []
@@ -0,0 +1,10 @@
1
+ name "pivotal_ci_test"
2
+ maintainer "Pivotal Labs"
3
+ maintainer_email "commoncode+lobot@pivotallabs.com"
4
+ license "MIT"
5
+ description "Sets up Lobot"
6
+ version "0.1.0"
7
+
8
+ recipe "pivotal_ci_test::default", "Bootstrap Lobot"
9
+
10
+ supports "ubuntu", "12.04"
@@ -0,0 +1,41 @@
1
+ bash "bootstrap" do
2
+ cwd "/home/vagrant"
3
+ code <<-BOOTSTRAP
4
+ #!/bin/bash
5
+
6
+ set -e
7
+
8
+ sudo apt-get update > /dev/null
9
+
10
+ packages="git build-essential openssl libreadline6 libreadline6-dev libreadline5 curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev autoconf libc6-dev libncurses5-dev automake libtool bison subversion pkg-config"
11
+ selections=`dpkg --get-selections`
12
+
13
+ for package in $packages
14
+ do
15
+ if ! echo "$selections" | grep "^$package\s" > /dev/null
16
+ then
17
+ to_install="$to_install $package"
18
+ fi
19
+ done
20
+
21
+ if [ ! -z "$to_install" ]
22
+ then
23
+ sudo apt-get install -y $to_install
24
+ fi
25
+
26
+ test -d /usr/local/rvm || curl -L https://get.rvm.io | sudo bash -s stable
27
+
28
+ sudo tee /etc/profile.d/rvm.sh > /dev/null <<RVMSH_CONTENT
29
+ [[ -s "/usr/local/rvm/scripts/rvm" ]] && source "/usr/local/rvm/scripts/rvm"
30
+ RVMSH_CONTENT
31
+
32
+ sudo tee /etc/rvmrc > /dev/null <<RVMRC_CONTENTS
33
+ rvm_install_on_use_flag=1
34
+ rvm_trust_rvmrcs_flag=1
35
+ rvm_gemset_create_on_use_flag=1
36
+ RVMRC_CONTENTS
37
+
38
+ sudo usermod vagrant -a -G rvm
39
+
40
+ BOOTSTRAP
41
+ end
@@ -18,5 +18,6 @@ env_variable_switches:
18
18
  node_attributes:
19
19
  travis_build_environment:
20
20
  user: jenkins
21
- group: jenkins
21
+ group: nogroup
22
22
  home: /var/lib/jenkins
23
+
@@ -1,15 +1,4 @@
1
- require File.expand_path('lobot/version', File.dirname(__FILE__))
2
- require 'headless'
1
+ require File.expand_path("../lobot/version", __FILE__)
3
2
 
4
3
  module Lobot
5
- def self.rails3?
6
- return Rails.version.split(".").first.to_i == 3 if defined? Rails
7
- begin
8
- Gem::Specification::find_by_name "rails", ">= 3.0"
9
- rescue
10
- Gem.available? "rails", ">= 3.0"
11
- end
12
- end
13
4
  end
14
-
15
- require File.expand_path(File.join('lobot', "railtie"), File.dirname(__FILE__)) if Lobot.rails3?
@@ -0,0 +1,91 @@
1
+ require "fog"
2
+
3
+ module Lobot
4
+ class Amazon
5
+ PORTS_TO_OPEN = [22, 443] + (9000...9010).to_a
6
+
7
+ attr_reader :key, :secret
8
+
9
+ def initialize(key, secret)
10
+ @key = key
11
+ @secret = secret
12
+ end
13
+
14
+ def security_groups
15
+ fog.security_groups
16
+ end
17
+
18
+ def key_pairs
19
+ fog.key_pairs
20
+ end
21
+
22
+ def servers
23
+ fog.servers
24
+ end
25
+
26
+ def elastic_ip_address
27
+ @elastic_ip_address ||= fog.addresses.create
28
+ end
29
+
30
+ def create_security_group(group_name)
31
+ unless security_groups.get(group_name)
32
+ security_groups.create(:name => group_name, :description => 'Lobot-generated group')
33
+ end
34
+ end
35
+
36
+ def open_port(group_name, *ports)
37
+ group = security_groups.get(group_name)
38
+ ports.each do |port|
39
+ unless group.ip_permissions.any? { |p| (p["fromPort"]..p["toPort"]).include?(port) }
40
+ group.authorize_port_range(port..port)
41
+ end
42
+ end
43
+ end
44
+
45
+ def delete_key_pair(key_pair_name)
46
+ key_pairs.new(:name => key_pair_name).destroy
47
+ end
48
+
49
+ def add_key_pair(key_pair_name, key_path)
50
+ unless key_pairs.get(key_pair_name)
51
+ key_pairs.create(:name => key_pair_name, :public_key => File.read("#{key_path}"))
52
+ end
53
+ end
54
+
55
+ def launch_server(key_pair_name, security_group_name, instance_type = "m1.medium")
56
+ servers.create(
57
+ :image_id => "ami-a29943cb",
58
+ :flavor_id => instance_type,
59
+ :availability_zone => "us-east-1a",
60
+ :tags => {"Name" => "Lobot", "lobot" => Lobot::VERSION},
61
+ :key_name => key_pair_name,
62
+ :groups => [security_group_name]
63
+ ).tap do |server|
64
+ server.wait_for { ready? }
65
+ fog.associate_address(server.id, elastic_ip_address.public_ip) # needs to be running
66
+ server.reload
67
+ end
68
+ end
69
+
70
+ def destroy_ec2
71
+ servers = fog.servers.select { |s| s.tags.keys.include?("lobot") && s.state == "running" }
72
+ ips = servers.map(&:public_ip_address)
73
+ servers.map(&:destroy)
74
+ ips.each { |ip| release_elastic_ip(ip) }
75
+ end
76
+
77
+ def release_elastic_ip(ip)
78
+ fog.addresses.get(ip).destroy if fog.addresses.get(ip)
79
+ end
80
+
81
+ private
82
+
83
+ def fog
84
+ @fog ||= Fog::Compute.new(
85
+ :provider => "aws",
86
+ :aws_access_key_id => key,
87
+ :aws_secret_access_key => secret
88
+ )
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,148 @@
1
+ require "thor"
2
+ require "lobot/config"
3
+ require "lobot/sobo"
4
+ require "lobot/amazon"
5
+ require "lobot/jenkins"
6
+ require "pp"
7
+ require "tempfile"
8
+ require "json"
9
+
10
+ module Lobot
11
+ class CLI < Thor
12
+ desc "ssh", "SSH into Lobot"
13
+ def ssh
14
+ exec("ssh -i #{lobot_config.server_ssh_key} ubuntu@#{lobot_config.master} -p #{lobot_config.ssh_port}")
15
+ end
16
+
17
+ desc "open", "Open a browser to Lobot"
18
+ def open
19
+ exec("open https://#{lobot_config.node_attributes.nginx.basic_auth_user}:#{lobot_config.node_attributes.nginx.basic_auth_password}@#{lobot_config.master}/")
20
+ end
21
+
22
+ desc "create", "Create a new Lobot server using EC2"
23
+ def create
24
+ ssh_key_path = File.expand_path("#{lobot_config.server_ssh_key}.pub")
25
+
26
+ amazon.add_key_pair("lobot", ssh_key_path)
27
+ amazon.create_security_group("lobot")
28
+ amazon.open_port("lobot", 22, 443)
29
+ server = amazon.launch_server("lobot", "lobot", lobot_config.instance_size)
30
+
31
+ puts "Writing ip address for ec2: #{server.public_ip_address}"
32
+ lobot_config.master = server.public_ip_address
33
+ lobot_config.instance_id = server.id
34
+ lobot_config.save
35
+
36
+ update_known_hosts
37
+ end
38
+
39
+ desc "destroy_ec2", "Destroys all the lobot resources that we can find on ec2. Be Careful!"
40
+ def destroy_ec2
41
+ amazon.destroy_ec2
42
+ lobot_config.master = nil
43
+ end
44
+
45
+ desc "create_vagrant", "Lowers the price of heroin to reasonable levels"
46
+ def create_vagrant
47
+ spawn_env = {"LOBOT_SSH_KEY" => File.expand_path("#{lobot_config.server_ssh_key}.pub"),
48
+ "VAGRANT_HOME" => File.expand_path("~")}
49
+ spawn_options = {chdir: lobot_root_path}
50
+
51
+ pid = Process.spawn(spawn_env, "vagrant up", spawn_options)
52
+ Process.wait(pid)
53
+
54
+ vagrant_ip = "192.168.33.10"
55
+
56
+ puts "Writing ip address for vagrant: #{vagrant_ip}"
57
+ lobot_config.master = vagrant_ip
58
+ lobot_config.save
59
+
60
+ update_known_hosts
61
+ end
62
+
63
+ desc "config", "Dumps all configuration data for Lobot"
64
+ def config
65
+ pp lobot_config.to_hash
66
+ end
67
+
68
+ desc "bootstrap", "Configures Lobot's master node"
69
+ def bootstrap
70
+ sync_bootstrap_script
71
+ master_server.exec("bash -l script/bootstrap_server.sh")
72
+ rescue Errno::ECONNRESET
73
+ sleep 1
74
+ end
75
+
76
+ desc "chef", "Uploads chef recipes and runs them"
77
+ def chef
78
+ sync_chef_recipes
79
+ upload_soloist
80
+ sync_github_ssh_key
81
+ master_server.exec("bash -l -c 'rvm use 1.9.3; gem list | grep soloist || gem install --no-ri --no-rdoc soloist; soloist'")
82
+ rescue Errno::ECONNRESET
83
+ sleep 1
84
+ end
85
+
86
+ desc "add_build(name, repository, branch, command)", "Adds a build to Lobot"
87
+ def add_build(name, repository, branch, command)
88
+ build = {
89
+ "name" => name,
90
+ "repository" => repository,
91
+ "branch" => branch,
92
+ "command" => command
93
+ }
94
+ lobot_config.node_attributes = lobot_config.node_attributes.tap do |config|
95
+ config.jenkins.builds << build unless config.jenkins.builds.include?(build)
96
+ end
97
+ lobot_config.save
98
+ end
99
+
100
+ no_tasks do
101
+ def master_server
102
+ @master_server ||= Lobot::Sobo.new(lobot_config.master, lobot_config.server_ssh_key)
103
+ end
104
+
105
+ def lobot_config
106
+ @lobot_config ||= Lobot::Config.from_file(lobot_config_path)
107
+ end
108
+
109
+ def amazon
110
+ @amazon ||= Lobot::Amazon.new(lobot_config.aws_key, lobot_config.aws_secret)
111
+ end
112
+
113
+ def sync_bootstrap_script
114
+ master_server.upload(File.join(lobot_root_path, "script/"), "script/")
115
+ end
116
+
117
+ def sync_github_ssh_key
118
+ master_server.upload(lobot_config.github_ssh_key, "~/.ssh/id_rsa")
119
+ end
120
+
121
+ def sync_chef_recipes
122
+ master_server.upload(File.join(lobot_root_path, "chef/"), "chef/")
123
+ end
124
+
125
+ def upload_soloist
126
+ Tempfile.open("lobot-soloistrc") do |file|
127
+ file.write(YAML.dump(JSON.parse(JSON.dump(lobot_config.soloistrc))))
128
+ file.close
129
+ master_server.upload(file.path, "soloistrc")
130
+ end
131
+ end
132
+ end
133
+
134
+ private
135
+ def lobot_root_path
136
+ File.expand_path('../../..', __FILE__)
137
+ end
138
+
139
+ def lobot_config_path
140
+ File.expand_path("config/lobot.yml", Dir.pwd)
141
+ end
142
+
143
+ def update_known_hosts
144
+ raise "failed to remove old host key from known_hosts" unless system "ssh-keygen -R #{lobot_config.master} 2> /dev/null"
145
+ raise "failed to add host key to known_hosts" unless system "ssh-keyscan #{lobot_config.master} 2> /dev/null >> ~/.ssh/known_hosts"
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,80 @@
1
+ require "hashie"
2
+
3
+ module Lobot
4
+ class Config < Hashie::Dash
5
+ property :path
6
+ property :aws_key
7
+ property :aws_secret
8
+ property :instance_id
9
+ property :master
10
+
11
+ property :instance_size, :default => 'c1.medium'
12
+ property :ssh_port, :default => 22
13
+ property :server_ssh_key, :default => File.expand_path("~/.ssh/id_rsa")
14
+ property :github_ssh_key, :default => File.expand_path("~/.ssh/id_rsa")
15
+ property :recipes, :default => ["pivotal_ci::jenkins", "pivotal_ci::limited_travis_ci_environment", "pivotal_ci"]
16
+ property :cookbook_paths, :default => ['./chef/cookbooks/', './chef/travis-cookbooks/ci_environment']
17
+ property :node_attributes, :default => {
18
+ :travis_build_environment => {
19
+ :user => "jenkins",
20
+ :group => "nogroup",
21
+ :home => "/var/lib/jenkins"
22
+ },
23
+ :nginx => {
24
+ :basic_auth_user => "ci",
25
+ },
26
+ :jenkins => {
27
+ :builds => []
28
+ }
29
+ }
30
+
31
+ def initialize(attributes = {})
32
+ super
33
+ self["node_attributes"] = Hashie::Mash.new(node_attributes)
34
+ end
35
+
36
+ def node_attributes=(attributes)
37
+ self["node_attributes"] = Hashie::Mash.new(attributes)
38
+ end
39
+
40
+ def soloistrc
41
+ {
42
+ "recipes" => recipes,
43
+ "cookbook_paths" => cookbook_paths,
44
+ "node_attributes" => node_attributes.to_hash
45
+ }
46
+ end
47
+
48
+ def save
49
+ return self unless path
50
+ File.open(path, "w+") { |file| file.write(YAML.dump(JSON.parse(JSON.dump(to_hash)))) }
51
+ self
52
+ end
53
+
54
+ def reload
55
+ self.class.from_file(path)
56
+ end
57
+
58
+ def to_hash
59
+ hash = super
60
+ hash.delete("path")
61
+ {
62
+ "ssh_port" => ssh_port,
63
+ "master" => master,
64
+ "server_ssh_key" => server_ssh_key,
65
+ "recipes" => recipes,
66
+ "cookbook_paths" => cookbook_paths,
67
+ "node_attributes" => node_attributes
68
+ }.merge(hash)
69
+ end
70
+
71
+ def self.from_file(yaml_file)
72
+ config = read_config(yaml_file)
73
+ self.new(config.merge(:path => yaml_file))
74
+ end
75
+
76
+ def self.read_config(yaml_file)
77
+ File.open(yaml_file, "r") { |file| YAML.load(file.read) }
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,17 @@
1
+ module Lobot
2
+ class Jenkins
3
+ attr_reader :config
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ def jobs
10
+ Hashie::Mash.new(api_json).jobs
11
+ end
12
+
13
+ def api_json
14
+ JSON.parse(`curl -s http://#{config.master}:8080/api/json`)
15
+ end
16
+ end
17
+ end