knife-server 1.1.0 → 1.2.0
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.
- checksums.yaml +4 -4
- data/.cane +1 -0
- data/.rspec +1 -0
- data/.rubocop.yml +3 -0
- data/.travis.yml +12 -8
- data/CHANGELOG.md +32 -1
- data/Gemfile +9 -4
- data/Guardfile +28 -0
- data/README.md +28 -5
- data/Rakefile +31 -10
- data/knife-server.gemspec +18 -8
- data/lib/chef/knife/bootstrap/_omnibus.sh +63 -10
- data/lib/chef/knife/bootstrap/chef10/rhel.erb +2 -0
- data/lib/chef/knife/bootstrap/chef11/omnibus.erb +4 -1
- data/lib/chef/knife/bootstrap/chef11/rhel.erb +2 -0
- data/lib/chef/knife/server_backup.rb +24 -10
- data/lib/chef/knife/server_bootstrap_base.rb +68 -23
- data/lib/chef/knife/server_bootstrap_ec2.rb +33 -20
- data/lib/chef/knife/server_bootstrap_linode.rb +20 -13
- data/lib/chef/knife/server_bootstrap_openstack.rb +128 -0
- data/lib/chef/knife/server_bootstrap_standalone.rb +28 -16
- data/lib/chef/knife/server_restore.rb +23 -9
- data/lib/knife-server.rb +1 -0
- data/lib/knife/server/credentials.rb +78 -42
- data/lib/knife/server/ec2_security_group.rb +24 -21
- data/lib/knife/server/ssh.rb +54 -18
- data/lib/knife/server/version.rb +2 -1
- data/spec/chef/knife/server_backup_spec.rb +58 -44
- data/spec/chef/knife/server_bootstrap_ec2_spec.rb +108 -80
- data/spec/chef/knife/server_bootstrap_linode_spec.rb +93 -64
- data/spec/chef/knife/server_bootstrap_openstack_spec.rb +305 -0
- data/spec/chef/knife/server_bootstrap_standalone_spec.rb +113 -76
- data/spec/chef/knife/server_restore_spec.rb +38 -37
- data/spec/knife/server/credientials_spec.rb +248 -51
- data/spec/knife/server/ec2_security_group_spec.rb +76 -68
- data/spec/knife/server/ssh_spec.rb +138 -22
- 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
|
-
[
|
61
|
-
{:proto =>
|
62
|
-
{:proto =>
|
63
|
-
{:proto =>
|
64
|
-
{:proto =>
|
65
|
-
{:proto =>
|
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[
|
72
|
-
p[
|
73
|
-
p[
|
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
|
-
|
82
|
+
"IpPermissions" => [
|
80
83
|
{
|
81
|
-
|
82
|
-
|
83
|
-
|
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[
|
92
|
+
options["IpPermissions"].first["IpRanges"] = [
|
90
93
|
{
|
91
|
-
|
94
|
+
"CidrIp" => opts[:cidr]
|
92
95
|
}
|
93
96
|
]
|
94
97
|
else
|
95
|
-
options[
|
98
|
+
options["IpPermissions"].first["Groups"] = [
|
96
99
|
{
|
97
|
-
|
98
|
-
|
100
|
+
"GroupName" => group.name,
|
101
|
+
"UserId" => group.owner_id
|
99
102
|
}
|
100
103
|
]
|
101
104
|
end
|
data/lib/knife/server/ssh.rb
CHANGED
@@ -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
|
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 =
|
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
|
-
|
52
|
+
cmd
|
38
53
|
else
|
39
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
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 |
|
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,
|
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 |
|
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 |
|
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
|
-
|
140
|
+
[result, exit_status]
|
105
141
|
end
|
106
142
|
end
|
107
143
|
end
|
data/lib/knife/server/version.rb
CHANGED
@@ -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
|
20
|
-
require
|
21
|
-
require
|
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.
|
31
|
-
@knife.ui.
|
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.
|
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.
|
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.
|
68
|
-
Chef::Node.
|
69
|
-
Chef::Role.
|
70
|
-
Chef::Role.
|
71
|
-
Chef::Environment.
|
72
|
-
Chef::Environment.
|
73
|
-
|
74
|
-
Chef::DataBag.
|
75
|
-
Chef::
|
76
|
-
with("mybag"
|
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
|
83
|
+
@knife.name_args = %w[nodes toasterovens]
|
81
84
|
|
82
|
-
|
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
|
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("/")).
|
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.
|
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"].
|
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
|
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?(
|
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.
|
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"].
|
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
|
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?(
|
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.
|
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")
|
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"].
|
160
|
+
expect(json["name"]).to eq("myenv")
|
154
161
|
end
|
155
162
|
|
156
163
|
it "skips the _default environment" do
|
157
|
-
Chef::Environment.
|
158
|
-
|
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.
|
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
|
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?(
|
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.
|
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")
|
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"].
|
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.
|
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.
|
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.
|
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.
|
225
|
+
expect(File.exist?("/baks/data_bags/mybag/myitem.json")).to be_truthy
|
212
226
|
end
|
213
227
|
end
|
214
228
|
end
|