ironfan 4.10.3 → 4.10.4

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # v4.10.4
2
+ * Cleaning up specs to use local credentials, chef-zero (requires libssl): fixes #242
3
+ * Added RDS support, see https://github.com/infochimps-labs/ironfan/pull/290 for details
4
+
1
5
  # v4.10.3
2
6
  * Tightening version constraints on excon, to avoid bad interaction between it & fog
3
7
 
data/Gemfile CHANGED
@@ -34,4 +34,6 @@ group :test do
34
34
  gem 'guard-yard'
35
35
  gem 'ruby_gntp'
36
36
  gem 'ruby-debug19'
37
+ #
38
+ gem 'chef-zero'
37
39
  end
data/Gemfile.lock CHANGED
@@ -22,6 +22,11 @@ GEM
22
22
  treetop (~> 1.4.9)
23
23
  uuidtools
24
24
  yajl-ruby (~> 1.1)
25
+ chef-zero (1.1.3)
26
+ hashie (~> 2.0)
27
+ mixlib-log (~> 1.3)
28
+ moneta (< 0.7.0)
29
+ puma (~> 2.0)
25
30
  coderay (1.0.9)
26
31
  columnize (0.3.6)
27
32
  configliere (0.4.18)
@@ -59,6 +64,7 @@ GEM
59
64
  guard-yard (2.1.0)
60
65
  guard (>= 1.1.0)
61
66
  yard (>= 0.7.0)
67
+ hashie (2.0.5)
62
68
  highline (1.6.19)
63
69
  ipaddress (0.8.0)
64
70
  jeweler (1.8.4)
@@ -107,6 +113,9 @@ GEM
107
113
  coderay (~> 1.0.5)
108
114
  method_source (~> 0.8)
109
115
  slop (~> 3.4)
116
+ puma (2.0.1)
117
+ rack (>= 1.1, < 2.0)
118
+ rack (1.5.2)
110
119
  rake (10.0.4)
111
120
  rb-fsevent (0.9.3)
112
121
  rb-inotify (0.9.0)
@@ -163,6 +172,7 @@ PLATFORMS
163
172
  DEPENDENCIES
164
173
  bundler (~> 1.0)
165
174
  chef (~> 10.16)
175
+ chef-zero
166
176
  excon (~> 0.21.0)
167
177
  fog (~> 1.2)
168
178
  formatador (~> 0.2)
data/README.md CHANGED
@@ -23,7 +23,7 @@ Ironfan consists of the following Toolset:
23
23
  - knife plugins to orchestrate clusters of machines using simple commands like `knife cluster launch`
24
24
  - logic to coordinate truth among chef server and cloud providers.
