knife-server 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +1 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +3 -0
  5. data/.travis.yml +12 -8
  6. data/CHANGELOG.md +32 -1
  7. data/Gemfile +9 -4
  8. data/Guardfile +28 -0
  9. data/README.md +28 -5
  10. data/Rakefile +31 -10
  11. data/knife-server.gemspec +18 -8
  12. data/lib/chef/knife/bootstrap/_omnibus.sh +63 -10
  13. data/lib/chef/knife/bootstrap/chef10/rhel.erb +2 -0
  14. data/lib/chef/knife/bootstrap/chef11/omnibus.erb +4 -1
  15. data/lib/chef/knife/bootstrap/chef11/rhel.erb +2 -0
  16. data/lib/chef/knife/server_backup.rb +24 -10
  17. data/lib/chef/knife/server_bootstrap_base.rb +68 -23
  18. data/lib/chef/knife/server_bootstrap_ec2.rb +33 -20
  19. data/lib/chef/knife/server_bootstrap_linode.rb +20 -13
  20. data/lib/chef/knife/server_bootstrap_openstack.rb +128 -0
  21. data/lib/chef/knife/server_bootstrap_standalone.rb +28 -16
  22. data/lib/chef/knife/server_restore.rb +23 -9
  23. data/lib/knife-server.rb +1 -0
  24. data/lib/knife/server/credentials.rb +78 -42
  25. data/lib/knife/server/ec2_security_group.rb +24 -21
  26. data/lib/knife/server/ssh.rb +54 -18
  27. data/lib/knife/server/version.rb +2 -1
  28. data/spec/chef/knife/server_backup_spec.rb +58 -44
  29. data/spec/chef/knife/server_bootstrap_ec2_spec.rb +108 -80
  30. data/spec/chef/knife/server_bootstrap_linode_spec.rb +93 -64
  31. data/spec/chef/knife/server_bootstrap_openstack_spec.rb +305 -0
  32. data/spec/chef/knife/server_bootstrap_standalone_spec.rb +113 -76
  33. data/spec/chef/knife/server_restore_spec.rb +38 -37
  34. data/spec/knife/server/credientials_spec.rb +248 -51
  35. data/spec/knife/server/ec2_security_group_spec.rb +76 -68
  36. data/spec/knife/server/ssh_spec.rb +138 -22
  37. metadata +107 -31
@@ -1,3 +1,4 @@
1
+ # -*- encoding: utf-8 -*-
1
2
  #
2
3
  # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
3
4
  # Copyright:: Copyright (c) 2012 Fletcher Nichol
@@ -18,6 +19,7 @@
18
19
 
19
20
  module Knife
20
21
  module Server
22
+ # Sets up EC2 security groups for a Chef Server.
21
23
  class Ec2SecurityGroup
22
24
  def initialize(connection, ui)
23
25
  @aws = connection
@@ -29,10 +31,10 @@ module Knife
29
31
 
30
32
  ip_permissions.each do |p|
31
33
  if permission_exists?(group, p)
32
- @ui.msg "Inbound security group rule " +
34
+ @ui.msg "Inbound security group rule " \
33
35
  "#{p[:proto]}(#{p[:from]} -> #{p[:to]}) exists"
34
36
  else
35
- @ui.msg "Creating inbound security group rule for " +
37
+ @ui.msg "Creating inbound security group rule for " \
36
38
  "#{p[:proto]}(#{p[:from]} -> #{p[:to]})"
37
39
  options = permission_options(group, p)
38
40
  @aws.authorize_security_group_ingress(group.name, options)
@@ -57,45 +59,46 @@ module Knife
57
59
  private
58
60
 
59
61
  def ip_permissions
60
- [ {:proto => 'icmp', :from => -1, :to => -1},
61
- {:proto => 'tcp', :from => 0, :to => 65535},
62
- {:proto => 'udp', :from => 0, :to => 65535},
63
- {:proto => 'tcp', :from => 22, :to => 22, :cidr => '0.0.0.0/0'},
64
- {:proto => 'tcp', :from => 443, :to => 443, :cidr => '0.0.0.0/0'},
65
- {:proto => 'tcp', :from => 444, :to => 444, :cidr => '0.0.0.0/0'},
62
+ [
63
+ { :proto => "icmp", :from => -1, :to => -1 },
64
+ { :proto => "tcp", :from => 0, :to => 65535 },
65
+ { :proto => "udp", :from => 0, :to => 65535 },
66
+ { :proto => "tcp", :from => 22, :to => 22, :cidr => "0.0.0.0/0" },
67
+ { :proto => "tcp", :from => 443, :to => 443, :cidr => "0.0.0.0/0" },
68
+ { :proto => "tcp", :from => 444, :to => 444, :cidr => "0.0.0.0/0" }
66
69
  ].freeze
67
70
  end
68
71
 
69
72
  def permission_exists?(group, perm)
70
73
  group.ip_permissions.find do |p|
71
- p['ipProtocol'] == perm[:proto] &&
72
- p['fromPort'] == perm[:from] &&
73
- p['toPort'] == perm[:to]
74
+ p["ipProtocol"] == perm[:proto] &&
75
+ p["fromPort"] == perm[:from] &&
76
+ p["toPort"] == perm[:to]
74
77
  end
75
78
  end
76
79
 
77
- def permission_options(group, opts)
80
+ def permission_options(group, opts) # rubocop:disable Metrics/MethodLength
78
81
  options = {
79
- 'IpPermissions' => [
82
+ "IpPermissions" => [
80
83
  {
81
- 'IpProtocol' => opts[:proto],
82
- 'FromPort' => opts[:from],
83
- 'ToPort' => opts[:to]
84
+ "IpProtocol" => opts[:proto],
85
+ "FromPort" => opts[:from],
86
+ "ToPort" => opts[:to]
84
87
  }
85
88
  ]
86
89
  }
87
90
 
88
91
  if opts[:cidr]
89
- options['IpPermissions'].first['IpRanges'] = [
92
+ options["IpPermissions"].first["IpRanges"] = [
90
93
  {
91
- 'CidrIp' => opts[:cidr]
94
+ "CidrIp" => opts[:cidr]
92
95
  }
93
96
  ]
94
97
  else
95
- options['IpPermissions'].first['Groups'] = [
98
+ options["IpPermissions"].first["Groups"] = [
96
99
  {
97
- 'GroupName' => group.name,
98
- 'UserId' => group.owner_id
100
+ "GroupName" => group.name,
101
+ "UserId" => group.owner_id
99
102
  }
100
103
  ]
101
104
  end
@@ -1,3 +1,4 @@
1
+ # -*- encoding: utf-8 -*-
1
2
  #
2
3
  # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
3
4
  # Copyright:: Copyright (c) 2012 Fletcher Nichol
@@ -16,13 +17,15 @@
16
17
  # limitations under the License.
17
18
  #
18
19
 
19
- require 'net/ssh'
20
+ require "net/ssh"
20
21
 
21
22
  module Knife
22
23
  module Server
24
+ # Communicates with an SSH node.
23
25
  class SSH
24
26
  DEFAULT_OPTIONS = { :user => "root", :port => "22" }.freeze
25
- USER_SWITCH_COMMAND = %[sudo USER=root HOME="$(getent passwd root | cut -d : -f 6)"]
27
+ USER_SWITCH_COMMAND =
28
+ %{sudo USER=root HOME="$(getent passwd root | cut -d : -f 6)"}.freeze
26
29
 
27
30
  def initialize(params)
28
31
  options = DEFAULT_OPTIONS.merge(params)
@@ -33,20 +36,49 @@ module Knife
33
36
  end
34
37
 
35
38
  def exec!(cmd)
39
+ result = ""
40
+ exit_code = nil
41
+ Net::SSH.start(@host, @user, @options) do |session|
42
+ exit_code = ssh_session(session, full_cmd(cmd), result)
43
+ end
44
+ if exit_code != 0
45
+ raise "SSH exited with code #{exit_code} for [#{full_cmd(cmd)}]"
46
+ end
47
+ result
48
+ end
49
+
50
+ def full_cmd(cmd)
36
51
  if @user == "root"
37
- full_cmd = cmd
52
+ cmd
38
53
  else
39
- full_cmd = [
40
- USER_SWITCH_COMMAND,
41
- %[bash -c '#{cmd}']
42
- ].join(" ")
54
+ [USER_SWITCH_COMMAND, %{bash -c '#{cmd}'}].join(" ")
43
55
  end
56
+ end
44
57
 
45
- result = ""
46
- Net::SSH.start(@host, @user, @options) do |ssh|
47
- result = ssh.exec!(full_cmd)
58
+ def ssh_session(session, cmd, result)
59
+ exit_code = nil
60
+ session.open_channel do |channel|
61
+
62
+ channel.request_pty
63
+
64
+ channel.exec(cmd) do |_ch, _success|
65
+
66
+ channel.on_data do |_ch, data|
67
+ result << data
68
+ end
69
+
70
+ channel.on_extended_data do |_ch, _type, data|
71
+ result << data
72
+ end
73
+
74
+ channel.on_request("exit-status") do |_ch, data|
75
+ exit_code = data.read_long
76
+ end
77
+ end
48
78
  end
49
- result
79
+
80
+ session.loop
81
+ exit_code
50
82
  end
51
83
 
52
84
  # runs a script on the target host by passing it to the stdin of a sh
@@ -71,26 +103,30 @@ module Knife
71
103
  fi
72
104
  EOF
73
105
 
106
+ exec_ssh(wrapper, content)
107
+ end
108
+
109
+ def exec_ssh(wrapper, content) # rubocop:disable Metrics/MethodLength
74
110
  result = ""
75
111
  exit_status = nil
76
112
 
77
113
  Net::SSH.start(@host, @user, @options) do |ssh|
78
114
  ssh.open_channel do |ch|
79
- ch.on_open_failed do |ch, code, desc|
115
+ ch.on_open_failed do |_, _, desc|
80
116
  raise "Connection Error to #{ip}: #{desc}"
81
117
  end
82
118
 
83
- ch.exec(wrapper) do |channel, type, data|
84
- # spit out the shell script and close stdin so sh can do its magic.
119
+ ch.exec(wrapper) do |channel, _, _|
120
+ # spit out the shell script and close stdin so sh can do its magic
85
121
  channel.send_data(content)
86
122
  channel.eof!
87
123
 
88
- # then we just wait for sweet, sweet output.
89
- channel.on_data do |ch2, data|
124
+ # then we just wait for sweet, sweet output
125
+ channel.on_data do |_, data|
90
126
  result << data
91
127
  end
92
128
 
93
- channel.on_request("exit-status") do |ch2, data|
129
+ channel.on_request("exit-status") do |_, data|
94
130
  exit_status = data.read_long
95
131
  end
96
132
  end
@@ -101,7 +137,7 @@ module Knife
101
137
  ssh.loop
102
138
  end
103
139
 
104
- return result, exit_status
140
+ [result, exit_status]
105
141
  end
106
142
  end
107
143
  end
@@ -1,3 +1,4 @@
1
+ # -*- encoding: utf-8 -*-
1
2
  #
2
3
  # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
3
4
  # Copyright:: Copyright (c) 2012 Fletcher Nichol
@@ -18,6 +19,6 @@
18
19
 
19
20
  module Knife
20
21
  module Server
21
- VERSION = "1.1.0"
22
+ VERSION = "1.2.0"
22
23
  end
23
24
  end
@@ -1,3 +1,4 @@
1
+ # -*- encoding: utf-8 -*-
1
2
  #
2
3
  # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
3
4
  # Copyright:: Copyright (c) 2012 Fletcher Nichol
@@ -16,9 +17,9 @@
16
17
  # limitations under the License.
17
18
  #
18
19
 
19
- require 'chef/knife/server_backup'
20
- require 'fakefs/spec_helpers'
21
- require 'timecop'
20
+ require "chef/knife/server_backup"
21
+ require "fakefs/spec_helpers"
22
+ require "timecop"
22
23
 
23
24
  describe Chef::Knife::ServerBackup do
24
25
  include FakeFS::SpecHelpers
@@ -27,10 +28,10 @@ describe Chef::Knife::ServerBackup do
27
28
  Chef::Log.logger = Logger.new(StringIO.new)
28
29
  @knife = Chef::Knife::ServerBackup.new
29
30
  @stdout = StringIO.new
30
- @knife.ui.stub!(:stdout).and_return(@stdout)
31
- @knife.ui.stub(:msg)
31
+ allow(@knife.ui).to receive(:stdout).and_return(@stdout)
32
+ allow(@knife.ui).to receive(:msg)
32
33
  @stderr = StringIO.new
33
- @knife.ui.stub!(:stderr).and_return(@stderr)
34
+ allow(@knife.ui).to receive(:stderr).and_return(@stderr)
34
35
  @knife.config[:backup_dir] = "/baks"
35
36
 
36
37
  Chef::Config[:chef_server_url] = "https://chef.example.com:9876"
@@ -50,7 +51,7 @@ describe Chef::Knife::ServerBackup do
50
51
  @knife.config[:backup_dir] = nil
51
52
  Chef::Config[:file_backup_path] = "/da/bomb"
52
53
 
53
- @knife.backup_dir.should eq(
54
+ expect(@knife.backup_dir).to eq(
54
55
  "/da/bomb/chef.example.com_20120102T030405-0000")
55
56
  end
56
57
  end
@@ -64,35 +65,37 @@ describe Chef::Knife::ServerBackup do
64
65
  let(:data_bag_item_list) { Hash["myitem" => "http://p/bags/mybag/myitem"] }
65
66
 
66
67
  before do
67
- Chef::Node.stub(:list) { node_list }
68
- Chef::Node.stub(:load).with("mynode") { stub_node("mynode") }
69
- Chef::Role.stub(:list) { role_list }
70
- Chef::Role.stub(:load).with("myrole") { stub_role("myrole") }
71
- Chef::Environment.stub(:list) { env_list }
72
- Chef::Environment.stub(:load).with("myenv") { stub_env("myenv") }
73
- Chef::DataBag.stub(:list) { data_bag_list }
74
- Chef::DataBag.stub(:load).with("mybag") { data_bag_item_list }
75
- Chef::DataBagItem.stub(:load).
76
- with("mybag", "myitem") { stub_bag_item("mybag", "myitem")}
68
+ allow(Chef::Node).to receive(:list) { node_list }
69
+ allow(Chef::Node).to receive(:load).with("mynode") { stub_node("mynode") }
70
+ allow(Chef::Role).to receive(:list) { role_list }
71
+ allow(Chef::Role).to receive(:load).with("myrole") { stub_role("myrole") }
72
+ allow(Chef::Environment).to receive(:list) { env_list }
73
+ allow(Chef::Environment).to receive(:load).
74
+ with("myenv") { stub_env("myenv") }
75
+ allow(Chef::DataBag).to receive(:list) { data_bag_list }
76
+ allow(Chef::DataBag).to receive(:load).
77
+ with("mybag") { data_bag_item_list }
78
+ allow(Chef::DataBagItem).to receive(:load).
79
+ with("mybag", "myitem") { stub_bag_item("mybag", "myitem") }
77
80
  end
78
81
 
79
82
  it "exits if component type is invalid" do
80
- @knife.name_args = %w{nodes toasterovens}
83
+ @knife.name_args = %w[nodes toasterovens]
81
84
 
82
- lambda { @knife.run }.should raise_error SystemExit
85
+ expect { @knife.run }.to raise_error SystemExit
83
86
  end
84
87
 
85
88
  context "for nodes" do
86
- before { @knife.name_args = %w{nodes} }
89
+ before { @knife.name_args = %w[nodes] }
87
90
 
88
91
  it "creates the backup nodes dir" do
89
92
  @knife.run
90
93
 
91
- File.directory?(["/baks", "nodes"].join("/")).should be_true
94
+ expect(File.directory?(["/baks", "nodes"].join("/"))).to be_truthy
92
95
  end
93
96
 
94
97
  it "sends a message to the ui" do
95
- @knife.ui.should_receive(:msg).with(/mynode/)
98
+ expect(@knife.ui).to receive(:msg).with(/mynode/)
96
99
 
97
100
  @knife.run
98
101
  end
@@ -102,21 +105,22 @@ describe Chef::Knife::ServerBackup do
102
105
  json_str = File.open("/baks/nodes/mynode.json", "rb") { |f| f.read }
103
106
  json = JSON.parse(json_str, :create_additions => false)
104
107
 
105
- json["name"].should eq("mynode")
108
+ expect(json["name"]).to eq("mynode")
106
109
  end
107
110
  end
108
111
 
109
112
  context "for roles" do
110
- before { @knife.name_args = %w{roles} }
113
+ before { @knife.name_args = %w[roles] }
111
114
 
112
115
  it "creates the backup roles dir" do
113
116
  @knife.run
117
+ dir = File.join("/baks", "roles")
114
118
 
115
- File.directory?(["/baks", "roles"].join("/")).should be_true
119
+ expect(File.directory?(dir)).to be_truthy
116
120
  end
117
121
 
118
122
  it "sends a message to the ui" do
119
- @knife.ui.should_receive(:msg).with(/myrole/)
123
+ expect(@knife.ui).to receive(:msg).with(/myrole/)
120
124
 
121
125
  @knife.run
122
126
  end
@@ -126,63 +130,73 @@ describe Chef::Knife::ServerBackup do
126
130
  json_str = File.open("/baks/roles/myrole.json", "rb") { |f| f.read }
127
131
  json = JSON.parse(json_str, :create_additions => false)
128
132
 
129
- json["name"].should eq("myrole")
133
+ expect(json["name"]).to eq("myrole")
130
134
  end
131
135
  end
132
136
 
133
137
  context "for environments" do
134
- before { @knife.name_args = %w{environments} }
138
+ before { @knife.name_args = %w[environments] }
135
139
 
136
140
  it "creates the backup environments dir" do
137
141
  @knife.run
142
+ dir = File.join("/baks", "environments")
138
143
 
139
- File.directory?(["/baks", "environments"].join("/")).should be_true
144
+ expect(File.directory?(dir)).to be_truthy
140
145
  end
141
146
 
142
147
  it "sends a message to the ui" do
143
- @knife.ui.should_receive(:msg).with(/myenv/)
148
+ expect(@knife.ui).to receive(:msg).with(/myenv/)
144
149
 
145
150
  @knife.run
146
151
  end
147
152
 
148
153
  it "writes out each environment to a json file" do
149
154
  @knife.run
150
- json_str = File.open("/baks/environments/myenv.json", "rb") { |f| f.read }
155
+ json_str = File.open("/baks/environments/myenv.json", "rb") do |f|
156
+ f.read
157
+ end
151
158
  json = JSON.parse(json_str, :create_additions => false)
152
159
 
153
- json["name"].should eq("myenv")
160
+ expect(json["name"]).to eq("myenv")
154
161
  end
155
162
 
156
163
  it "skips the _default environment" do
157
- Chef::Environment.stub(:list) { Hash["_default" => "http://url"] }
158
- Chef::Environment.stub(:load).with("_default") { stub_env("_default") }
164
+ allow(Chef::Environment).to receive(:list) do
165
+ Hash["_default" => "http://url"]
166
+ end
167
+ allow(Chef::Environment).to receive(:load).with("_default") do
168
+ stub_env("_default")
169
+ end
159
170
  @knife.run
160
171
 
161
- File.exists?("/baks/environments/_default.json").should_not be_true
172
+ expect(File.exist?("/baks/environments/_default.json")).to_not be_truthy
162
173
  end
163
174
  end
164
175
 
165
176
  context "for data_bags" do
166
- before { @knife.name_args = %w{data_bags} }
177
+ before { @knife.name_args = %w[data_bags] }
167
178
 
168
179
  it "creates the backup data_bags dir" do
169
180
  @knife.run
181
+ dir = File.join("/baks", "data_bags")
170
182
 
171
- File.directory?(["/baks", "data_bags"].join("/")).should be_true
183
+ expect(File.directory?(dir)).to be_truthy
172
184
  end
173
185
 
174
186
  it "sends messages to the ui" do
175
- @knife.ui.should_receive(:msg).with(/myitem/)
187
+ expect(@knife.ui).to receive(:msg).with(/myitem/)
176
188
 
177
189
  @knife.run
178
190
  end
179
191
 
180
192
  it "writes out each data bag item to a json file" do
181
193
  @knife.run
182
- json_str = File.open("/baks/data_bags/mybag/myitem.json", "rb") { |f| f.read }
194
+ json_str = File.open("/baks/data_bags/mybag/myitem.json", "rb") do |f|
195
+ f.read
196
+ end
183
197
  json = JSON.parse(json_str, :create_additions => false)
184
198
 
185
- json["name"].should eq("data_bag_item_mybag_myitem")
199
+ expect(json["name"]).to eq("data_bag_item_mybag_myitem")
186
200
  end
187
201
  end
188
202
 
@@ -190,25 +204,25 @@ describe Chef::Knife::ServerBackup do
190
204
  it "writes a node file" do
191
205
  @knife.run
192
206
 
193
- File.exists?("/baks/nodes/mynode.json").should be_true
207
+ expect(File.exist?("/baks/nodes/mynode.json")).to be_truthy
194
208
  end
195
209
 
196
210
  it "writes a role file" do
197
211
  @knife.run
198
212
 
199
- File.exists?("/baks/roles/myrole.json").should be_true
213
+ expect(File.exist?("/baks/roles/myrole.json")).to be_truthy
200
214
  end
201
215
 
202
216
  it "writes an environment file" do
203
217
  @knife.run
204
218
 
205
- File.exists?("/baks/environments/myenv.json").should be_true
219
+ expect(File.exist?("/baks/environments/myenv.json")).to be_truthy
206
220
  end
207
221
 
208
222
  it "writes a data bag item file" do
209
223
  @knife.run
210
224
 
211
- File.exists?("/baks/data_bags/mybag/myitem.json").should be_true
225
+ expect(File.exist?("/baks/data_bags/mybag/myitem.json")).to be_truthy
212
226
  end
213
227
  end
214
228
  end