dust-deploy 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,5 +1,4 @@
1
- dust - a ssh/facter only server deployment tool
2
- =============
1
+ # dust - a ssh only server deployment tool
3
2
 
4
3
  dust is a deployment tool, kinda like sprinkle. but cooler (at least for me).
5
4
  it's for those, who would like to maintain their servers with a tool like puppet or chef, but are scared by the thought of having configuration files, credentials and other stuff centrally on a server reachable via the internet.
@@ -7,21 +6,19 @@ it's for those, who would like to maintain their servers with a tool like puppet
7
6
  although the tool is not as versatile and elite as puppet, it's still cool for most use cases, and easily extendable.
8
7
 
9
8
 
10
- installing
11
- ------------
9
+ ## installing
12
10
 
13
11
  installation is quite simple. just
14
12
 
15
13
  # gem install dust-deploy
16
14
 
17
15
 
18
- using
19
- ------------
16
+ ## quickstart
20
17
 
21
18
  let's start by creating a new directory skeleton
22
19
 
23
20
  $ dust new mynetwork
24
- - spawning new dust directory skeleton into 'mynetwork.dust' [ ok ]
21
+ - spawning new dust directory skeleton into 'mynetwork.dust' [ ok ]
25
22
 
26
23
  this will create a directory called mynetwork.dust, the nodes, templates and recipes subdirectories and will copy over example templates and node configurations. hop into your new dust directory and see what's going on:
27
24
 
@@ -53,208 +50,20 @@ you can then save the file, and tell dust to get to work:
53
50
 
54
51
  $ dust deploy
55
52
 
56
- [ yourhost.example.com ]
53
+ [ yourhost.example.com ]
57
54
 
58
55
  |packages|
59
- - checking if vim is installed [ ok ]
60
- - checking if git-core is installed [ failed ]
61
- - installing git-core [ ok ]
62
- - checking if rsync is installed [ ok ]
56
+ - checking if vim is installed [ ok ]
57
+ - checking if git-core is installed [ failed ]
58
+ - installing git-core [ ok ]
59
+ - checking if rsync is installed [ ok ]
63
60
 
64
61
  you should see dust connecting to the node, checking if the requested packages are installed, and if not, install them.
65
62
  dust works with aptitude, yum and emerge systems at the moment (testet with ubuntu, debian, gentoo, scientificlinux, centos).
66
63
  feel free to contribute to dust, so that your system is also supported. contribution is easy! just send me a github pull request. You can find the repository here: https://github.com/kechagia/dust-deploy
67
64
 
68
65
 
