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