lobot 1.0.pre → 2.0.0pre
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/.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
|