69
- inheritance
70
- ------------
71
-
72
- because sometimes you will have similar configuration files for multiple systems, you can create templates.
73
- i usually start filenames of templates with an underscore, but that's not a must.
74
-
75
- $ vi nodes/_default.yaml
76
-
77
- this template defines some general settings, usually used by most hosts
78
-
79
- domain: example.com
80
- port: 22
81
- user: root
82
-
83
-
84
- and another one:
85
-
86
- $ vi nodes/_debian.yaml
87
-
88
- in this template, i put in some debian specific settings
89
-
90
- # you can add custom fields like "group"
91
- # and filter on which hosts to deploy later
92
- group: debian
93
-
94
- recipes:
95
- locale: en_US.UTF-8
96
- debsecan: default
97
- repositories:
98
- default:
99
- url: "http://ftp.de.debian.org/debian/"
100
- components: "main contrib non-free"
101
-
102
-
103
- you can then inherit these templates in your yourhost.yaml:
104
-
105
- hostname: yourhost
106
- inherits: [ _default, _debian ]
107
-
108
- recipes:
109
- packages: [ 'vim', 'git-core', 'rsync' ]
110
-
111
-
112
- running dust now, will use the inherited settings as well.
113
- you can also overwrite settings in the template with the ones in yourhost.yaml
114
-
115
- **NOTE:** hashes will be deep merged with inherited hashes, other types will be overwritten!
116
-
117
- $ dust deploy
118
-
119
- [ yourhost ]
120
-
121
- |repositories|
122
- - determining whether node uses apt [ ok ]
123
- - deleting old repositories [ ok ]
124
-
125
- - deploying default repository [ ok ]
126
-
127
- |packages|
128
- - checking if vim is installed [ ok ]
129
- - checking if git-core is installed [ ok ]
130
- - checking if rsync is installed [ ok ]
131
-
132
- |locale|
133
- - setting locale to 'en_US.UTF-8' [ ok ]
134
-
135
- |debsecan|
136
- - checking if debsecan is installed [ ok ]
137
- - configuring debsecan [ ok ]
138
-
139
-
140
-
141
- filters and proxy
142
- ------------
143
-
144
- because that's not awesome enough, you can also filter your hosts using the --filter flag
145
-
146
- $ dust deploy --filter hostname:myhost-1,otherhost
147
-
148
- $ dust deploy --filter group:debian
149
-
150
-
151
- and even more, it supports socks proxys, so you can maintain your whole infrastructure without setting up a vpn from the outside via ssh
152
-
153
- $ ssh user@gateway.yourcompany.net -D 1080
154
-
155
- $ dust deploy --proxy localhost:1080
156
-
157
-
158
-
159
- using recipes (and their templates)
160
- ------------
161
-
162
- dust comes with a set of predifined, (almost) ready to use recipes managing a lot of stuff for you, including the following:
163
-
164
- - ssh authorized keys
165
- - email aliases file
166
- - /etc/hosts
167
- - /etc/motd
168
- - /etc/resolv.conf
169
- - install basic system tools and pushing .configuration files for root
170
- - iptables firewall
171
- - debian/ubuntu debsecan security notifications
172
- - debian/ubuntu repositories
173
- - duplicity backups
174
- - mysql server configuration
175
- - postgresql server configuration (including corosync scripts)
176
- - nginx configuration
177
- - zabbix agent
178
- - debian/ubuntu unattended upgrades
179
- - newrelic system monitoring daemon
180
-
181
-
182
- writing your own recipes
183
- ------------
184
-
185
- because the above recipes will probably not in all cases fulfill your needs, it's pretty easy to write your own recipes. You can either file them in using a git pull request (if you think it's a generic one which others might use as well), or place them locally in the "recipes" folder in your mynetwork.dust directory.
186
-
187
- dust comes with a set of predefined functions to perform system tasks, which you can (and should!) use.
188
-
189
- ### the server.rb methods you can (and should!) use
190
-
191
- almost all functions understand the quiet=true and indend=integer arguments
192
-
193
- #### exec command
194
- #### write target, text, quiet=false, indent=1
195
- #### append target, text, quiet=false, indent=1
196
- #### scp source, destination, quiet=false, indent=1
197
- #### symlink source, destination, quiet=false, indent=1
198
- #### chmod mode, file, quiet=false, indent=1
199
- #### chown user, file, quiet=false, indent=1
200
- #### rm file, quiet=false, indent=1
201
- #### mkdir dir, quiet=false, indent=1
202
- #### restorecon path, quiet=false, indent=1
203
- #### get_system_users quiet=false
204
- #### package_installed? packages, quiet=false, indent=1
205
- #### install_package package, quiet=false, indent=1, env=""
206
- #### remove_package package, quiet=false, indent=1
207
- #### update_repos quiet=false, indent=1
208
- #### system_update quiet=false, indent=1
209
- #### uses_apt? quiet=false, indent=1
210
- #### uses_rpm? quiet=false, indent=1
211
- #### uses_emerge? quiet=false, indent=1
212
- #### is_executable? file, quiet=false, indent=1
213
- #### file_exists? file, quiet=false, indent=1
214
- #### dir_exists? dir, quiet=false, indent=1
215
- #### autostart_service service, quiet=false, indent=1
216
- #### restart_service service, quiet=false, indent=1
217
- #### reload_service service, quiet=false, indent=1
218
- #### user_exists? user, quiet=false, indent=1
219
- #### create_user user, home=nil, shell=nil, quiet=false, indent=1
220
-
221
- #### collect_facts quiet=false, indent=1
222
- #### is_os? os_list, quiet=false, indent=1
223
- #### is_debian? quiet=false, indent=1
224
- #### is_ubuntu? quiet=false, indent=1
225
- #### is_gentoo? quiet=false, indent=1
226
- #### is_centos? quiet=false, indent=1
227
- #### is_scientific? quiet=false, indent=1
228
- #### is_fedora? quiet=false, indent=1
229
-
230
-
231
- ### example recipes
232
-
233
- The best is to have a look at dusts build-in recipes: https://github.com/kechagia/dust-deploy/tree/master/lib/dust/recipes
234
-
235
- this is the basic skeletton of a recipe file, placed in recipes/your_task.rb
236
-
237
- class YourTask < Thor
238
- desc 'your_task:deploy', 'example task: displays a message and does basically nothing'
239
- def deploy node, ingredients, options
240
-
241
- ::Dust.print_msg 'this is a test example. welcome! output of uname -a below:'
242
- puts node.exec('uname -a')[:stdout]
243
-
244
- node.uses_apt?
245
-
246
- node.restart_service 'your-service' if options.restart?
247
- end
248
-
249
- desc 'your_task:status', 'example status: displays the status of this recipe (optional)'
250
- def status, node, ingredients, options
251
- ::Dust.print_msg "displaying status of this example recipe!"
252
- end
253
- end
254
-
255
-
256
- contributing
257
- ------------
258
-
259
- you have a cool contribution or bugfix? yippie! just file in a pull-request!
66
+ ## documentation
260
67
 
