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.
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
@@ -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 'chef/knife/server_bootstrap_base'
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 'knife/server/ssh'
31
- require 'knife/server/credentials'
32
- require 'chef/knife/bootstrap'
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 = self.options
38
+ current_options = options
36
39
  self.options = Chef::Knife::Bootstrap.options.dup
37
- self.options.merge!(current_options)
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
- validate!
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
- ENV['WEBUI_PASSWORD'] = config_val(:webui_password)
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 = [ config[:host] ]
60
+ bootstrap.name_args = [config[:host]]
60
61
  Chef::Knife::Bootstrap.options.keys.each do |attr|
61
- bootstrap.config[attr] = config_val(attr)
62
+ val = config_val(attr)
63
+ next if val.nil?
64
+
65
+ bootstrap.config[attr] = val
62
66
  end
63
- bootstrap.ui = self.ui
67
+ bootstrap.ui = ui
64
68
  bootstrap.config[:distro] = bootstrap_distro
65
- bootstrap.config[:use_sudo] = true unless config_val(:ssh_user) == "root"
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
- "trying password auth")
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 'chef/knife'
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 'chef/json_compat'
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" => { :singular => "node", :klass => Chef::Node },
47
- "roles" => { :singular => "role", :klass => Chef::Role },
48
- "environments" => { :singular => "environment", :klass => Chef::Environment },
49
- "data_bags" => { :singular => "data_bag", :klass => Chef::DataBag },
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", { "name" => name })
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
@@ -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 'fileutils'
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
- if File.exists?(@validation_key_path)
32
- FileUtils.cp(@validation_key_path,
33
- backup_file_path(@validation_key_path, suffix))
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
- File.open(@validation_key_path, "wb") do |f|
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
- chef10_cmd = [
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
- create_user_client(user)
73
-
74
- if File.exists?(client_key_path)
75
- FileUtils.cp(client_key_path,
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
- "--file /tmp/chef-client-#{user}.pem",
94
+ "--#{is_private ? "user-key" : "file"} /tmp/chef-client-#{user}.pem",
111
95
  "--disable-editing",
112
- "--password #{ENV['WEBUI_PASSWORD']}"
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