ironfan 4.10.3 → 4.10.4

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