68
+ for further documentation (this README only covers the very basics), head over to the github wiki:
69
+ https://github.com/kechagia/dust-deploy/wiki
data/bin/dust CHANGED
@@ -12,35 +12,77 @@ module Dust
12
12
 
13
13
  default_task :list
14
14
 
15
- desc 'deploy [server.yaml] [--filter key=value,value2] [--recipes recipe1 recipe2] [--proxy host:port]',
15
+ desc 'deploy [--yaml server.yaml] [--filter key=value,value2] [--recipes recipe1 recipe2] [--proxy host:port]',
16
16
  'deploy all recipes to the node(s) specified in server.yaml or to all nodes defined in ./nodes/'
17
17
 
18
- method_options :filter => :hash, :recipes => :array, :proxy => :string,
18
+ method_options :yaml => :string, :filter => :hash, :recipes => :array, :proxy => :string,
19
19
  :restart => :boolean, :reload => :boolean
20
20
 
21
- def deploy yaml=''
21
+ def deploy
22
22
  return unless check_dust_dir
23
23
  initialize_thorfiles
24
- Dust.print_failed 'no servers match this filter' if load_servers(yaml).empty?
24
+ Dust.print_failed 'no servers match this filter' if load_servers.empty?
25
25
 
26
26
  run_recipes 'deploy'
27
27
  end
28
28
 
29
29
 
30
- desc 'status [server.yaml] [--filter key=value,value2] [--recipes recipe1 recipe2] [--proxy host:port]',
30
+ desc 'status [--yaml server.yaml] [--filter key=value,value2] [--recipes recipe1 recipe2] [--proxy host:port]',
31
31
  'display status of recipes specified by filter'
32
32
 
33
- method_options :filter => :hash, :recipes => :array, :proxy => :string
33
+ method_options :yaml => :string, :filter => :hash, :recipes => :array, :proxy => :string
34
34
 
35
- def status yaml=''
35
+ def status
36
36
  return unless check_dust_dir
37
37
  initialize_thorfiles
38
- Dust.print_failed 'no servers match this filter' if load_servers(yaml).empty?
38
+ Dust.print_failed 'no servers match this filter' if load_servers.empty?
39
39
 
40
40
  run_recipes 'status'
41
41
  end
42
42
 
43
43
 
44
+ desc 'system_update [--yaml server.yaml] [--filter key=value,vale2] [--proxy host:port]',
45
+ 'perform a full system upgrade (using aptitude, emerge, yum)'
46
+
47
+ method_options :yaml => :string, :filter => :hash, :proxy => :string
48
+
49
+ def system_update
50
+ return unless check_dust_dir
51
+ initialize_thorfiles
52
+ Dust.print_failed 'no servers match this filter' if load_servers.empty?
53
+
54
+ @nodes.each do |node|
55
+ # connect to server
56
+ server = Server.new node
57
+ next unless server.connect
58
+ server.system_update
59
+ server.disconnect
60
+ end
61
+ end
62
+
63
+
64
+ desc 'exec <command> [--yaml server.yaml] [--filter key=value,vale2] [--proxy host:port]',
65
+ 'run a command on the server'
66
+
67
+ method_options :yaml => :string, :filter => :hash, :proxy => :string
68
+
69
+ def exec cmd, yaml=''
70
+ return unless check_dust_dir
71
+ initialize_thorfiles
72
+ Dust.print_failed 'no servers match this filter' if load_servers.empty?
73
+
74
+ @nodes.each do |node|
75
+ # connect to server
76
+ server = Server.new node
77
+ next unless server.connect
78
+ ret = server.exec cmd
79
+ puts "#{Dust.grey}#{ret[:stdout]}#{Dust.none}"
80
+ puts "#{Dust.red}#{ret[:stderr]}#{Dust.none}"
81
+ server.disconnect
82
+ end
83
+ end
84
+
85
+
44
86
  # creates directory skeleton for a dust setup
