dust-deploy 0.16.4 → 0.16.5

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.
@@ -0,0 +1,33 @@
1
+ require 'dust/server/ssh'
2
+ require 'dust/server/osdetect'
3
+ require 'dust/server/package'
4
+
5
+ module Dust
6
+ class Server
7
+ # collect additional system facts using puppets facter
8
+ def collect_facts(options={})
9
+ options = default_options.merge(options)
10
+
11
+ # if facts already have been collected, just return
12
+ return true if @node['operatingsystem']
13
+
14
+ # check if lsb-release (on apt systems) and facter are installed
15
+ # and install them if not
16
+ if uses_apt? and not package_installed?('lsb-release', :quiet => true)
17
+ install_package('lsb-release', :quiet => false)
18
+ end
19
+
20
+ unless package_installed?('facter', :quiet => true)
21
+ return false unless install_package('facter', :quiet => false)
22
+ end
23
+
24
+ msg = messages.add("collecting additional system facts (using facter)", options)
25
+
26
+ # run facter with -y for yaml output, and merge results into @node
27
+ ret = exec('facter -y')
28
+ @node = YAML.load(ret[:stdout]).merge(@node)
29
+
30
+ msg.parse_result(ret[:exit_code])
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,152 @@
1
+ require 'erb'
2
+ require 'tempfile'
3
+ require 'dust/server/ssh'
4
+ require 'dust/server/selinux'
5
+
6
+ module Dust
7
+ class Server
8
+ def write(destination, content, options={})
9
+ options = default_options.merge(options)
10
+
11
+ msg = messages.add("deploying #{File.basename destination}", options)
12
+
13
+ f = Tempfile.new('dust-write')
14
+ f.print(content)
15
+ f.close
16
+
17
+ ret = msg.parse_result(scp(f.path, destination, :quiet => true))
18
+ f.unlink
19
+
20
+ ret
21
+ end
22
+
23
+ def append(destination, newcontent, options={})
24
+ options = default_options.merge(options)
25
+
26
+ msg = messages.add("appending to #{File.basename destination}", options)
27
+
28
+ content = exec("cat #{destination}")[:stdout]
29
+ content.concat(newcontent)
30
+
31
+ msg.parse_result(write(destination, content, :quiet => true))
32
+ end
33
+
34
+ # if file is a regular file, copy it using scp
35
+ # if it's an file.erb exists, render template and push to server
36
+ def deploy_file(file, destination, options={})
37
+ options = default_options(:binding => binding).merge(options)
38
+
39
+ if File.exists?(file)
40
+ scp(file, destination, options)
41
+
42
+ elsif File.exists?("#{file}.erb")
43
+ template = ERB.new( File.read("#{file}.erb"), nil, '%<>')
44
+ write(destination, template.result(options[:binding]), options)
45
+
46
+ else
47
+ messages.add("'#{file}' was not found.", options).failed
48
+ end
49
+ end
50
+
51
+ # create a temporary file
52
+ def mktemp(options={:type => 'file'})
53
+ if options[:type] == 'file'
54
+ ret = exec('mktemp --tmpdir dust.XXXXXXXXXX')
55
+ elsif options[:type] == 'directory'
56
+ ret = exec('mktemp -d --tmpdir dust.XXXXXXXXXX')
57
+ else
58
+ return messages.add("mktemp: unknown type '#{options[:type]}'").failed
59
+ end
60
+
61
+ return false if ret[:exit_code] != 0
62
+ ret[:stdout].chomp
63
+ end
64
+
65
+ def symlink(source, destination, options={})
66
+ options = default_options.merge(options)
67
+
68
+ msg = messages.add("symlinking #{File.basename source} to '#{destination}'", options)
69
+ ret = msg.parse_result(exec("ln -s #{source} #{destination}")[:exit_code])
70
+ restorecon(destination, options) # restore SELinux labels
71
+ ret
72
+ end
73
+
74
+ def chmod(mode, file, options={})
75
+ options = default_options.merge(options)
76
+
77
+ msg = messages.add("setting mode of #{File.basename file} to #{mode}", options)
78
+ msg.parse_result(exec("chmod -R #{mode} #{file}")[:exit_code])
79
+ end
80
+
81
+ def chown(user, file, options={})
82
+ options = default_options.merge(options)
83
+
84
+ msg = messages.add("setting owner of #{File.basename file} to #{user}", options)
85
+ msg.parse_result(exec("chown -R #{user} #{file}")[:exit_code])
86
+ end
87
+
88
+
89
+ def rm(file, options={})
90
+ options = default_options.merge(options)
91
+
92
+ msg = messages.add("deleting #{file}", options)
93
+ msg.parse_result(exec("rm -rf #{file}")[:exit_code])
94
+ end
95
+
96
+ def cp(source, destination, options={})
97
+ options = default_options.merge(options)
98
+
99
+ # get rid of overly careful aliases
100
+ exec 'unalias -a'
101
+
102
+ msg = messages.add("copying #{source} to #{destination}", options)
103
+ msg.parse_result(exec("cp -a #{source} #{destination}")[:exit_code])
104
+ end
105
+
106
+ def mv(source, destination, options={})
107
+ options = default_options.merge(options)
108
+
109
+ # get rid of overly careful aliases
110
+ exec 'unalias -a'
111
+
112
+ msg = messages.add("moving #{source} to #{destination}", options)
113
+ msg.parse_result(exec("mv #{source} #{destination}")[:exit_code])
114
+ end
115
+
116
+ def mkdir(dir, options={})
117
+ options = default_options.merge(options)
118
+
119
+ return true if dir_exists?(dir, :quiet => true)
120
+
121
+ msg = messages.add("creating directory #{dir}", options)
122
+ ret = msg.parse_result(exec("mkdir -p #{dir}")[:exit_code])
123
+ restorecon(dir, options) # restore SELinux labels
124
+ ret
125
+ end
126
+
127
+ def is_executable?(file, options={})
128
+ options = default_options.merge(options)
129
+
130
+ msg = messages.add("checking if file #{file} exists and is executeable", options)
131
+ msg.parse_result(exec("test -x $(which #{file})")[:exit_code])
132
+ end
133
+
134
+ def file_exists?(file, options={})
135
+ options = default_options.merge(options)
136
+
137
+ msg = messages.add("checking if file #{file} exists", options)
138
+
139
+ # don't treat directories as files
140
+ return msg.failed if dir_exists?(file, :quiet => true)
141
+
142
+ msg.parse_result(exec("test -e #{file}")[:exit_code])
143
+ end
144
+
145
+ def dir_exists?(dir, options={})
146
+ options = default_options.merge(options)
147
+
148
+ msg = messages.add("checking if directory #{dir} exists", options)
149
+ msg.parse_result(exec("test -d #{dir}")[:exit_code])
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,112 @@
1
+ require 'dust/server/ssh'
2
+
3
+ module Dust
4
+ class Server
5
+ # determining the system packet manager has to be done without facter
6
+ # because it's used to find out whether facter is installed / install facter
7
+ def uses_apt?(options={})
8
+ options = default_options(:quiet => true).merge(options)
9
+
10
+ return @uses_apt if defined?(@uses_apt)
11
+ msg = messages.add('determining whether node uses apt', options)
12
+ @uses_apt = msg.parse_result(exec('test -e /etc/debian_version')[:exit_code])
13
+ end
14
+
15
+ def uses_rpm?(options={})
16
+ options = default_options(:quiet => true).merge(options)
17
+
18
+ return @uses_rpm if defined?(@uses_rpm)
19
+ msg = messages.add('determining whether node uses rpm', options)
20
+ @uses_rpm = msg.parse_result(exec('test -e /etc/redhat-release')[:exit_code])
21
+ end
22
+
23
+ def uses_emerge?(options={})
24
+ options = default_options(:quiet => true).merge(options)
25
+
26
+ return @uses_emerge if defined?(@uses_emerge)
27
+ msg = messages.add('determining whether node uses emerge', options)
28
+ @uses_emerge = msg.parse_result(exec('test -e /etc/gentoo-release')[:exit_code])
29
+ end
30
+
31
+ def uses_pacman?(options={})
32
+ options = default_options(:quiet => true).merge(options)
33
+
34
+ return @uses_pacman if defined?(@uses_pacman)
35
+ msg = messages.add('determining whether node uses pacman', options)
36
+ @uses_pacman = msg.parse_result(exec('test -e /etc/arch-release')[:exit_code])
37
+ end
38
+
39
+ def uses_opkg?(options={})
40
+ options = default_options(:quiet => true).merge(options)
41
+
42
+ return @uses_opkg if defined?(@uses_opkg)
43
+ msg = messages.add('determining whether node uses opkg', options)
44
+ @uses_opkg = msg.parse_result(exec('test -e /etc/opkg.conf')[:exit_code])
45
+ end
46
+
47
+ def is_os?(os_list, options={})
48
+ options = default_options(:quiet => true).merge(options)
49
+
50
+ msg = messages.add("checking if this machine runs #{os_list.join(' or ')}", options)
51
+ return msg.failed unless collect_facts options
52
+
53
+ os_list.each do |os|
54
+ if @node['operatingsystem'].downcase == os.downcase
55
+ return msg.ok
56
+ end
57
+ end
58
+
59
+ msg.failed
60
+ false
61
+ end
62
+
63
+ def is_debian?(options={})
64
+ options = default_options(:quiet => true).merge(options)
65
+
66
+ return false unless uses_apt?
67
+ is_os?(['debian'], options)
68
+ end
69
+
70
+ def is_ubuntu?(options={})
71
+ options = default_options(:quiet => true).merge(options)
72
+
73
+ return false unless uses_apt?
74
+ is_os?(['ubuntu'], options)
75
+ end
76
+
77
+ def is_gentoo?(options={})
78
+ options = default_options(:quiet => true).merge(options)
79
+
80
+ return false unless uses_emerge?
81
+ is_os?(['gentoo'], options)
82
+ end
83
+
84
+ def is_centos?(options={})
85
+ options = default_options(:quiet => true).merge(options)
86
+
87
+ return false unless uses_rpm?
88
+ is_os?(['centos'], options)
89
+ end
90
+
91
+ def is_scientific?(options={})
92
+ options = default_options(:quiet => true).merge(options)
93
+
94
+ return false unless uses_rpm?
95
+ is_os?(['scientific'], options)
96
+ end
97
+
98
+ def is_fedora?(options={})
99
+ options = default_options(:quiet => true).merge(options)
100
+
101
+ return false unless uses_rpm?
102
+ is_os?(['fedora'], options)
103
+ end
104
+
105
+ def is_arch?(options={})
106
+ options = default_options(:quiet => true).merge(options)
107
+
108
+ return false unless uses_pacman?
109
+ is_os?(['archlinux'], options)
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,215 @@
1
+ require 'dust/server/ssh'
2
+ require 'dust/server/osdetect'
3
+
4
+ module Dust
5
+ class Server
6
+ # checks if one of the packages is installed
7
+ def package_installed?(packages, options={})
8
+ options = default_options.merge(options)
9
+
10
+ packages = [ packages ] if packages.is_a?(String)
11
+
12
+ msg = messages.add("checking if #{packages.join(' or ')} is installed", options)
13
+
14
+ packages.each do |package|
15
+ if uses_apt?
16
+ return msg.ok if exec("dpkg -l #{package} |grep '^ii'")[:exit_code] == 0
17
+ elsif uses_emerge?
18
+ return msg.ok unless exec("qlist -I #{package}")[:stdout].empty?
19
+ elsif uses_rpm?
20
+ return msg.ok if exec("rpm -q #{package}")[:exit_code] == 0
21
+ elsif uses_pacman?
22
+ return msg.ok if exec("pacman -Q #{package}")[:exit_code] == 0
23
+ elsif uses_opkg?
24
+ return msg.ok unless exec("opkg status #{package}")[:stdout].empty?
25
+ end
26
+ end
27
+
28
+ msg.failed
29
+ end
30
+
31
+ def install_package(package, options={})
32
+ options = default_options.merge(options)
33
+ options[:env] ||= ''
34
+
35
+ if package_installed?(package, :quiet => true)
36
+ return messages.add("package #{package} already installed", options).ok
37
+ end
38
+
39
+ # if package is an url, download and install the package file
40
+ if package =~ /^(http:\/\/|https:\/\/|ftp:\/\/)/
41
+ if uses_apt?
42
+ messages.add("installing #{package}\n", options)
43
+ return false unless install_package('wget')
44
+
45
+ msg = messages.add('downloading package', options.merge(:indent => options[:indent] + 1))
46
+
47
+ # creating temporary file
48
+ tmpfile = mktemp
49
+ return msg.failed('could not create temporary file') unless tmpfile
50
+
51
+ msg.parse_result(exec("wget #{package} -O #{tmpfile}")[:exit_code])
52
+
53
+ msg = messages.add('installing package', options.merge(:indent => options[:indent] + 1))
54
+ ret = msg.parse_result(exec("dpkg -i #{tmpfile}")[:exit_code])
55
+
56
+ msg = messages.add('deleting downloaded file', options.merge(:indent => options[:indent] + 1))
57
+ msg.parse_result(rm(tmpfile, :quiet => true))
58
+
59
+ return ret
60
+
61
+ elsif uses_rpm?
62
+ msg = messages.add("installing #{package}", options)
63
+ return msg.parse_result(exec("rpm -U #{package}")[:exit_code])
64
+
65
+ else
66
+ return msg.failed("\ninstalling packages from url not yet supported " +
67
+ "for your distribution. feel free to contribute!").failed
68
+ end
69
+
70
+ # package is not an url, use package manager
71
+ else
72
+ msg = messages.add("installing #{package}", options)
73
+
74
+ if uses_apt?
75
+ exec "DEBIAN_FRONTEND=noninteractive apt-get install -y #{package}"
76
+ elsif uses_emerge?
77
+ exec "#{options[:env]} emerge #{package}"
78
+ elsif uses_rpm?
79
+ exec "yum install -y #{package}"
80
+ elsif uses_pacman?
81
+ exec "echo y |pacman -S #{package}"
82
+ elsif uses_opkg?
83
+ exec "opkg install #{package}"
84
+ else
85
+ return msg.failed("\ninstall_package only supports apt, emerge and yum systems at the moment")
86
+ end
87
+
88
+ # check if package actually was installed
89
+ return msg.parse_result(package_installed?(package, :quiet => true))
90
+ end
91
+ end
92
+
93
+ # check if installed package is at least version min_version
94
+ def package_min_version?(package, min_version, options={})
95
+ msg = messages.add("checking if #{package} is at least version #{min_version}", options)
96
+ return msg.failed unless package_installed?(package, :quiet => true)
97
+
98
+ if uses_apt?
99
+ v = exec("dpkg --list |grep #{package}")[:stdout].chomp
100
+ elsif uses_rpm?
101
+ v = exec("rpm -q #{package}")[:stdout].chomp
102
+ elsif uses_pacman?
103
+ v = exec("pacman -Q #{package}")[:stdout].chomp
104
+ else
105
+ return msg.failed('os not supported')
106
+ end
107
+
108
+ # convert version numbers to arrays
109
+ current_version = v.to_s.split(/[-. ]/ ).select {|j| j =~ /^[0-9]+$/ }
110
+ min_version = min_version.to_s.split(/[-. ]/ ).select {|j| j =~ /^[0-9]+$/ }
111
+
112
+ # compare
113
+ min_version.each_with_index do |i, pos|
114
+ break unless current_version[pos]
115
+ return msg.failed if i.to_i < current_version[pos].to_i
116
+ end
117
+
118
+ msg.ok
119
+ end
120
+
121
+ def remove_package(package, options={})
122
+ options = default_options.merge(options)
123
+
124
+ unless package_installed?(package, :quiet => true)
125
+ return messages.add("package #{package} not installed", options).ok
126
+ end
127
+
128
+ msg = messages.add("removing #{package}", options)
129
+ if uses_apt?
130
+ msg.parse_result(exec("DEBIAN_FRONTEND=noninteractive apt-get purge -y #{package}")[:exit_code])
131
+ elsif uses_emerge?
132
+ msg.parse_result(exec("emerge --unmerge #{package}")[:exit_code])
133
+ elsif uses_rpm?
134
+ msg.parse_result(exec("yum erase -y #{package}")[:exit_code])
135
+ elsif uses_pacman?
136
+ msg.parse_result(exec("echo y |pacman -R #{package}")[:exit_code])
137
+ elsif uses_opkg?
138
+ msg.parse_result(exec("opkg remove #{package}")[:exit_code])
139
+ else
140
+ msg.failed
141
+ end
142
+ end
143
+
144
+ def update_repos(options={})
145
+ options = default_options.merge(options)
146
+
147
+ msg = messages.add('updating system repositories', options)
148
+
149
+ if uses_apt?
150
+ ret = exec('apt-get update', options)
151
+ elsif uses_emerge?
152
+ ret = exec('emerge --sync', options)
153
+ elsif uses_rpm?
154
+ ret = exec('yum check-update', options)
155
+
156
+ # yum returns != 0 if packages that need to be updated are found
157
+ # we don't want that this is producing an error
158
+ ret[:exit_code] = 0 if ret[:exit_code] == 100
159
+ elsif uses_pacman?
160
+ ret = exec('pacman -Sy', options)
161
+ elsif uses_opkg?
162
+ ret = exec('opkg update', options)
163
+ else
164
+ return msg.failed
165
+ end
166
+
167
+ unless options[:live]
168
+ msg.parse_result(ret[:exit_code])
169
+ end
170
+
171
+ ret[:exit_code]
172
+ end
173
+
174
+ def system_update(options={})
175
+ options = default_options.merge(:live => true).merge(options)
176
+
177
+ update_repos
178
+
179
+ msg = messages.add('installing system updates', options)
180
+
181
+ if uses_apt?
182
+ ret = exec 'DEBIAN_FRONTEND=noninteractive apt-get full-upgrade -y', options
183
+ if is_ubuntu?
184
+ if install_package('update-manager-core')
185
+ ret = exec 'do-release-upgrade -d -f DistUpgradeViewNonInteractive', options
186
+ else
187
+ msg.failed('could not install the update-manager package')
188
+ return false
189
+ end
190
+ else
191
+ ret = exec 'DEBIAN_FRONTEND=noninteractive apt-get full-upgrade -y', options
192
+ end
193
+ elsif uses_emerge?
194
+ ret = exec('emerge -uND @world', options)
195
+ elsif uses_rpm?
196
+ ret = exec('yum upgrade -y', options)
197
+ elsif uses_pacman?
198
+ # pacman has no --yes option that i know of, so echoing y
199
+ ret = exec('echo y |pacman -Su', options)
200
+ elsif uses_opkg?
201
+ # upgrading openwrt is very experimental, and should not used normally
202
+ ret = exec('opkg upgrade $(echo $(opkg list-upgradable |cut -d' ' -f1 |grep -v Multiple))', options)
203
+ else
204
+ msg.failed('system not (yet) supported')
205
+ return false
206
+ end
207
+
208
+ unless options[:live]
209
+ msg.parse_result(ret[:exit_code])
210
+ end
211
+
212
+ ret[:exit_code]
213
+ end
214
+ end
215
+ end