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
@@ -0,0 +1,128 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Author:: John Bellone (<jbellone@bloomberg.net>)
|
4
|
+
# Copyright:: Copyright (c) 2014 Bloomberg Finance L.P.
|
5
|
+
# License:: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require "chef/knife/server_bootstrap_base"
|
21
|
+
|
22
|
+
class Chef
|
23
|
+
class Knife
|
24
|
+
# Provisions an OpenStack instance and sets up an Open Source Chef Server.
|
25
|
+
class ServerBootstrapOpenstack < Knife
|
26
|
+
|
27
|
+
banner "knife server bootstrap openstack (options)"
|
28
|
+
|
29
|
+
include Knife::ServerBootstrapBase
|
30
|
+
|
31
|
+
deps do
|
32
|
+
require "knife/server/ssh"
|
33
|
+
require "knife/server/credentials"
|
34
|
+
|
35
|
+
begin
|
36
|
+
require "chef/knife/openstack_server_create"
|
37
|
+
require "fog"
|
38
|
+
Chef::Knife::OpenstackServerCreate.load_deps
|
39
|
+
|
40
|
+
current_options = options
|
41
|
+
self.options = Chef::Knife::OpenstackServerCreate.options.dup
|
42
|
+
options.merge!(current_options)
|
43
|
+
rescue LoadError => ex
|
44
|
+
ui.error [
|
45
|
+
"Knife plugin knife-openstack could not be loaded.",
|
46
|
+
"Please add the knife-openstack gem to your Gemfile or",
|
47
|
+
"install the gem manually with `gem install knife-openstack'.",
|
48
|
+
"(#{ex.message})"
|
49
|
+
].join(" ")
|
50
|
+
exit 1
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def run
|
55
|
+
super
|
56
|
+
openstack_bootstrap.run
|
57
|
+
fetch_validation_key
|
58
|
+
create_root_client
|
59
|
+
install_client_key
|
60
|
+
end
|
61
|
+
|
62
|
+
def openstack_bootstrap
|
63
|
+
ENV["WEBUI_PASSWORD"] = config_val(:webui_password)
|
64
|
+
ENV["AMQP_PASSWORD"] = config_val(:amqp_password)
|
65
|
+
ENV["NO_TEST"] = "1" if config[:no_test]
|
66
|
+
bootstrap = Chef::Knife::OpenstackServerCreate.new
|
67
|
+
Chef::Knife::OpenstackServerCreate.options.keys.each do |attr|
|
68
|
+
val = config_val(attr)
|
69
|
+
next if val.nil?
|
70
|
+
|
71
|
+
bootstrap.config[attr] = val
|
72
|
+
end
|
73
|
+
bootstrap.config[:distro] = bootstrap_distro
|
74
|
+
bootstrap
|
75
|
+
end
|
76
|
+
|
77
|
+
def openstack_connection
|
78
|
+
@openstack_connection ||= Fog::Compute.new(
|
79
|
+
:provider => :openstack,
|
80
|
+
:openstack_username => config_val(:openstack_username),
|
81
|
+
:openstack_password => config_val(:openstack_password),
|
82
|
+
:openstack_auth_url => config_val(:openstack_auth_url),
|
83
|
+
:openstack_tenant => config_val(:openstack_tenant),
|
84
|
+
:openstack_region => config_val(:openstack_region)
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
def server_ip_address
|
89
|
+
server = openstack_connection.servers.find do |s|
|
90
|
+
s.status == 1 && s.name == config_val(:openstack_node_name)
|
91
|
+
end
|
92
|
+
|
93
|
+
server && server.public_ip_address
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def validate!
|
99
|
+
super
|
100
|
+
|
101
|
+
if config[:chef_node_name].nil?
|
102
|
+
ui.error "You did not provide a valid --node-name value."
|
103
|
+
exit 1
|
104
|
+
end
|
105
|
+
if config_val(:platform) == "auto"
|
106
|
+
ui.error "Auto platform cannot be used with knife-openstack plugin"
|
107
|
+
exit 1
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def ssh_connection
|
112
|
+
opts = {
|
113
|
+
:host => server_ip_address,
|
114
|
+
:user => config_val(:ssh_user),
|
115
|
+
:port => config_val(:ssh_port),
|
116
|
+
:keys => [config_val(:identity_file)].compact,
|
117
|
+
:password => config_val(:ssh_password)
|
118
|
+
}
|
119
|
+
if config_val(:host_key_verify) == false
|
120
|
+
opts[:user_known_hosts_file] = "/dev/null"
|
121
|
+
opts[:paranoid] = false
|
122
|
+
end
|
123
|
+
|
124
|
+
::Knife::Server::SSH.new(opts)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
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,10 +17,12 @@
|
|
16
17
|
# limitations under the License.
|
17
18
|
#
|
18
19
|
|
19
|
-
require
|
20
|
+
require "chef/knife/server_bootstrap_base"
|
20
21
|
|
21
22
|
class Chef
|
22
23
|
class Knife
|
24
|
+
# Provisions a standalone server that is reachable on the network and
|
25
|
+
# sets up an Open Source Chef Server.
|
23
26
|
class ServerBootstrapStandalone < Knife
|
24
27
|
|
25
28
|
banner "knife server bootstrap standalone (options)"
|
@@ -27,14 +30,14 @@ class Chef
|
|
27
30
|
include Knife::ServerBootstrapBase
|
28
31
|
|
29
32
|
deps do
|
30
|
-
require
|
31
|
-
require
|
32
|
-
require
|
33
|
+
require "knife/server/ssh"
|
34
|
+
require "knife/server/credentials"
|
35
|
+
require "chef/knife/bootstrap"
|
33
36
|
Chef::Knife::Bootstrap.load_deps
|
34
37
|
|
35
|
-
current_options =
|
38
|
+
current_options = options
|
36
39
|
self.options = Chef::Knife::Bootstrap.options.dup
|
37
|
-
|
40
|
+
options.merge!(current_options)
|
38
41
|
end
|
39
42
|
|
40
43
|
option :host,
|
@@ -43,7 +46,7 @@ class Chef
|
|
43
46
|
:description => "Hostname or IP address of host to bootstrap"
|
44
47
|
|
45
48
|
def run
|
46
|
-
|
49
|
+
super
|
47
50
|
check_ssh_connection
|
48
51
|
standalone_bootstrap.run
|
49
52
|
fetch_validation_key
|
@@ -52,23 +55,26 @@ class Chef
|
|
52
55
|
end
|
53
56
|
|
54
57
|
def standalone_bootstrap
|
55
|
-
|
56
|
-
ENV['AMQP_PASSWORD'] = config_val(:amqp_password)
|
57
|
-
ENV['NO_TEST'] = "1" if config[:no_test]
|
58
|
+
setup_environment
|
58
59
|
bootstrap = Chef::Knife::Bootstrap.new
|
59
|
-
bootstrap.name_args = [
|
60
|
+
bootstrap.name_args = [config[:host]]
|
60
61
|
Chef::Knife::Bootstrap.options.keys.each do |attr|
|
61
|
-
|
62
|
+
val = config_val(attr)
|
63
|
+
next if val.nil?
|
64
|
+
|
65
|
+
bootstrap.config[attr] = val
|
62
66
|
end
|
63
|
-
bootstrap.ui =
|
67
|
+
bootstrap.ui = ui
|
64
68
|
bootstrap.config[:distro] = bootstrap_distro
|
65
|
-
bootstrap.config[:use_sudo] = true
|
69
|
+
bootstrap.config[:use_sudo] = true if config_val(:ssh_user) != "root"
|
66
70
|
bootstrap
|
67
71
|
end
|
68
72
|
|
69
73
|
private
|
70
74
|
|
71
75
|
def validate!
|
76
|
+
super
|
77
|
+
|
72
78
|
if config[:chef_node_name].nil?
|
73
79
|
ui.error "You did not provide a valid --node-name value."
|
74
80
|
exit 1
|
@@ -79,11 +85,17 @@ class Chef
|
|
79
85
|
end
|
80
86
|
end
|
81
87
|
|
88
|
+
def setup_environment
|
89
|
+
ENV["WEBUI_PASSWORD"] = config_val(:webui_password)
|
90
|
+
ENV["AMQP_PASSWORD"] = config_val(:amqp_password)
|
91
|
+
ENV["NO_TEST"] = "1" if config[:no_test]
|
92
|
+
end
|
93
|
+
|
82
94
|
def check_ssh_connection
|
83
95
|
ssh_connection.exec! "hostname -f"
|
84
96
|
rescue Net::SSH::AuthenticationFailed
|
85
|
-
ui.warn("Failed to authenticate #{config_val(:ssh_user)} - "
|
86
|
-
|
97
|
+
ui.warn("Failed to authenticate #{config_val(:ssh_user)} - " \
|
98
|
+
"trying password auth")
|
87
99
|
config[:ssh_password] = ui.ask(
|
88
100
|
"Enter password for #{config_val(:ssh_user)}@#{config_val(:host)}: "
|
89
101
|
) { |q| q.echo = false }
|
@@ -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,14 +17,15 @@
|
|
16
17
|
# limitations under the License.
|
17
18
|
#
|
18
19
|
|
19
|
-
require
|
20
|
+
require "chef/knife"
|
20
21
|
|
21
22
|
class Chef
|
22
23
|
class Knife
|
24
|
+
# Restores Chef data primitives from JSON backups to a Chef Server.
|
23
25
|
class ServerRestore < Knife
|
24
26
|
|
25
27
|
deps do
|
26
|
-
require
|
28
|
+
require "chef/json_compat"
|
27
29
|
end
|
28
30
|
|
29
31
|
banner "knife server restore COMPONENT[ COMPONENT ...] (options)"
|
@@ -43,11 +45,23 @@ class Chef
|
|
43
45
|
private
|
44
46
|
|
45
47
|
COMPONENTS = {
|
46
|
-
"nodes" => {
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
"nodes" => {
|
49
|
+
:singular => "node",
|
50
|
+
:klass => Chef::Node
|
51
|
+
},
|
52
|
+
"roles" => {
|
53
|
+
:singular => "role",
|
54
|
+
:klass => Chef::Role
|
55
|
+
},
|
56
|
+
"environments" => {
|
57
|
+
:singular => "environment",
|
58
|
+
:klass => Chef::Environment
|
59
|
+
},
|
60
|
+
"data_bags" => {
|
61
|
+
:singular => "data_bag",
|
62
|
+
:klass => Chef::DataBag
|
63
|
+
}
|
64
|
+
}.freeze
|
51
65
|
|
52
66
|
def validate!
|
53
67
|
bad_names = name_args.reject { |c| COMPONENTS.keys.include?(c) }
|
@@ -77,7 +91,7 @@ class Chef
|
|
77
91
|
|
78
92
|
if c[:klass] == Chef::DataBag
|
79
93
|
create_data_bag(::File.basename(::File.dirname(json_file)))
|
80
|
-
msg = "Restoring #{c[:singular]}"
|
94
|
+
msg = "Restoring #{c[:singular]}" \
|
81
95
|
"[#{obj.data_bag}][#{obj.raw_data[:id]}]"
|
82
96
|
else
|
83
97
|
msg = "Restoring #{c[:singular]}[#{obj.name}]"
|
@@ -92,7 +106,7 @@ class Chef
|
|
92
106
|
|
93
107
|
unless @created_data_bags.include?(name)
|
94
108
|
ui.msg "Restoring data_bag[#{name}]"
|
95
|
-
rest.post_rest("data",
|
109
|
+
rest.post_rest("data", "name" => name)
|
96
110
|
@created_data_bags << name
|
97
111
|
end
|
98
112
|
end
|
data/lib/knife-server.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,68 +17,47 @@
|
|
16
17
|
# limitations under the License.
|
17
18
|
#
|
18
19
|
|
19
|
-
require
|
20
|
+
require "fileutils"
|
21
|
+
require "openssl"
|
20
22
|
|
21
23
|
module Knife
|
22
24
|
module Server
|
25
|
+
# Creates credentials for a Chef server.
|
23
26
|
class Credentials
|
24
27
|
def initialize(ssh, validation_key_path, options = {})
|
25
28
|
@ssh = ssh
|
26
29
|
@validation_key_path = validation_key_path
|
27
30
|
@omnibus = options[:omnibus]
|
31
|
+
@io = options.delete(:io) || $stdout
|
28
32
|
end
|
29
33
|
|
30
34
|
def install_validation_key(suffix = Time.now.to_i)
|
31
|
-
|
32
|
-
|
33
|
-
|
35
|
+
dest = @validation_key_path
|
36
|
+
backup = backup_file_path(@validation_key_path, suffix)
|
37
|
+
|
38
|
+
if File.exist?(dest)
|
39
|
+
info "Creating backup of #{dest} locally at #{backup}"
|
40
|
+
FileUtils.cp(dest, backup)
|
34
41
|
end
|
35
42
|
|
36
43
|
chef10_key = "/etc/chef/validation.pem"
|
37
44
|
omnibus_key = "/etc/chef-server/chef-validator.pem"
|
38
45
|
|
39
|
-
|
46
|
+
info "Installing validation private key locally at #{dest}"
|
47
|
+
File.open(dest, "wb") do |f|
|
40
48
|
f.write(@ssh.exec!("cat #{omnibus? ? omnibus_key : chef10_key}"))
|
41
49
|
end
|
42
50
|
end
|
43
51
|
|
44
52
|
def create_root_client
|
45
|
-
|
46
|
-
"knife configure",
|
47
|
-
"--initial",
|
48
|
-
"--server-url http://127.0.0.1:4000",
|
49
|
-
"--user root",
|
50
|
-
'--repository ""',
|
51
|
-
"--defaults --yes"
|
52
|
-
].join(" ")
|
53
|
-
|
54
|
-
omnibus_cmd = [
|
55
|
-
"echo '#{ENV['WEBUI_PASSWORD']}' |",
|
56
|
-
"knife configure",
|
57
|
-
"--initial",
|
58
|
-
"--server-url http://127.0.0.1:8000",
|
59
|
-
"--user root",
|
60
|
-
'--repository ""',
|
61
|
-
"--admin-client-name chef-webui",
|
62
|
-
"--admin-client-key /etc/chef-server/chef-webui.pem",
|
63
|
-
"--validation-client-name chef-validator",
|
64
|
-
"--validation-key /etc/chef-server/chef-validator.pem",
|
65
|
-
"--defaults --yes"
|
66
|
-
].join(" ")
|
67
|
-
|
68
|
-
@ssh.exec!(omnibus? ? omnibus_cmd : chef10_cmd)
|
53
|
+
@ssh.exec!(omnibus? ? client_omnibus_cmd : client_chef10_cmd)
|
69
54
|
end
|
70
55
|
|
71
56
|
def install_client_key(user, client_key_path, suffix = Time.now.to_i)
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
backup_file_path(client_key_path, suffix))
|
77
|
-
end
|
78
|
-
|
79
|
-
File.open(client_key_path, "wb") do |f|
|
80
|
-
f.write(@ssh.exec!("cat /tmp/chef-client-#{user}.pem"))
|
57
|
+
if omnibus? && File.exist?(client_key_path)
|
58
|
+
use_current_client_key(user, client_key_path)
|
59
|
+
else
|
60
|
+
create_new_client_key(user, client_key_path, suffix)
|
81
61
|
end
|
82
62
|
|
83
63
|
@ssh.exec!("rm -f /tmp/chef-client-#{user}.pem")
|
@@ -85,8 +65,12 @@ module Knife
|
|
85
65
|
|
86
66
|
private
|
87
67
|
|
68
|
+
def info(msg)
|
69
|
+
@io.puts "-----> #{msg}"
|
70
|
+
end
|
71
|
+
|
88
72
|
def omnibus?
|
89
|
-
@omnibus
|
73
|
+
@omnibus ? true : false
|
90
74
|
end
|
91
75
|
|
92
76
|
def backup_file_path(file_path, suffix)
|
@@ -94,7 +78,7 @@ module Knife
|
|
94
78
|
"#{parts[0]}.#{suffix}.#{parts[2]}"
|
95
79
|
end
|
96
80
|
|
97
|
-
def create_user_client(user)
|
81
|
+
def create_user_client(user, is_private = false)
|
98
82
|
chef10_cmd = [
|
99
83
|
"knife client create",
|
100
84
|
user,
|
@@ -107,13 +91,65 @@ module Knife
|
|
107
91
|
"knife user create",
|
108
92
|
user,
|
109
93
|
"--admin",
|
110
|
-
"
|
94
|
+
"--#{is_private ? "user-key" : "file"} /tmp/chef-client-#{user}.pem",
|
111
95
|
"--disable-editing",
|
112
|
-
"--password #{ENV[
|
96
|
+
"--password #{ENV["WEBUI_PASSWORD"]}"
|
113
97
|
].join(" ")
|
114
98
|
|
115
99
|
@ssh.exec!(omnibus? ? omnibus_cmd : chef10_cmd)
|
116
100
|
end
|
101
|
+
|
102
|
+
def client_chef10_cmd
|
103
|
+
[
|
104
|
+
"knife configure",
|
105
|
+
"--initial",
|
106
|
+
"--server-url http://127.0.0.1:4000",
|
107
|
+
"--user root",
|
108
|
+
'--repository ""',
|
109
|
+
"--defaults --yes"
|
110
|
+
].join(" ")
|
111
|
+
end
|
112
|
+
|
113
|
+
def client_omnibus_cmd
|
114
|
+
[
|
115
|
+
"echo '#{ENV["WEBUI_PASSWORD"]}' |",
|
116
|
+
"knife configure",
|
117
|
+
"--initial",
|
118
|
+
"--server-url http://127.0.0.1:8000",
|
119
|
+
"--user root",
|
120
|
+
'--repository ""',
|
121
|
+
"--admin-client-name chef-webui",
|
122
|
+
"--admin-client-key /etc/chef-server/chef-webui.pem",
|
123
|
+
"--validation-client-name chef-validator",
|
124
|
+
"--validation-key /etc/chef-server/chef-validator.pem",
|
125
|
+
"--defaults --yes 2>> /tmp/chef-server-install-errors.txt"
|
126
|
+
].join(" ")
|
127
|
+
end
|
128
|
+
|
129
|
+
def use_current_client_key(user, private_key)
|
130
|
+
public_key = OpenSSL::PKey::RSA.new(
|
131
|
+
File.open(private_key, "rb") { |file| file.read }
|
132
|
+
).public_key.to_s
|
133
|
+
|
134
|
+
info "Uploading public key for pre-existing #{user} key"
|
135
|
+
@ssh.exec!(%{echo "#{public_key}" > /tmp/chef-client-#{user}.pem})
|
136
|
+
create_user_client(user, true)
|
137
|
+
end
|
138
|
+
|
139
|
+
def create_new_client_key(user, private_key, suffix)
|
140
|
+
create_user_client(user)
|
141
|
+
|
142
|
+
if File.exist?(private_key)
|
143
|
+
backup = backup_file_path(private_key, suffix)
|
144
|
+
info "Creating backup of #{private_key} locally at #{backup}"
|
145
|
+
FileUtils.cp(private_key, backup)
|
146
|
+
end
|
147
|
+
|
148
|
+
info "Installing #{user} private key locally at #{private_key}"
|
149
|
+
File.open(private_key, "wb") do |f|
|
150
|
+
f.write(@ssh.exec!("cat /tmp/chef-client-#{user}.pem"))
|
151
|
+
end
|
152
|
+
end
|
117
153
|
end
|
118
154
|
end
|
119
155
|
end
|