45
87
  desc 'new <name>', 'creates a dust directory skeleton for your network'
46
88
  def new name
@@ -122,17 +164,20 @@ module Dust
122
164
  end
123
165
 
124
166
  # loads servers
125
- def load_servers yaml=''
167
+ def load_servers
126
168
  @nodes = []
127
169
 
128
170
  # if the argument is empty, load all yaml files in the ./nodes/ directory
129
171
  # if the argument is a directory, load yaml files in this directory
130
172
  # if the argument is a file, load the file.
131
- if yaml.empty?
132
- yaml_files = Dir['./nodes/**/*.yaml']
173
+ if options[:yaml]
174
+ if File.directory? options[:yaml]
175
+ yaml_files = Dir["#{options[:yaml]}/**/*.yaml"]
176
+ elsif File.exists? options[:yaml]
177
+ yaml_files = options[:yaml]
178
+ end
133
179
  else
134
- yaml_files = Dir["#{yaml}/**/*.yaml"] if File.directory? yaml
135
- yaml_files = yaml if File.exists? yaml
180
+ yaml_files = Dir['./nodes/**/*.yaml']
136
181
  end
137
182
 
138
183
  unless yaml_files
data/changelog.md CHANGED
@@ -1,6 +1,19 @@
1
1
  Changelog
2
2
  =============
3
3
 
4
+ 0.3.0
5
+ ------------
6
+
7
+ refactoring was done, you may have to upgrade your recipes.
8
+ - options for all node.* functions having an options={:quiet => false, :indent => 1} paremeter now, instead of plain arguments
9
+ - adds 2 new dust options
10
+ - dust exec 'command' # executes a command on all servers
11
+ - dust system_update # runs a full system upgrade on all servers
12
+ - small adjustments and improvements
13
+
14
+ if you encounter bugs, please report.
15
+
16
+
4
17
  0.2.3
5
18
  ------------
6
19
 
@@ -12,7 +12,7 @@ RECOVERY=$PG_DATA/recovery.conf
12
12
  RECOVERY_DONE=$PG_DATA/recovery.done
13
13
 
14
14
  # path to postgresql init script
15
- % if node.is_gentoo? true
15
+ % if node.is_gentoo?
16
16
  PG_INIT=/etc/init.d/postgresql-<%= config['version'] %>
17
17
  % else
18
18
  PG_INIT=/etc/init.d/postgresql
@@ -59,22 +59,22 @@ DebugLevel=3
59
59
 
60
60
  # Name of PID file
61
61
 
62
- % if node.uses_apt? true
62
+ % if node.uses_apt?
63
63
  PidFile=/var/run/zabbix-agent/zabbix_agentd.pid
64
- % elsif node.uses_emerge? true
64
+ % elsif node.uses_emerge?
65
65
  PidFile=/var/run/zabbix/zabbix_agentd.pid
66
- % elsif node.uses_rpm? true
66
+ % elsif node.uses_rpm?
67
67
  PidFile=/var/run/zabbix/zabbix_agentd.pid
68
68
  % end
69
69
 
70
70
  # Name of log file.
71
71
  # If not set, syslog will be used
72
72
 
73
- % if node.uses_apt? true
73
+ % if node.uses_apt?
74
74
  LogFile=/var/log/zabbix-agent/zabbix_agentd.log
75
- % elsif node.uses_emerge? true
75
+ % elsif node.uses_emerge?
76
76
  LogFile=/var/log/zabbix/zabbix_agentd.log
77
- % elsif node.uses_emerge? true
77
+ % elsif node.uses_emerge?
78
78
  LogFile=/var/log/zabbix/zabbix_agentd.log
79
79
  % end
80
80
 
@@ -91,17 +91,17 @@ Timeout=30
91
91
  # Note that shell command must not return empty string or EOL only
