dust-deploy 0.6.2 → 0.7.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.
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