25
25
  * [ironfan-pantry](https://github.com/infochimps-labs/ironfan-pantry): Our collection of industrial-strength, cloud-ready recipes for Hadoop, HBase, Cassandra, Elasticsearch, Zabbix and more.
26
- * [silverware cookbook](https://github.com/infochimps-labs/ironfan-homebase/tree/master/cookbooks/silverware): coordinate discovery of services ("list all the machines for `awesome_webapp`, that I might load balance them") and aspects ("list all components that write logs, that I might logrotate them, or that I might monitor the free space on their volumes".
26
+ * [silverware cookbook](https://github.com/infochimps-labs/ironfan-pantry/tree/master/cookbooks/silverware): coordinate discovery of services ("list all the machines for `awesome_webapp`, that I might load balance them") and aspects ("list all components that write logs, that I might logrotate them, or that I might monitor the free space on their volumes".
27
27
 
28
28
  ### Documentation
29
29
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 4.10.3
1
+ 4.10.4
data/ironfan.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "ironfan"
8
- s.version = "4.10.3"
8
+ s.version = "4.10.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Infochimps"]
12
- s.date = "2013-05-17"
12
+ s.date = "2013-06-03"
13
13
  s.description = "Ironfan allows you to orchestrate not just systems but clusters of machines. It includes a powerful layer on top of knife and a collection of cloud cookbooks."
14
14
  s.email = "coders@infochimps.com"
15
15
  s.extra_rdoc_files = [
@@ -40,6 +40,7 @@ Gem::Specification.new do |s|
40
40
  "ironfan.gemspec",
41
41
  "lib/chef/knife/bootstrap/centos6.2-ironfan.erb",
42
42
  "lib/chef/knife/bootstrap/chef-full-ironfan.erb",
43
+ "lib/chef/knife/bootstrap/rhel6.3-ironfan.erb",
43
44
  "lib/chef/knife/bootstrap/ubuntu10.04-ironfan.erb",
44
45
  "lib/chef/knife/bootstrap/ubuntu12.04-ironfan.erb",
45
46
  "lib/chef/knife/cluster_bootstrap.rb",
@@ -69,6 +70,7 @@ Gem::Specification.new do |s|
69
70
  "lib/ironfan/dsl/compute.rb",
70
71
  "lib/ironfan/dsl/ec2.rb",
71
72
  "lib/ironfan/dsl/facet.rb",
73
+ "lib/ironfan/dsl/rds.rb",
72
74
  "lib/ironfan/dsl/role.rb",
73
75
  "lib/ironfan/dsl/server.rb",
74
76
  "lib/ironfan/dsl/virtualbox.rb",
@@ -89,6 +91,9 @@ Gem::Specification.new do |s|
89
91
  "lib/ironfan/provider/ec2/machine.rb",
90
92
  "lib/ironfan/provider/ec2/placement_group.rb",
91
93
  "lib/ironfan/provider/ec2/security_group.rb",
94
+ "lib/ironfan/provider/rds.rb",
95
+ "lib/ironfan/provider/rds/machine.rb",
96
+ "lib/ironfan/provider/rds/security_group.rb",
92
97
  "lib/ironfan/provider/virtualbox.rb",
93
98
  "lib/ironfan/provider/virtualbox/machine.rb",
94
99
  "lib/ironfan/provider/vsphere.rb",
@@ -132,6 +137,7 @@ Gem::Specification.new do |s|
132
137
  "spec/fixtures/ec2/elb/snakeoil.key",
133
138
  "spec/fixtures/gunbai.rb",
134
139
  "spec/fixtures/gunbai_slice.json",
140
+ "spec/fixtures/knife/knife.rb",
135
141
  "spec/integration/minimal-chef-repo/chefignore",
136
142
  "spec/integration/minimal-chef-repo/environments/_default.json",
137
143
  "spec/integration/minimal-chef-repo/knife/credentials/knife-org.rb",
@@ -155,7 +161,7 @@ Gem::Specification.new do |s|
155
161
  s.require_paths = ["lib"]
156
162
  s.rubygems_version = "1.8.23"
157
163
  s.summary = "Infochimps' lightweight cloud orchestration toolkit, built on top of Chef."
158
- s.test_files = ["spec/spec_helper/dummy_chef.rb", "spec/integration/spec_helper/launch_cluster.rb", "spec/integration/minimal-chef-repo/roles/systemwide.rb", "spec/integration/minimal-chef-repo/chefignore", "spec/integration/minimal-chef-repo/knife/credentials/knife-org.rb", "spec/integration/minimal-chef-repo/knife/knife.rb", "spec/integration/minimal-chef-repo/environments/_default.json", "spec/integration/spec/simple_cluster_spec.rb", "spec/integration/spec/elb_build_spec.rb", "spec/integration/spec_helper.rb", "spec/test_config.rb", "spec/chef/cluster_bootstrap_spec.rb", "spec/chef/cluster_launch_spec.rb", "spec/fixtures/gunbai_slice.json", "spec/fixtures/ec2/elb/snakeoil.crt", "spec/fixtures/ec2/elb/snakeoil.key", "spec/fixtures/gunbai.rb", "spec/spec_helper.rb", "spec/ironfan/cluster_spec.rb", "spec/ironfan/ec2/cloud_provider_spec.rb", "spec/ironfan/ec2/elb_spec.rb", "spec/ironfan/ec2/security_group_spec.rb"]
164
+ s.test_files = ["spec/spec_helper/dummy_chef.rb", "spec/integration/spec_helper/launch_cluster.rb", "spec/integration/minimal-chef-repo/roles/systemwide.rb", "spec/integration/minimal-chef-repo/chefignore", "spec/integration/minimal-chef-repo/knife/credentials/knife-org.rb", "spec/integration/minimal-chef-repo/knife/knife.rb", "spec/integration/minimal-chef-repo/environments/_default.json", "spec/integration/spec/simple_cluster_spec.rb", "spec/integration/spec/elb_build_spec.rb", "spec/integration/spec_helper.rb", "spec/test_config.rb", "spec/chef/cluster_bootstrap_spec.rb", "spec/chef/cluster_launch_spec.rb", "spec/fixtures/gunbai_slice.json", "spec/fixtures/knife/knife.rb", "spec/fixtures/ec2/elb/snakeoil.crt", "spec/fixtures/ec2/elb/snakeoil.key", "spec/fixtures/gunbai.rb", "spec/spec_helper.rb", "spec/ironfan/cluster_spec.rb", "spec/ironfan/ec2/cloud_provider_spec.rb", "spec/ironfan/ec2/elb_spec.rb", "spec/ironfan/ec2/security_group_spec.rb"]
159
165
 
160
166
  if s.respond_to? :specification_version then
161
167
  s.specification_version = 3
@@ -0,0 +1,179 @@
1
+ bash <<'EOF' || echo "Chef bootstrap failed!"
2
+
3
+ # This is the RHEL 6 bootstrap script from infochimps' ironfan. It is
4
+ # based on opscode's bootstrap script, with the following important differences:
5
+ #
6
+ # * installs ruby 1.9.2 (not 1.8.7) from source
7
+ # * upgrades rubygems rather than installing from source
8
+ # * pushes the computer identity into the first-boot.json
9
+ # * installs the chef-client service and kicks off the first run of chef
10
+
11
+ set -e
12
+
13
+ <%= (@config[:verbosity].to_i > 1 ? 'set -v' : '') %>
14
+
15
+ RUBY_VERSION=1.9.2-p290
16
+ CHEF_VERSION=<%= bootstrap_version_string.gsub(/.*[\s=]/,"") %>
17
+
18
+ mkdir -p /tmp/knife-bootstrap
19
+ chmod 700 /tmp/knife-bootstrap
20
+ cd /tmp/knife-bootstrap
21
+
22
+ <%= "export http_proxy=\"#{knife_config[:bootstrap_proxy]}\"" if knife_config[:bootstrap_proxy] -%>
23
+ cat /etc/redhat-release
24
+
25
+ date > /etc/box_build_time
26
+
27
+ echo -e "`date` \n\n**** \n**** yum upgrade:\n****\n"
28
+ yum upgrade --assumeyes
29
+
30
+ echo -e "`date` \n\n**** \n**** Installing base packages:\n****\n"
31
+ yum install --assumeyes make wget
32
+ yum install --assumeyes git rpm-build rpmdevtools gcc glibc-static zlib-devel libxml2-devel libxslt-devel openssl-devel telnet nc uuid-devel
33
+ if [ ! -d runit-rpm ]; then git clone https://github.com/imeyer/runit-rpm.git; fi
34
+ cd runit-rpm
35
+ ./build.sh
36
+ yum install --assumeyes /root/rpmbuild/RPMS/x86_64/runit-*.rpm || true # TODO: Remove this shim
37
+ cd -
38
+ yum remove --assumeyes prelink
39
+ yum clean all
40
+
41
+ if [ ! -f /usr/bin/chef-client ]; then
42
+ echo -e "`date` \n\n**** \n**** Installing ruby version ${RUBY_VERSION}:\n****\n"
43
+
44
+ wget ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-${RUBY_VERSION}.tar.gz
45
+ tar xzf ruby-${RUBY_VERSION}.tar.gz
46
+ cd ruby-${RUBY_VERSION}
47
+ ./configure --with-ruby-version=${RUBY_VERSION} --prefix=/usr --program-suffix=${RUBY_VERSION}
48
+ make -j2
49
+ make install
50
+
51
+ alternatives \
52
+ --install /usr/bin/ruby ruby /usr/bin/ruby${RUBY_VERSION} 400 \
53
+ --slave /usr/bin/ri ri /usr/bin/ri${RUBY_VERSION} \
54
+ --slave /usr/bin/irb irb /usr/bin/irb${RUBY_VERSION} \
55
+ --slave /usr/bin/erb erb /usr/bin/erb${RUBY_VERSION} \
56
+ --slave /usr/bin/gem gem /usr/bin/gem${RUBY_VERSION} \
57
+ --slave /usr/share/man/man1/ruby.1.gz ruby.1.gz \
58
+ /usr/share/man/man1/ruby${RUBY_VERSION}.1
59
+
60
+ echo -e "`date` \n\n**** \n**** Updating rubygems:\n****\n"
61
+ gem install rubygems-update -v 1.8.5
62
+
63
+ echo -e "`date` \n\n**** \n**** Installing chef:\n****\n"
64
+ gem install net-ssh --no-rdoc --no-ri --version 2.2.2
65
+ gem install net-ssh-gateway --no-rdoc --no-ri --version 1.1.0
66
+ gem install net-ssh-multi --no-rdoc --no-ri --version 1.1
67
+ gem install ohai --no-rdoc --no-ri --version 6.14.0
68
+ gem install chef --no-rdoc --no-ri --version 10.16.4
69
+ # gems needed for the client.rb or so generically useful you want them at hand
70
+ gem install --no-rdoc --no-ri extlib bundler json right_aws pry fog
71
+
72
+ else # no chef-client
73
+ echo -e "`date` \n\n**** \n**** Chef is present -- skipping apt/ruby/chef installation\n****\n"
74
+ fi # end ruby+chef install
75
+
76
+ echo -e "`date` \n\n**** \n**** Knifing in the chef client config files:\n****\n"
77
+ mkdir -p /etc/chef
78
+
79
+ <%- if @config[:client_key] %>
80
+ (
81
+ cat <<'EOP'
82
+ <%= @config[:client_key] %>
83
+ EOP
84
+ ) > /tmp/knife-bootstrap/client.pem
85
+ awk NF /tmp/knife-bootstrap/client.pem > /etc/chef/client.pem
86
+ <%- else %>
87
+ (
88
+ cat <<'EOP'
89
+ <%= validation_key %>
90
+ EOP
91
+ ) > /tmp/knife-bootstrap/validation.pem
92
+ awk NF /tmp/knife-bootstrap/validation.pem > /etc/chef/validation.pem
93
+ <%- end %>
94
+
95
+ <% if @chef_config[:encrypted_data_bag_secret] -%>
96
+ (
97
+ cat <<'EOP'
98
+ <%= encrypted_data_bag_secret %>
99
+ EOP
100
+ ) > /tmp/encrypted_data_bag_secret
101
+ awk NF /tmp/encrypted_data_bag_secret > /etc/chef/encrypted_data_bag_secret
102
+ rm /tmp/encrypted_data_bag_secret
103
+ <% end -%>
104
+
105
+ echo -e "`date` \n\n**** \n**** Nuking our temp files:\n****\n"
106
+
107
+ cd /tmp
108
+ rm -rf /tmp/knife-bootstrap
109
+
110
+ echo -e "`date` \n\n**** \n**** Creating chef client script:\n****\n"
111
+
112
+ (
113
+ cat <<'EOP'
114
+ <%= config_content %>
115
+ <%= @config[:computer].chef_client_script_content %>
116
+ EOP
117
+ ) > /etc/chef/client.rb
118
+
119
+ (
120
+ cat <<'EOP'
121
+ <%= { "run_list" => @run_list, "cluster_name" => @config[:server].cluster_name, "facet_name" => @config[:server].facet_name, "facet_index" => @config[:server].index }.to_json %>
122
+ EOP
123
+ ) > /etc/chef/first-boot.json
124
+
125
+ # Ensure that EC2 images are recognized even inside VPC
126
+ mkdir -p /etc/chef/ohai/hints/
127
+ touch /etc/chef/ohai/hints/ec2.json
128
+
129
+
130
+ echo -e "`date` \n\n**** \n**** Adding chef client nonce script:\n****\n"
131
+
132
+ cat > /etc/init.d/chef-client-nonce <<'EOP'
133
+ #! /bin/sh
134
+ ### BEGIN INIT INFO
135
+ # Provides: chef-client-nonce
136
+ # Required-Start: $remote_fs $network
137
+ # Required-Stop:
138
+ # Default-Start: 2 3 4 5
139
+ # Default-Stop: 0 1 6
140
+ # Short-Description: Start a single chef-client run.
141
+ ### END INIT INFO
142
+ #
143
+ # Copyright (c) 2009-2010 Opscode, Inc, <legal@opscode.com>
144
+ #
145
+ # chef-client Startup script for chef-client.
146
+ # chkconfig: - 99 02
147
+ # description: starts up chef-client once, at boot
148
+
149
+ case "$1" in
150
+ start)
151
+ /usr/bin/chef-client -L /var/log/chef/client.log
152
+ exit $?
153
+ ;;
154
+ *)
155
+ echo "Usage: /etc/init.d/chef-client-nonce start" >&2
156
+ exit 1
157
+ ;;
158
+ esac
159
+ EOP
160
+
161
+ mkdir -p /var/log/chef
162
+ mkdir -p /etc/sv
163
+ chmod +x /etc/init.d/chef-client-nonce
164
+ chkconfig --add chef-client-nonce
165
+ chkconfig --del iptables
166
+ chkconfig --del rh-cloud-firstboot
167
+ chkconfig --add rh-cloud-firstboot
168
+
169
+ rm /etc/sysconfig/rh-cloud-firstboot
170
+
171
+ <%- if (@config[:bootstrap_runs_chef_client].to_s == 'true') || (@chef_config.knife[:bootstrap_runs_chef_client].to_s == 'true') %>
172
+ sudo /etc/init.d/chef-client-nonce start
173
+ <%- end %>
174
+
175
+
176
+
177
+ echo -e "`date` \n\n**** \n**** Cleanup:\n****\n"
178
+ cd /
179
+ rm -r /tmp/knife-bootstrap
@@ -95,7 +95,7 @@ class Chef
95
95
  # As each server finishes, configure it
96
96
  Ironfan.parallel(launched) do |computer|
97
97
  if (computer.is_a?(Exception)) then ui.warn "Error launching #{computer.inspect}; skipping after-launch tasks."; next; end
98
- perform_after_launch_tasks(computer)
98
+ perform_after_launch_tasks(computer) if computer.machine.perform_after_launch_tasks?
99
99
  end
100
100
 
101
101
  if healthy?
@@ -13,6 +13,7 @@ module Ironfan
13
13
  when :ec2 then Ec2
14
14
  when :virtualbox then VirtualBox
15
15
  when :vsphere then Vsphere
16
+ when :rds then Rds
16
17
  else raise "Unsupported cloud #{obj[:name]}"
17
18
  end
18
19
  end
@@ -0,0 +1,84 @@
1
+ require 'digest/md5'
2
+
3
+ module Ironfan
4
+ class Dsl
5
+
6
+ class Compute < Ironfan::Dsl
7
+ def rds(*attrs,&block) cloud(:rds,*attrs,&block); end
8
+ end
9
+
10
+ class Rds < Cloud
11
+ magic :autoupgrade, :boolean, :default => true
12
+ magic :availability_zones, Array, :default => ['us-east-1d']
13
+ magic :backup_retention, Integer, :default => 1
14
+ magic :charset, String, :default => nil
15
+ magic :dbname, String, :default => nil
16
+ magic :default_availability_zone, String, :default => ->{ availability_zones.first }
17
+ magic :engine, String, :default => ->{ engines.first }
18
+ magic :engines, Array, :default => ["MySQL", "oracle-se1", "oracle-se", "oracle-ee", "sqlserver-ee", "sqlserver-se", "sqlserver-ex", "sqlserver-web"]
19
+ magic :flavor, String, :default => "db.t1.micro"
20
+ magic :iops, Integer, :default => 1000
21
+ magic :license_model, String, :default => nil
22
+ magic :multi_az, :boolean, :default => false
23
+ magic :password, String, :default => nil
24
+ magic :port, Integer, :default => -> { default_port }
25
+ magic :preferred_backup_window, String, :default => nil # Format hh24:mi-hh24:mi. Needs to be at least 30 minutes, UTC,
26
+ magic :preferred_maintenance_window, String, :default => nil # Format ddd:hh24:mi-ddd:hh24:mi. Minimum 30 minutes, UTC
27
+ magic :provider, Whatever, :default => Ironfan::Provider::Rds
28
+ magic :size, Integer, :default => 50 # Size in GB
29
+ collection :security_groups, Ironfan::Dsl::Rds::SecurityGroup, :key_method => :name
30
+ magic :username, String, :default => nil
31
+ magic :version, String, :default => nil
32
+
33
+ def receive_provider(obj)
34
+ if obj.is_a?(String)
35
+ write_attribute :provider, Gorillib::Inflector.constantize(Gorillib::Inflector.camelize(obj.gsub(/\./, '/')))
36
+ else
37
+ super(obj)
38
+ end
39
+ end
40
+
41
+ # This is a dummy function to fill Ironfan's requirements.
42
+ def implied_volumes
43
+ []
44
+ end
45
+
46
+ def default_port
47
+ return 3306 if engine == "MySQL"
48
+ return 1521 if ["oracle-se1", "oracle-se", "oracle-ee"].include?(engine)
49
+ return 1433 if ["sqlserver-ee", "sqlserver-se", "sqlserver-ex", "sqlserver-web"].include?(engine)
50
+ end
51
+
52
+ def to_display(style,values={})
53
+ return values if style == :minimal
54
+
55
+ values["Engine"] = engine
56
+ values["AZ"] = default_availability_zone
57
+ return values if style == :default
58
+
59
+ # values["Public IP"] = elastic_ip if elastic_ip
60
+ values
61
+ end
62
+
63
+ class SecurityGroup < Ironfan::Dsl
64
+ field :name, String
65
+ field :ec2_authorizations, Array, :default => []
66
+ field :ip_authorizations, Array, :default => []
67
+
68
+ def authorize_ip_range(cidr_ip = '0.0.0.0/0')
69
+ ip_authorizations << cidr_ip
70
+ ip_authorizations.compact!
71
+ ip_authorizations.uniq!
72
+ end
73
+
74
+ def authorize_ec2_group(ec2_security_group)
75
+ ec2_authorizations << ec2_security_group
76
+ ec2_authorizations.compact!
77
+ ec2_authorizations.uniq!
78
+ end
79
+ end # SecurityGroup
80
+
81
+ end
82
+ end
83
+ end
84
+
@@ -31,6 +31,9 @@ module Ironfan
31
31
  end
32
32
  class VirtualBox < Cloud; end
33
33
  class Vsphere < Cloud; end
34
+ class Rds < Cloud
35
+ class SecurityGroup < Ironfan::Dsl; end
36
+ end
34
37
  end
35
38
 
36
39
  class Provider < Builder
@@ -62,7 +65,10 @@ module Ironfan
62
65
  class Machine < Ironfan::IaasProvider::Machine; end
63
66
  class Keypair < Ironfan::Provider::Resource; end
64
67
  end
65
-
68
+ class Rds < Ironfan::IaasProvider
69
+ class Machine < Ironfan::IaasProvider::Machine; end
70
+ class SecurityGroup < Ironfan::Provider::Resource; end
71
+ end
66
72
  end
67
73
 
68
74
  end
@@ -77,6 +77,10 @@ module Ironfan
77
77
  adaptee.wait_for{ machine.stopping? or machine.stopped? }
78
78
  end
79
79
 
80
+ def perform_after_launch_tasks?
81
+ true
82
+ end
83
+
80
84
  def to_display(style,values={})
81
85
  # style == :minimal
82
86
  values["State"] = state.to_sym
@@ -0,0 +1,225 @@
1
+ module Ironfan
2
+ class Provider
3
+ class Rds
4
+
5
+ class Machine < Ironfan::IaasProvider::Machine
6
+ delegate :availability_zone, :created_at, :desstroy, :endpoint, :engine,
7
+ :engine_version, :destroy, :flavor_id, :id, :id=, :name, :state, :add_tags,
8
+ :tags,
9
+ :to => :adaptee
10
+
11
+ def self.shared?() false; end
12
+ def self.multiple?() false; end
13
+ def self.resource_type() :machine; end
14
+ def self.expected_ids(computer) [computer.server.full_name]; end
15
+
16
+ def name
17
+ return id if tags.empty?
18
+ tags["Name"] || tags["name"] || id
19
+ end
20
+
21
+ def public_hostname ; dns_name ; end
22
+
23
+ def created?
24
+ not ['terminated', 'shutting-down'].include? state
25
+ end
26
+
27
+ def deleting?
28
+ state == "deleting"
29
+ end
30
+
31
+ def pending?
32
+ state == "pending"
33
+ end
34
+
35
+ def creating?
36
+ state == "creating"
37
+ end
38
+
39
+ def rebooting?
40
+ state == "rebooting"
41
+ end
42
+
43
+ def available?
44
+ state == "available"
45
+ end
46
+
47
+ def stopped?
48
+ end
49
+
50
+ def perform_after_launch_tasks?
51
+ false
52
+ end
53
+
54
+ def to_display(style,values={})
55
+ # style == :minimal
56
+ values["State"] = state.to_sym
57
+ values["Endpoint"] = adaptee.endpoint["Address"]
58
+ values["Created On"] = created_at.to_date
59
+ return values if style == :minimal
60
+
61
+ # style == :default
62
+ values["Flavor"] = flavor_id
63
+ values["AZ"] = availability_zone
64
+ return values if style == :default
65
+
66
+ # style == :expanded
67
+ values["Port"] = adaptee.endpoint["Port"]
68
+ values["Engine"] = engine
69
+ values["EngineVersion"] = engine_version
70
+ values
71
+ end
72
+
73
+ def to_s
74
+ "<%-15s %-12s %-25s %-25s %-15s %-15s>" % [
75
+ self.class.handle, id, created_at, tags['name'], flavor_id, availability_zone]
76
+ end
77
+
78
+ #
79
+ # Discovery
80
+ #
81
+ def self.load!(cluster=nil)
82
+ Rds.connection.servers.each do |fs|
83
+ machine = new(:adaptee => fs)
84
+ if (not machine.created?)
85
+ next unless Ironfan.chef_config[:include_terminated]
86
+ remember machine, :append_id => "terminated:#{machine.id}"
87
+ elsif recall? machine.name
88
+ machine.bogus << :duplicate_machines
89
+ recall(machine.name).bogus << :duplicate_machines
90
+ remember machine, :append_id => "duplicate:#{machine.id}"
91
+ else # never seen it
92
+ remember machine
93
+ end
94
+ Chef::Log.debug("Loaded #{machine}")
95
+ end
96
+ end
97
+
98
+ def receive_adaptee(obj)
99
+ obj = Rds.connection.servers.new(obj) if obj.is_a?(Hash)
100
+ super
101
+ end
102
+
103
+ # Find active machines that haven't matched, but should have,
104
+ # make sure all bogus machines have a computer to attach to
105
+ # for display purposes
106
+ def self.validate_resources!(computers)
107
+ recall.each_value do |machine|
108
+ next unless machine.users.empty? and machine.name
109
+ if machine.name.match("^#{computers.cluster.name}-")
110
+ machine.bogus << :unexpected_machine
111
+ end
112
+ next unless machine.bogus?
113
+ fake = Ironfan::Broker::Computer.new
114
+ fake[:machine] = machine
115
+ fake.name = machine.name
116
+ machine.users << fake
117
+ computers << fake
118
+ end
119
+ end
120
+
121
+ #
122
+ # Manipulation
123
+ #
124
+ def self.create!(computer)
125
+ return if computer.machine? and computer.machine.created?
126
+ Ironfan.step(computer.name,"creating RDS machine... go grab some a beer or two.", :green)
127
+ #
128
+ errors = lint(computer)
129
+ if errors.present? then raise ArgumentError, "Failed validation: #{errors.inspect}" ; end
130
+ #
131
+ launch_desc = launch_description(computer)
132
+ launch_desc[:id] = computer.name
133
+ Chef::Log.debug(JSON.pretty_generate(launch_desc))
134
+
135
+ Ironfan.safely do
136
+ fog_server = Rds.connection.servers.create(launch_desc)
137
+ machine = Machine.new(:adaptee => fog_server)
138
+ computer.machine = machine
139
+ remember machine, :id => computer.name
140
+
141
+ Ironfan.step(fog_server.id,"waiting for machine to be ready", :gray)
142
+ Ironfan.tell_you_thrice :name => fog_server.id,
143
+ :problem => "server unavailable",
144
+ :error_class => Fog::Errors::Error do
145
+ fog_server.wait_for { ready? }
146
+ end
147
+ end
148
+
149
+ # tag the computer correctly
150
+ tags = {
151
+ 'cluster' => computer.server.cluster_name,
152
+ 'facet' => computer.server.facet_name,
153
+ 'index' => computer.server.index,
154
+ 'name' => computer.name,
155
+ 'Name' => computer.name,
156
+ }
157
+ Rds.ensure_tags(tags, computer.machine)
158
+ end
159
+
160
+ # @returns [Hash{String, Array}] of 'what you did wrong' => [relevant, info]
161
+ def self.lint(computer)
162
+ cloud = computer.server.cloud(:rds)
163
+ info = [computer.name, cloud.inspect]
164
+ errors = {}
165
+ errors["No Password"] = info if cloud.password.empty?
166
+ errors["No Username"] = info if cloud.username.empty?
167
+ errors["Size"] = info if cloud.size < 5
168
+ errors
169
+ end
170
+
171
+ def self.launch_description(computer)
172
+ cloud = computer.server.cloud(:rds)
173
+ user_data_hsh = {
174
+ :chef_server => Chef::Config[:chef_server_url],
175
+ :node_name => computer.name,
176
+ :organization => Chef::Config[:organization],
177
+ :cluster_name => computer.server.cluster_name,
178
+ :facet_name => computer.server.facet_name,
179
+ :facet_index => computer.server.index,
180
+ }
181
+
182
+
183
+ # main machine info
184
+ # note that Fog does not actually create tags when it creates a
185
+ # server; they and permanence are applied during sync
186
+ description = {
187
+ :allocated_storage => cloud.size,
188
+ :auto_minor_version_upgrade => cloud.autoupgrade,
189
+ :availability_zone => cloud.multi_az ? nil : cloud.default_availability_zone,
190
+ :backup_retention_period => cloud.backup_retention,
191
+ :db_name => cloud.dbname,
192
+ :engine => cloud.engine,
193
+ :engine_version => cloud.version,
194
+ :flavor_id => cloud.flavor,
195
+ :license_model => cloud.license_model,
196
+ :master_username => cloud.username,
197
+ :multi_az => cloud.multi_az,
198
+ :password => cloud.password,
199
+ :port => cloud.port,
200
+ :preferred_backup_window => cloud.preferred_backup_window,
201
+ :preferred_maintenance_window => cloud.preferred_maintenance_window,
202
+ :security_group_names => cloud.security_groups.keys,
203
+ :user_data => JSON.pretty_generate(user_data_hsh),
204
+ # :charset => cloud.charset, # Not supported in FOG?
205
+ # :iops => cloud.iops, # Not supported in FOG?
206
+ }
207
+
208
+ description
209
+ end
210
+
211
+ def self.destroy!(computer)
212
+ return unless computer.machine?
213
+ forget computer.machine.name
214
+ computer.machine.destroy
215
+ computer.machine.reload # show the node as shutting down
216
+ end
217
+
218
+ def self.save!(computer)
219
+ return unless computer.machine?
220
+ end
221
+ end
222
+
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,128 @@
1
+ module Ironfan
2
+ class Provider
3
+ class Rds
4
+
5
+ class SecurityGroup < Ironfan::Provider::Resource
6
+
7
+ delegate :id, :authorize_port_range,
8
+ :to => :adaptee
9
+
10
+ def self.shared?() true; end
11
+ def self.multiple?() true; end
12
+ def self.resource_type() :security_group; end
13
+ def self.expected_ids(computer)
14
+ return unless computer.server
15
+ rds = computer.server.cloud(:rds)
16
+ rds.security_groups.keys.uniq
17
+ end
18
+
19
+ def name()
20
+ adaptee.id
21
+ end
22
+
23
+ #
24
+ # Discovery
25
+ #
26
+ def self.load!(cluster=nil)
27
+ Rds.connection.security_groups.reject { |raw| raw.blank? }.each do |raw|
28
+ sg = SecurityGroup.new(:adaptee => raw)
29
+ remember(sg)
30
+ Chef::Log.debug("Loaded #{sg}: #{sg.inspect}")
31
+ end
32
+ end
33
+
34
+ def receive_adaptee(obj)
35
+ obj = Rds.connection.security_groups.new(obj) if obj.is_a?(Hash)
36
+ super
37
+ end
38
+
39
+ def to_s
40
+ return "<%-15s %s>" % [ self.class.handle, id ]
41
+ end
42
+
43
+ #
44
+ # Manipulation
45
+ #
46
+
47
+ def self.prepare!(computers)
48
+
49
+ # Create any groups that don't yet exist, and ensure any authorizations
50
+ # that are required for those groups
51
+ cluster_name = nil
52
+ groups_to_create = [ ]
53
+ ec2_authorizations = [ ]
54
+ ip_authorizations = [ ]
55
+
56
+ # First, deduce the list of all groups to which at least one instance belongs
57
+ # We'll use this later to decide whether to create groups, or authorize access,
58
+ # using a VPC security group or an EC2 security group.
59
+
60
+ computers.select { |computer| Rds.applicable computer }.each do |computer|
61
+ cloud = computer.server.cloud(:rds)
62
+ cluster_name = computer.server.cluster_name
63
+
64
+ # Iterate over all of the security group information, keeping track of
65
+ # any groups that must exist and any authorizations that must be ensured
66
+ cloud.security_groups.values.each do |dsl_group|
67
+ groups_to_create << dsl_group.name
68
+ ip_authorizations << dsl_group.ip_authorizations.map do |ip|
69
+ {
70
+ :grantor => dsl_group.name,
71
+ :grantee => ip,
72
+ }
73
+ end
74
+
75
+ ec2_authorizations << dsl_group.ec2_authorizations.map do |ec2|
76
+ {
77
+ :grantor => dsl_group.name,
78
+ :grantee => ec2,
79
+ }
80
+ end
81
+ end
82
+ end
83
+
84
+ groups_to_create = groups_to_create.flatten.uniq.reject { |group| recall? group.to_s }.sort
85
+ ip_authorizations = ip_authorizations.flatten.uniq.sort { |a,b| a[:grantor] <=> b[:grantor] }
86
+ ec2_authorizations = ec2_authorizations.flatten.uniq.sort { |a,b| a[:grantor] <=> b[:grantor] }
87
+
88
+ Ironfan.step(cluster_name, "creating security groups", :blue) unless groups_to_create.empty?
89
+ groups_to_create.each do |group|
90
+ Ironfan.step(group, "creating #{group} security group", :blue)
91
+ begin
92
+ Rds.connection.create_db_security_group(group,"Ironfan created group #{group}")
93
+ rescue Fog::Compute::AWS::Error => e # InvalidPermission.Duplicate
94
+ Chef::Log.info("ignoring security group error: #{e}")
95
+ end
96
+ end
97
+
98
+ # Re-load everything so that we have a @@known list of security groups to manipulate
99
+ load! unless groups_to_create.empty?
100
+
101
+ # Now make sure that all required authorizations are present
102
+ Ironfan.step(cluster_name, "ensuring security group permissions", :blue) unless (ec2_authorizations.empty? or ip_authorizations.empty?)
103
+ ip_authorizations.each do |auth|
104
+ message = " ensuring access from #{auth[:grantor]} to #{auth[:grantee]} "
105
+ Ironfan.step(auth[:grantor], message, :blue)
106
+ begin
107
+ Rds.connection.authorize_db_security_group_ingress(auth[:grantor], { "CIDRIP" => auth[:grantee] })
108
+ rescue Fog::AWS::RDS::AuthorizationAlreadyExists => e
109
+ Chef::Log.info("ignoring security group error: #{e}")
110
+ end
111
+ end
112
+
113
+ ec2_authorizations.each do |auth|
114
+ message = " ensuring access from #{auth[:grantor]} to #{auth[:grantee]} "
115
+ Ironfan.step(auth[:grantor], message, :blue)
116
+ aws_account, security_group = auth[:grantee].split('/')
117
+ begin
118
+ Rds.connection.authorize_db_security_group_ingress(auth[:grantor], { "EC2SecurityGroupName" => security_group, "EC2SecurityGroupOwnerId" => aws_account })
119
+ rescue Fog::AWS::RDS::AuthorizationAlreadyExists => e
120
+ Chef::Log.info("ignoring security group error: #{e}")
121
+ end
122
+ end
123
+
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,53 @@
1
+ module Ironfan
2
+ class Provider
3
+
4
+ class Rds < Ironfan::IaasProvider
5
+ self.handle = :rds
6
+
7
+ def self.resources
8
+ [ Machine, SecurityGroup ]
9
+ end
10
+
11
+ #
12
+ # Utility functions
13
+ #
14
+ def self.connection
15
+ @@connection ||= Fog::AWS::RDS.new(self.aws_credentials)
16
+ end
17
+
18
+ def self.iam
19
+ credentials = self.aws_credentials
20
+ credentials.delete(:region)
21
+ @@iam ||= Fog::AWS::IAM.new(credentials)
22
+ end
23
+
24
+ def self.aws_account_id()
25
+ Chef::Config[:knife][:aws_account_id]
26
+ end
27
+
28
+ # Ensure that a fog object (machine, volume, etc.) has the proper tags on it
29
+ def self.ensure_tags(tags, fog)
30
+ tags.delete_if {|k, v| fog.tags[k] == v.to_s rescue false }
31
+ return if tags.empty?
32
+
33
+ Ironfan.step(fog.id, "tagging with #{tags.inspect}", :green)
34
+ fog.add_tags(tags)
35
+ end
36
+
37
+ def self.applicable(computer)
38
+ computer.server and computer.server.clouds.include?(:rds)
39
+ end
40
+
41
+ private
42
+
43
+ def self.aws_credentials
44
+ return {
45
+ :aws_access_key_id => Chef::Config[:knife][:aws_access_key_id],
46
+ :aws_secret_access_key => Chef::Config[:knife][:aws_secret_access_key],
47
+ :region => Chef::Config[:knife][:region]
48
+ }
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -78,6 +78,10 @@ module Ironfan
78
78
  adaptee.PowerOffVM_Task.wait_for_completion
79
79
  end
80
80
 
81
+ def perform_after_launch_tasks?
82
+ true
83
+ end
84
+
81
85
  def self.ip_settings(settings, hostname)
82
86
  # FIXME: A lot of this is required if using a static IP
83
87
  # Heavily referenced Serengeti's FOG here
@@ -15,6 +15,7 @@ module Ironfan
15
15
  when :ec2 then Ec2
16
16
  when :vsphere then Vsphere
17
17
  when :virtualbox then VirtualBox
18
+ when :rds then Rds
18
19
  else raise "Unsupported provider #{obj[:name]}"
19
20
  end
20
21
  end
@@ -20,6 +20,7 @@ require 'ironfan/dsl/volume'
20
20
  require 'ironfan/dsl/cloud'
21
21
  require 'ironfan/dsl/ec2'
22
22
  require 'ironfan/dsl/vsphere'
23
+ require 'ironfan/dsl/rds'
23
24
 
24
25
 
25
26
  # Providers for specific resources
@@ -47,6 +48,9 @@ require 'ironfan/provider/vsphere'
47
48
  require 'ironfan/provider/vsphere/machine'
48
49
  require 'ironfan/provider/vsphere/keypair'
49
50
 
51
+ require 'ironfan/provider/rds'
52
+ require 'ironfan/provider/rds/machine'
53
+ require 'ironfan/provider/rds/security_group'
50
54
 
51
55
  # Broker classes to coordinate DSL expectations and provider resources
52
56
  require 'ironfan/broker'
@@ -0,0 +1,5 @@
1
+ username 'testmonkey'
2
+ ec2_key_dir "."
3
+ log_level :info
4
+ node_name username
5
+ client_key "#{username}.pem"
data/spec/spec_helper.rb CHANGED
@@ -12,9 +12,11 @@ Pathname.register_paths(
12
12
  fixtures: [:code, 'spec', 'fixtures'],
13
13
  )
14
14
 
15
- RSpec.configure do |config|
15
+ RSpec.configure do |cfg|
16
16
  def ironfan_go!
17
- Chef::Knife.new.configure_chef
17
+ k = Chef::Knife.new
18
+ k.config[:config_file] = Pathname.path_to(:fixtures, 'knife/knife.rb')
19
+ k.configure_chef
18
20
  Chef::Config.instance_eval do
19
21
  knife.merge!({
20
22
  :aws_access_key_id => 'access_key',
@@ -26,7 +28,11 @@ RSpec.configure do |config|
26
28
  require 'ironfan'
27
29
 
28
30
  Ironfan.ui = Chef::Knife::UI.new(STDOUT, STDERR, STDIN, {})
29
- Ironfan.chef_config = { :verbosity => 0 }
31
+ Ironfan.chef_config = k.config
30
32
  Ironfan.cluster_path
31
33
  end
32
34
  end
35
+
36
+ require 'chef_zero/server'
37
+ server = ChefZero::Server.new(port: 4000)
38
+ server.start_background
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ironfan
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.10.3
4
+ version: 4.10.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-17 00:00:00.000000000 Z
12
+ date: 2013-06-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: chef
@@ -251,6 +251,7 @@ files:
251
251
  - ironfan.gemspec
252
252
  - lib/chef/knife/bootstrap/centos6.2-ironfan.erb
253
253
  - lib/chef/knife/bootstrap/chef-full-ironfan.erb
254
+ - lib/chef/knife/bootstrap/rhel6.3-ironfan.erb
254
255
  - lib/chef/knife/bootstrap/ubuntu10.04-ironfan.erb
255
256
  - lib/chef/knife/bootstrap/ubuntu12.04-ironfan.erb
256
257
  - lib/chef/knife/cluster_bootstrap.rb
@@ -280,6 +281,7 @@ files:
280
281
  - lib/ironfan/dsl/compute.rb
281
282
  - lib/ironfan/dsl/ec2.rb
282
283
  - lib/ironfan/dsl/facet.rb
284
+ - lib/ironfan/dsl/rds.rb
283
285
  - lib/ironfan/dsl/role.rb
284
286
  - lib/ironfan/dsl/server.rb
285
287
  - lib/ironfan/dsl/virtualbox.rb
@@ -300,6 +302,9 @@ files:
300
302
  - lib/ironfan/provider/ec2/machine.rb
301
303
  - lib/ironfan/provider/ec2/placement_group.rb
302
304
  - lib/ironfan/provider/ec2/security_group.rb
305
+ - lib/ironfan/provider/rds.rb
306
+ - lib/ironfan/provider/rds/machine.rb
307
+ - lib/ironfan/provider/rds/security_group.rb
303
308
  - lib/ironfan/provider/virtualbox.rb
304
309
  - lib/ironfan/provider/virtualbox/machine.rb
305
310
  - lib/ironfan/provider/vsphere.rb
@@ -343,6 +348,7 @@ files:
343
348
  - spec/fixtures/ec2/elb/snakeoil.key
344
349
  - spec/fixtures/gunbai.rb
345
350
  - spec/fixtures/gunbai_slice.json
351
+ - spec/fixtures/knife/knife.rb
346
352
  - spec/integration/minimal-chef-repo/chefignore
347
353
  - spec/integration/minimal-chef-repo/environments/_default.json
348
354
  - spec/integration/minimal-chef-repo/knife/credentials/knife-org.rb
@@ -375,7 +381,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
375
381
  version: '0'
376
382
  segments:
377
383
  - 0
378
- hash: -4135598770723135786
384
+ hash: -3042039387970317693
379
385
  required_rubygems_version: !ruby/object:Gem::Requirement
380
386
  none: false
381
387
  requirements:
@@ -403,6 +409,7 @@ test_files:
403
409
  - spec/chef/cluster_bootstrap_spec.rb
404
410
  - spec/chef/cluster_launch_spec.rb
405
411
  - spec/fixtures/gunbai_slice.json
412
+ - spec/fixtures/knife/knife.rb
406
413
  - spec/fixtures/ec2/elb/snakeoil.crt
407
414
  - spec/fixtures/ec2/elb/snakeoil.key
408
415
  - spec/fixtures/gunbai.rb