lobot 1.0.pre → 2.0.0pre
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile +0 -1
- data/Guardfile +10 -0
- data/README.md +52 -72
- data/Rakefile +2 -6
- data/Vagrantfile +15 -0
- data/bin/lobot +7 -0
- data/chef/cookbooks/pivotal_ci/.gitignore +4 -0
- data/chef/cookbooks/pivotal_ci/Gemfile +3 -0
- data/chef/cookbooks/pivotal_ci/README.md +3 -0
- data/chef/cookbooks/pivotal_ci/attributes/git.rb +1 -1
- data/chef/cookbooks/pivotal_ci/attributes/jenkins.rb +2 -3
- data/chef/cookbooks/pivotal_ci/attributes/nginx.rb +1 -2
- data/chef/cookbooks/pivotal_ci/attributes/ssl.rb +1 -1
- data/chef/cookbooks/pivotal_ci/files/default/tests/minitest/default_test.rb +37 -0
- data/chef/cookbooks/pivotal_ci/metadata.rb +10 -0
- data/chef/cookbooks/pivotal_ci/recipes/id_rsa.rb +5 -2
- data/chef/cookbooks/pivotal_ci/recipes/jenkins.rb +1 -1
- data/chef/cookbooks/pivotal_ci/recipes/jenkins_config.rb +10 -17
- data/chef/cookbooks/pivotal_ci/recipes/nginx.rb +2 -0
- data/chef/cookbooks/pivotal_ci/templates/default/jenkins-job-config.xml.erb +11 -3
- data/chef/cookbooks/pivotal_ci/templates/default/nginx-htaccess.erb +3 -3
- data/chef/cookbooks/pivotal_ci/templates/default/org.jenkinsci.plugins.xvfb.XvfbBuildWrapper.xml.erb +11 -0
- data/chef/cookbooks/pivotal_ci/test/kitchen/Kitchenfile +4 -0
- data/chef/cookbooks/pivotal_ci/test/kitchen/cookbooks/pivotal_ci_test/attributes/default.rb +3 -0
- data/chef/cookbooks/pivotal_ci/test/kitchen/cookbooks/pivotal_ci_test/metadata.rb +10 -0
- data/chef/cookbooks/pivotal_ci/test/kitchen/cookbooks/pivotal_ci_test/recipes/default.rb +41 -0
- data/lib/generators/lobot/templates/soloistrc +2 -1
- data/lib/lobot.rb +1 -12
- data/lib/lobot/amazon.rb +91 -0
- data/lib/lobot/cli.rb +148 -0
- data/lib/lobot/config.rb +80 -0
- data/lib/lobot/jenkins.rb +17 -0
- data/lib/lobot/sobo.rb +35 -0
- data/lib/lobot/tasks/ci.rake +0 -178
- data/lib/lobot/version.rb +1 -1
- data/lobot.gemspec +19 -15
- data/script/bootstrap_server.sh +4 -1
- data/spec/lib/lobot/amazon_spec.rb +127 -0
- data/spec/lib/lobot/cli_spec.rb +212 -0
- data/spec/lib/lobot/config_spec.rb +95 -0
- data/spec/lib/lobot/jenkins_spec.rb +14 -0
- data/spec/spec_helper.rb +5 -10
- metadata +87 -31
- data/chef/cookbooks/pivotal_ci/libraries/ci_config.rb +0 -2
- data/spec/install_spec.rb +0 -80
@@ -1,3 +1,3 @@
|
|
1
|
-
<% node[
|
2
|
-
<%= "#{
|
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 %>
|
data/chef/cookbooks/pivotal_ci/templates/default/org.jenkinsci.plugins.xvfb.XvfbBuildWrapper.xml.erb
ADDED
@@ -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,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
|
data/lib/lobot.rb
CHANGED
@@ -1,15 +1,4 @@
|
|
1
|
-
require File.expand_path(
|
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?
|
data/lib/lobot/amazon.rb
ADDED
@@ -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
|
data/lib/lobot/cli.rb
ADDED
@@ -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
|
data/lib/lobot/config.rb
ADDED
@@ -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
|