92
92
 
93
93
  # system updates
94
- % if node.uses_apt? true
94
+ % if node.uses_apt?
95
95
  UserParameter=debian.updates,aptitude search '~U' |wc -l
96
96
  UserParameter=debian.security,debsecan --suite squeeze --only-fixed --format packages |wc -l
97
97
 
98
- % elsif node.uses_emerge? true
98
+ % elsif node.uses_emerge?
99
99
  UserParameter=gentoo.security,glsa-check -t all 2>/dev/null | wc -l
100
100
  UserParameter=gentoo.updates,emerge -uNDp @world | grep ebuild|wc -l
101
101
  UserParameter=gentoo.portage,emerge --info| grep 'Timestamp of tree' | sed -e s/'Timestamp of tree':// -e 's/\n//' | xargs -I {} date --date={} +%s |xargs -I {} expr $(date +%s) - {}
102
102
  UserParameter=gentoo.config,find /etc/ -name '._cfg*' 2>/dev/null|wc -l
103
103
 
104
- % elsif node.uses_rpm? true
104
+ % elsif node.uses_rpm?
105
105
  UserParameter=centos.updates,yum check-update -q |wc -l
106
106
  % end
107
107
 
@@ -12,45 +12,55 @@ module Dust
12
12
 
13
13
  $stdout.sync = true # autoflush
14
14
 
15
- def self.print_result ret, quiet=false
15
+ def self.print_result ret, options={:quiet => false, :indent => 1}
16
16
  if ret == 0 or ret == true
17
- print_ok unless quiet
17
+ print_ok '', options
18
18
  return true
19
19
  else
20
- print_failed unless quiet
20
+ print_failed '', options
21
21
  return false
22
22
  end
23
23
  end
24
24
 
25
- def self.print_ok string="", level=0
26
- print_msg "#{string} #{blue}[ ok ]#{none}\n", level
25
+ def self.print_ok string='', options={:quiet => false, :indent => 1}
26
+ options[:indent] = 0 if string.empty?
27
+ print_msg "#{string} #{blue}[ ok ]#{none}\n", options
28
+ true
27
29
  end
28
30
 
29
- def self.print_failed string="", level=0
30
- print_msg "#{string} #{red}[ failed ]#{none}\n", level
31
+ def self.print_failed string='', options={:quiet => false, :indent => 1}
32
+ options[:indent] = 0 if string.empty?
33
+ print_msg "#{string} #{red}[ failed ]#{none}\n", options
34
+ false
31
35
  end
32
36
 
33
- def self.print_warning string="", level=0
34
- print_msg "#{string} #{yellow}[ warning ]#{none}\n", level
37
+ def self.print_warning string='', options={:quiet => false, :indent => 1}
38
+ options[:indent] = 0 if string.empty?
39
+ print_msg "#{string} #{yellow}[ warning ]#{none}\n", options
35
40
  end
36
41
 
37
- def self.print_hostname hostname, level=0
38
- print_msg "\n[ #{blue}#{hostname}#{none} ]\n\n", level
42
+ def self.print_hostname hostname, options={:quiet => false, :indent => 0}
43
+ print_msg "\n[ #{blue}#{hostname}#{none} ]\n\n", options
39
44
  end
40
45
 
41
- def self.print_recipe recipe, level=0
42
- print_msg "#{green}|#{recipe}|#{none}\n", level
46
+ def self.print_recipe recipe, options={:quiet => false, :indent => 1}
47
+ print_msg "#{green}|#{recipe}|#{none}\n", options
43
48
  end
44
49
 
45
- # indent according to level
46
- # level 0
47
- # - level 1
48
- # - level 2
49
- def self.print_msg string, level=1
50
- if level == 0
50
+ # indent according to options[:indent]
51
+ # indent 0
52
+ # - indent 1
53
+ # - indent 2
54
+ def self.print_msg string, options={:quiet => false, :indent => 1}
55
+ # just return if in quiet mode
56
+ return if options[:quiet]
57
+
58
+ options[:indent] ||= 1
59
+
60
+ if options[:indent] == 0
51
61
  print string
52
62
  else
53
- print ' ' + ' ' * (level - 1) + '- ' + string
63
+ print ' ' + ' ' * (options[:indent] - 1) + '- ' + string
54
64
  end
55
65
  end
56
66