dust-deploy 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/changelog.md CHANGED
@@ -1,6 +1,27 @@
1
1
  Changelog
2
2
  =============
3
3
 
4
+ 0.7.0
5
+ ------------
6
+
7
+ - adds sudo support, you can now connect using an unpriviledged user. needs sudo rights, e.g.:
8
+ <username> ALL=(ALL) ALL
9
+
10
+ hostname: myhost
11
+ port: 22
12
+ user: myuser
13
+ password: mypass # not needed when connecting using ssh keys
14
+ sudo: true
15
+
16
+
17
+ - adds status command to most recipes (you can now watch the status of current recipes/daemons with 'dust status'
18
+ - node.restart/reload now tries systemd, upstart, sysvconfig and then uses initscript as fallback
19
+ - node.autostart_service now uses systemd on rpm systems (if available), falls back to chkconfig
20
+ - adds node.print_service_status method, used to retrieve daemon status
21
+ - adds new ::Dust.print_ret method, used to print stderr/stdout from node.exec in different colors
22
+ - mysql recipe now defaults to 0.7 of system ram for innodb buffer size, because 0.8 sometimes lead to oom situations
23
+
24
+
4
25
  0.6.2
5
26
  ------------
6
27
 
@@ -11,6 +32,7 @@ Changelog
11
32
  port: 6379
12
33
  daemonize: yes
13
34
 
35
+ - the redis recipe also supports the 'status' command
14
36
  - fixes hash_check recipe, now works with centos-like machines as well
15
37
  - improves mysql recipe: now sets shm sysctls as well (like the postgresql recipe does)
16
38
  - small improvements to automatic innodb tuning
@@ -50,6 +50,15 @@ module Dust
50
50
  print_msg "#{green}|#{recipe}|#{none}\n", options
51
51
  end
52
52
 
53
+ # prints stdout in grey and stderr in red (if existend)
54
+ def self.print_ret ret, options={:quiet => false, :indent => -1}
55
+ opts = options.clone
56
+
57
+ opts[:indent] += 1
58
+ print_msg "#{grey}#{ret[:stdout].chomp}#{none}\n", opts unless ret[:stdout].empty?
59
+ print_msg "#{red}#{ret[:stderr].chomp}#{none}\n", opts unless ret[:stderr].empty?
60
+ end
61
+
53
62
  # indent according to options[:indent]
54
63
  # indent 0
55
64
  # - indent 1
@@ -8,5 +8,13 @@ class Aliases < Recipe
8
8
  ::Dust.print_msg 'running newaliases'
9
9
  ::Dust.print_result @node.exec('newaliases')[:exit_code]
10
10
  end
11
+
12
+ desc 'aliases:status', 'shows current aliases'
13
+ def status
14
+ ::Dust.print_msg 'getting /etc/aliases'
15
+ ret = @node.exec 'cat /etc/aliases'
16
+ ::Dust.print_result ret[:exit_code]
17
+ ::Dust.print_ret ret
18
+ end
11
19
  end
12
20
 
@@ -9,6 +9,13 @@ class CupsClient < Recipe
9
9
  @node.write '/etc/cups/client.conf', "ServerName #{@config}\n"
10
10
  end
11
11
 
12
+ desc 'cups_client:status', 'shows current /etc/cups/client.conf'
13
+ def status
14
+ ::Dust.print_msg 'getting /etc/cups/client.conf'
15
+ ret = @node.exec 'cat /etc/cups/client.conf'
16
+ ::Dust.print_result ret[:exit_code]
17
+ ::Dust.print_ret ret
18
+ end
12
19
 
13
20
  private
14
21
 
@@ -9,5 +9,13 @@ class EtcHosts < Recipe
9
9
  @node.restart_service @config
10
10
  end
11
11
  end
12
+
13
+ desc 'etc_hosts:status', 'shows current /etc/hosts'
14
+ def status
15
+ ::Dust.print_msg 'getting /etc/hosts'
16
+ ret = @node.exec 'cat /etc/hosts'
17
+ ::Dust.print_result ret[:exit_code]
18
+ ::Dust.print_ret ret
19
+ end
12
20
  end
13
21
 
@@ -6,8 +6,8 @@ class HashCheck < Recipe
6
6
  keys = [ '*', '!', '!!', '', 'LK', 'NP' ]
7
7
 
8
8
  weak_passwords = File.open "#{@template_path}/weak_passwords", 'r'
9
- shadow = @node.exec('cat /etc/shadow')[:stdout]
10
9
 
10
+ shadow = @node.exec('getent shadow')[:stdout]
11
11
  ::Dust.print_msg "checking for weak password hashes\n"
12
12
 
13
13
  found_weak = false
@@ -37,13 +37,21 @@ class Iptables < Recipe
37
37
  end
38
38
  end
39
39
 
40
+ desc 'iptables:status', 'displays iptables rules'
41
+ def status
42
+ ::Dust.print_ok 'displaying iptables rules (ipv4)'
43
+ ::Dust.print_msg @node.exec('iptables -L -v -n')[:stdout], :indent => 0
44
+ puts
45
+ ::Dust.print_ok 'displaying iptables rules (ipv6)'
46
+ ::Dust.print_msg @node.exec('ip6tables -L -v -n')[:stdout], :indent => 0
47
+ end
40
48
 
41
49
  private
42
50
 
43
51
  # install iptables
44
52
  def install
45
53
  return false unless @node.install_package 'iptables'
46
- return false unless @node.install_package 'iptables-ipv6' if @node.uses_rpm?
54
+ return false unless @node.install_package 'iptables-ipv6' if @node.uses_rpm? and not @node.is_fedora?
47
55
  true
48
56
  end
49
57
 
@@ -17,5 +17,21 @@ class Locale < Recipe
17
17
  ::Dust.print_failed 'os not supported'
18
18
  end
19
19
  end
20
+
21
+ desc 'locale:status', 'shows current locale'
22
+ def status
23
+ ::Dust.print_msg 'getting current locale'
24
+
25
+ if @node.uses_apt?
26
+ ret = @node.exec 'cat /etc/default/locale'
27
+ elsif @node.uses_rpm?
28
+ ret = @node.exec 'cat /etc/sysconfig/i18n'
29
+ else
30
+ return ::Dust.print_failed
31
+ end
32
+
33
+ ::Dust.print_result ret[:exit_code]
34
+ ::Dust.print_ret ret
35
+ end
20
36
  end
21
37
 
@@ -3,4 +3,12 @@ class Motd < Recipe
3
3
  def deploy
4
4
  @node.deploy_file "#{@template_path}/motd", '/etc/motd', :binding => binding
5
5
  end
6
+
7
+ desc 'motd:status', 'shows current message of the day'
8
+ def status
9
+ ::Dust.print_msg 'getting /etc/motd'
10
+ ret = @node.exec 'cat /etc/motd'
11
+ ::Dust.print_result ret[:exit_code]
12
+ ::Dust.print_ret ret
13
+ end
6
14
  end
@@ -21,6 +21,12 @@ class Mysql < Recipe
21
21
  @node.reload_service 'mysql' if options.reload?
22
22
  end
23
23
 
24
+ desc 'mysql:status', 'displays status of the mysql daemon'
25
+ def status
26
+ return unless @node.package_installed? 'mysql-server'
27
+ @node.print_service_status 'mysql'
28
+ end
29
+
24
30
 
25
31
  private
26
32
 
@@ -82,7 +88,7 @@ class Mysql < Recipe
82
88
  system_mem = ::Dust.convert_size @node['memorysize']
83
89
 
84
90
  # allocate 80% of the available ram to mysql
85
- buffer_pool = (system_mem * 0.8).to_i
91
+ buffer_pool = (system_mem * 0.7).to_i
86
92
 
87
93
  ::Dust.print_ok
88
94
  "#{buffer_pool / 1024}M"
@@ -2,9 +2,9 @@ class Nginx < Recipe
2
2
  desc 'nginx:deploy', 'installs and configures nginx web server'
3
3
  def deploy
4
4
  # abort if nginx cannot be installed
5
- return unless @node.install_package('nginx')
5
+ return unless @node.install_package 'nginx'
6
6
 
7
- @node.scp("#{@template_path}/nginx.conf", '/etc/nginx/nginx.conf')
7
+ @node.scp "#{@template_path}/nginx.conf", '/etc/nginx/nginx.conf'
8
8
 
9
9
  # remove old sites that may be present
10
10
  ::Dust.print_msg 'deleting old sites in /etc/nginx/sites-*'
@@ -31,5 +31,11 @@ class Nginx < Recipe
31
31
  ::Dust.print_failed
32
32
  end
33
33
  end
34
+
35
+ desc 'nginx:status', 'displays nginx status'
36
+ def status
37
+ return unless @node.package_installed? 'nginx'
38
+ @node.print_service_status 'nginx'
39
+ end
34
40
  end
35
41
 
@@ -32,4 +32,12 @@ class Pacemaker < Recipe
32
32
  # not restarting automatically, because it provokes switching of ha services
33
33
  #@node.restart_service 'corosync' if @options.restart
34
34
  end
35
+
36
+ desc 'pacemaker:status', 'shows status of pacemaker/corosync cluster'
37
+ def status
38
+ ::Dust.print_msg 'running crm_mon'
39
+ ret = @node.exec 'crm_mon -1'
40
+ ::Dust.print_result ret[:exit_code]
41
+ ::Dust.print_ret ret
42
+ end
35
43
  end
@@ -22,7 +22,13 @@ class Postgres < Recipe
22
22
  @node.restart_service @config['service_name'] if options.restart?
23
23
  @node.reload_service @config['service_name'] if options.reload?
24
24
  end
25
-
25
+
26
+ desc 'postgres:status', 'displays status of postgres cluster'
27
+ def status
28
+ return unless @node.package_installed? [ 'postgresql-server', "postgresql-#{@config['version']}" ]
29
+ set_default_directories
30
+ @node.print_service_status @config['service_name']
31
+ end
26
32
 
27
33
  private
28
34
 
@@ -20,5 +20,13 @@ class RcLocal < Recipe
20
20
  ::Dust.print_failed 'os not supported'
21
21
  end
22
22
  end
23
+
24
+ desc 'rc_local:status', 'shows current /etc/rc.local'
25
+ def status
26
+ ::Dust.print_msg 'getting /etc/rc.local'
27
+ ret = @node.exec 'cat /etc/rc.local'
28
+ ::Dust.print_result ret[:exit_code]
29
+ ::Dust.print_ret ret
30
+ end
23
31
  end
24
32
 
@@ -10,7 +10,11 @@ class Redis < Recipe
10
10
  desc 'redis:status', 'displays redis-cli info'
11
11
  def status
12
12
  return false unless @node.package_installed? 'redis-server'
13
- puts @node.exec('redis-cli info')[:stdout]
13
+ ::Dust.print_msg 'running "redis-cli info"'
14
+ ret = @node.exec 'redis-cli info'
15
+ ::Dust.print_result ret[:exit_code]
16
+
17
+ ::Dust.print_msg ret[:stdout], :indent => 0 unless ret[:stdout].empty?
14
18
  end
15
19
 
16
20
 
@@ -88,4 +92,4 @@ class Redis < Recipe
88
92
  ::Dust.print_warning 'sysctl configuration not supported for your os'
89
93
  end
90
94
  end
91
- end
95
+ end
@@ -38,5 +38,13 @@ class ResolvConf < Recipe
38
38
 
39
39
  @node.write '/etc/resolv.conf', config_file
40
40
  end
41
+
42
+ desc 'resolv_conf:status', 'shows current /etc/resolv.conf'
43
+ def status
44
+ ::Dust.print_msg 'getting /etc/resolv.conf'
45
+ ret = @node.exec 'cat /etc/resolv.conf'
46
+ ::Dust.print_result ret[:exit_code]
47
+ ::Dust.print_ret ret
48
+ end
41
49
  end
42
50
 
@@ -13,6 +13,14 @@ class ZabbixAgent < Recipe
13
13
  @node.restart_service daemon if options.restart?
14
14
  end
15
15
 
16
+ desc 'zabbix_agent:status', 'displays status of the zabbix agent'
17
+ def status
18
+ daemon = @node.uses_emerge? ? 'zabbix-agentd' : 'zabbix-agent'
19
+ return unless @node.package_installed? daemon
20
+ @node.print_service_status daemon
21
+ end
22
+
23
+
16
24
  private
17
25
  # installs zabbix and its dependencies
18
26
  def install_zabbix
data/lib/dust/server.rb CHANGED
@@ -18,6 +18,7 @@ module Dust
18
18
  @node['user'] ||= 'root'
19
19
  @node['port'] ||= 22
20
20
  @node['password'] ||= ''
21
+ @node['sudo'] ||= false
21
22
  end
22
23
 
23
24
  def connect
@@ -50,15 +51,36 @@ module Dust
50
51
  end
51
52
 
52
53
  def exec command
54
+ sudo_authenticated = false
53
55
  stdout = ''
54
56
  stderr = ''
55
57
  exit_code = nil
56
58
  exit_signal = nil
57
-
59
+
58
60
  @ssh.open_channel do |channel|
61
+
62
+ # request a terminal (sudo needs it)
63
+ # and prepend "sudo"
64
+ if @node['sudo']
65
+ channel.request_pty
66
+ command = "sudo #{command}"
67
+ end
68
+
59
69
  channel.exec command do |ch, success|
60
70
  abort "FAILED: couldn't execute command (ssh.channel.exec)" unless success
61
- channel.on_data { |ch, data| stdout += data }
71
+
72
+ channel.on_data do |ch, data|
73
+ # only send password if sudo mode is enabled,
74
+ # sudo password string matches
75
+ # and only send password once in a session (trying to prevent attacks reading out the password)
76
+ if @node['sudo'] and data =~ /^\[sudo\] password for #{@node['user']}/ and not sudo_authenticated
77
+ channel.send_data "#{@node['password']}\n"
78
+ sudo_authenticated = true
79
+ else
80
+ stdout += data
81
+ end
82
+ end
83
+
62
84
  channel.on_extended_data { |ch, type, data| stderr += data }
63
85
  channel.on_request('exit-status') { |ch, data| exit_code = data.read_long }
64
86
  channel.on_request('exit-signal') { |ch, data| exit_signal = data.read_long }
@@ -66,10 +88,12 @@ module Dust
66
88
  end
67
89
 
68
90
  @ssh.loop
69
-
91
+
92
+ # sudo usage provokes a heading newline that's unwanted.
93
+ stdout.sub! /^(\r\n|\n|\r)/, '' if @node['sudo']
94
+
70
95
  { :stdout => stdout, :stderr => stderr, :exit_code => exit_code, :exit_signal => exit_signal }
71
96
  end
72
-
73
97
 
74
98
  def write destination, content, options = {}
75
99
  options = default_options.merge options
@@ -101,8 +125,29 @@ module Dust
101
125
  options = default_options.merge options
102
126
 
103
127
  Dust.print_msg "deploying #{File.basename source}", options
104
- @ssh.scp.upload! source, destination
105
- Dust.print_ok '', options
128
+
129
+ # if in sudo mode, copy file to temporary place, then move using sudo
130
+ if @node['sudo']
131
+ ret = exec 'mktemp --tmpdir dust.XXXXXXXXXX'
132
+ if ret[:exit_code] != 0
133
+ ::Dust.print_failed 'could not create temporary file (needed for sudo)'
134
+ return false
135
+ end
136
+
137
+ tmpfile = ret[:stdout].chomp
138
+
139
+ # allow user to write file without sudo (for scp)
140
+ # then change file back to root, and copy to the destination
141
+ chown @node['user'], tmpfile, :quiet => true
142
+ @ssh.scp.upload! source, tmpfile
143
+ chown 'root', tmpfile, :quiet => true
144
+ Dust.print_result exec("mv -f #{tmpfile} #{destination}")[:exit_code], options
145
+
146
+ else
147
+ @ssh.scp.upload! source, destination
148
+ Dust.print_ok '', options
149
+ end
150
+
106
151
  restorecon destination, options # restore SELinux labels
107
152
  end
108
153
 
@@ -272,12 +317,8 @@ module Dust
272
317
  return false
273
318
  end
274
319
 
275
- Dust.print_result ret[:exit_code], options
276
-
277
- # display stderr and stdout
278
- puts
279
- puts "#{Dust.grey}#{ret[:stdout]}#{Dust.none}"
280
- puts "#{Dust.red}#{ret[:stderr]}#{Dust.none}"
320
+ Dust.print_result ret[:exit_code], options
321
+ Dust.print_ret ret, options
281
322
  end
282
323
 
283
324
  # determining the system packet manager has to be done without facter
@@ -383,29 +424,69 @@ module Dust
383
424
  options = default_options.merge options
384
425
 
385
426
  Dust.print_msg "autostart #{service} on boot", options
427
+
386
428
  if uses_rpm?
387
- Dust.print_result exec("chkconfig #{service} on")[:exit_code], options
429
+ if file_exists? '/bin/systemctl', :quiet => true
430
+ Dust.print_result exec("systemctl enable #{service}.service")[:exit_code], options
431
+ else
432
+ Dust.print_result exec("chkconfig #{service} on")[:exit_code], options
433
+ end
434
+
388
435
  elsif uses_apt?
389
436
  Dust.print_result exec("update-rc.d #{service} defaults")[:exit_code], options
437
+
390
438
  elsif uses_emerge?
391
439
  Dust.print_result exec("rc-update add #{service} default")[:exit_code], options
392
440
  end
393
441
  end
394
-
442
+
443
+ # invoke 'command' on the service (e.g. @node.service 'postgresql', 'restart')
444
+ def service service, command, options = {}
445
+ options = default_options.merge options
446
+
447
+ return ::Dust.print_failed "service: '#{service}' unknown", options unless service.is_a? String
448
+
449
+ # try systemd, then upstart, then sysvconfig, then initscript
450
+ if file_exists? '/bin/systemctl', :quiet => true
451
+ Dust.print_msg "#{command}ing #{service} (via systemd)", options
452
+ ret = exec("systemctl #{command} #{service}.service")
453
+
454
+ elsif file_exists? "/etc/init/#{service}", :quiet => true
455
+ Dust.print_msg "#{command}ing #{service} (via upstart)", options
456
+ ret = exec("#{command} #{service}")
457
+
458
+ elsif file_exists? '/sbin/service', :quiet => true or file_exists? '/usr/sbin/service', :quiet => true
459
+ Dust.print_msg "#{command}ing #{service} (via sysvconfig)", options
460
+ ret = exec("service #{service} #{command}")
461
+
462
+ else
463
+ Dust.print_msg "#{command}ing #{service} (via initscript)", options
464
+ ret = exec("/etc/init.d/#{service} #{command}")
465
+ end
466
+
467
+ Dust.print_result ret[:exit_code], options
468
+ ret
469
+ end
470
+
395
471
  def restart_service service, options = {}
396
472
  options = default_options.merge options
397
473
 
398
- Dust.print_msg "restarting #{service}", options
399
- Dust.print_result exec("/etc/init.d/#{service} restart")[:exit_code], options
474
+ service service, 'restart', options
400
475
  end
401
-
476
+
402
477
  def reload_service service, options = {}
403
478
  options = default_options.merge options
404
479
 
405
- Dust.print_msg "reloading #{service}", options
406
- Dust.print_result exec("/etc/init.d/#{service} reload")[:exit_code], options
480
+ service service, 'reload', options
407
481
  end
408
-
482
+
483
+ def print_service_status service, options = {}
484
+ options = default_options.merge options
485
+ ret = service service, 'status', options
486
+ Dust.print_ret ret, options
487
+ ret
488
+ end
489
+
409
490
  # check whether a user exists on this node
410
491
  def user_exists? user, options = {}
411
492
  options = default_options.merge options
@@ -487,7 +568,7 @@ module Dust
487
568
 
488
569
 
489
570
  private
490
-
571
+
491
572
  def method_missing method, *args, &block
492
573
  # make server nodeibutes accessible via server.nodeibute
493
574
  if @node[method.to_s]
data/lib/dust/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Dust
2
- VERSION = "0.6.2"
2
+ VERSION = "0.7.0"
3
3
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 6
8
- - 2
9
- version: 0.6.2
7
+ - 7
8
+ - 0
9
+ version: 0.7.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - kris kechagia
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2012-02-02 00:00:00 +01:00
17
+ date: 2012-02-08 00:00:00 +01:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency