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.
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