simp-rake-helpers 5.6.2 → 5.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/lib/simp/packer/iso_vars_json.rb +87 -0
- data/lib/simp/rake/build/auto.rb +6 -31
- data/lib/simp/rake/helpers/assets/rpm_spec/simp6.spec +36 -8
- data/lib/simp/rake/helpers/assets/rpm_spec/simpdefault.spec +36 -8
- data/lib/simp/rake/helpers/version.rb +1 -1
- data/spec/acceptance/00_pkg_rpm_custom_scriptlets_spec.rb +19 -3
- data/spec/acceptance/10_pkg_rpm_spec.rb +52 -8
- data/spec/acceptance/20_pkg_rpm_upgrade_spec.rb +223 -0
- data/spec/acceptance/files/mock_packages/simp-adapter/usr/local/sbin/simp_rpm_helper +334 -245
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/CHANGELOG +2 -0
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/Rakefile +3 -0
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/build/rpm_metadata/requires +2 -0
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/data/os/CentOS.yaml +2 -0
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/data/os/RedHat.yaml +2 -0
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/hiera.yaml +14 -0
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/manifests/init.pp +2 -0
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/metadata.json +37 -0
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/CHANGELOG +5 -0
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/Rakefile +3 -0
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/build/rpm_metadata/requires +2 -0
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/data/os/CentOS.yaml +2 -0
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/data/os/RedHat.yaml +2 -0
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/hiera.yaml +14 -0
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/manifests/init.pp +3 -0
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/metadata.json +37 -0
- data/spec/acceptance/nodesets/default.yml +7 -8
- data/spec/acceptance/support/pkg_rpm_helpers.rb +2 -2
- data/spec/lib/simp/packer/iso_vars_json_spec.rb +65 -0
- metadata +29 -5
- data/spec/acceptance/20_pkg_rpm_safely_upgrading_obsolete_modules_spec.rb +0 -175
@@ -0,0 +1,223 @@
|
|
1
|
+
require 'spec_helper_acceptance'
|
2
|
+
require_relative 'support/pkg_rpm_helpers'
|
3
|
+
|
4
|
+
require 'beaker/puppet_install_helper'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
RSpec.configure do |c|
|
8
|
+
c.include Simp::BeakerHelpers::SimpRakeHelpers::PkgRpmHelpers
|
9
|
+
c.extend Simp::BeakerHelpers::SimpRakeHelpers::PkgRpmHelpers
|
10
|
+
end
|
11
|
+
|
12
|
+
# This tests RPM upgrade scenarios for components that use
|
13
|
+
# simp-adapter's simp_rpm_helper to copy files from the RPM install
|
14
|
+
# directory to a second destination directory
|
15
|
+
|
16
|
+
|
17
|
+
shared_examples_for 'RPM generator' do
|
18
|
+
it 'should create RPMs' do
|
19
|
+
testpackages.each do |package|
|
20
|
+
on host, %Q(#{run_cmd} "cd #{pkg_root_dir}/#{package}; ) +
|
21
|
+
%Q(rvm use default; bundle update --local || bundle update")
|
22
|
+
rpm_name = package.sub(/-[^-]+$/,'')
|
23
|
+
# In case previous tests haven't been clean
|
24
|
+
on host, "rpm -q #{rpm_name} && rpm -e #{rpm_name}; :"
|
25
|
+
|
26
|
+
on host, %(#{run_cmd} "cd #{pkg_root_dir}/#{package}; #{rake_cmd} pkg:rpm")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
shared_examples_for 'an upgrade path that works safely with simp_rpm_helper' do |first_package_file, second_package_file|
|
32
|
+
let( :rpm_regex ) do
|
33
|
+
/^(?<name>pupmod-[a-z0-9_]+-[a-z0-9_]+)-(?<version>\d+\.\d+\.\d+)-(?<release>\d+)\..*\.rpm$/
|
34
|
+
end
|
35
|
+
|
36
|
+
let( :first_package_version ){ first_package_file.match(rpm_regex)['version'] }
|
37
|
+
let( :first_package_name ){ first_package_file.match(rpm_regex)['name'] }
|
38
|
+
let( :first_package_forge_name ){ first_package_name.sub(/^[^-]+-/,'') }
|
39
|
+
let( :first_package_module_name ){ first_package_forge_name.sub(/^[^-]+-/,'') }
|
40
|
+
let( :first_package_dir_name ){ first_package_name + '-' + first_package_version.sub(/\.\d+-\d+$/,'') }
|
41
|
+
|
42
|
+
let( :second_package_name ){ second_package_file.match(rpm_regex)['name'] }
|
43
|
+
let( :second_package_forge_name ){ second_package_name.sub(/^[^-]+-/,'') }
|
44
|
+
let( :second_package_module_name ){ second_package_forge_name.sub(/^[^-]+-/,'') }
|
45
|
+
let( :second_package_version ){ second_package_file.match(rpm_regex)['version'] }
|
46
|
+
let( :second_package_dir_name ){ second_package_name + '-' + second_package_version.sub(/\.\d+-\d+$/,'') }
|
47
|
+
|
48
|
+
context "When upgrading from #{first_package_file} to #{second_package_file}" do
|
49
|
+
it "should clean out any old installs" do
|
50
|
+
on host, "rpm -e #{first_package_name} &> /dev/null; " +
|
51
|
+
"rpm -e #{second_package_name} &> /dev/null ",
|
52
|
+
accept_all_exit_codes: true
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should install #{first_package_file}" do
|
56
|
+
on host, "cd #{pkg_root_dir}/#{first_package_dir_name.gsub(/\.\d+$/,'')}; "+
|
57
|
+
"rpm -Uvh dist/#{first_package_file}"
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should transfer contents of #{first_package_file} into the code directory" do
|
61
|
+
result = on host, "cat /opt/test/puppet/code/#{first_package_module_name}/metadata.json"
|
62
|
+
metadata = JSON.parse(result.stdout)
|
63
|
+
expect(metadata['name']).to eq first_package_forge_name
|
64
|
+
expect(metadata['version']).to eq first_package_version
|
65
|
+
|
66
|
+
# This verifies all files/dirs from the first package are copied
|
67
|
+
on host, "diff -r /usr/share/simp/modules/#{first_package_module_name} /opt/test/puppet/code/#{first_package_module_name}"
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should upgrade to #{second_package_file}" do
|
71
|
+
on host, "cd #{pkg_root_dir}/#{second_package_dir_name.gsub(/\.\d+$/,'')}; rpm -Uvh dist/#{second_package_file}"
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should transfer contents of #{second_package_file} into the code directory" do
|
75
|
+
result = on host, "cat /opt/test/puppet/code/#{second_package_module_name}/metadata.json"
|
76
|
+
metadata = JSON.parse(result.stdout)
|
77
|
+
expect(metadata['name']).to eq second_package_forge_name
|
78
|
+
expect(metadata['version']).to eq second_package_version
|
79
|
+
|
80
|
+
# This verifies all files/dirs from the second package are copied and
|
81
|
+
# no files/dirs onyn in the old package remain
|
82
|
+
on host, "diff -r /usr/share/simp/modules/#{second_package_module_name} /opt/test/puppet/code/#{second_package_module_name}"
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe 'rake pkg:rpm + component upgrade scenarios' do
|
89
|
+
|
90
|
+
before :all do
|
91
|
+
copy_host_files_into_build_user_homedir(hosts)
|
92
|
+
|
93
|
+
comment 'ensure the Puppet AIO is installed'
|
94
|
+
#FIXME Should install Puppet 5
|
95
|
+
ENV['PUPPET_INSTALL_TYPE'] ||= 'agent'
|
96
|
+
ENV['PUPPET_INSTALL_VERSION'] ||= '1.10.6'
|
97
|
+
run_puppet_install_helper_on(hosts)
|
98
|
+
|
99
|
+
comment 'configure puppet agent to look like a Puppet server for simp_rpm_helper'
|
100
|
+
on hosts, '/opt/puppetlabs/bin/puppet config --section master set user root; ' +
|
101
|
+
'/opt/puppetlabs/bin/puppet config --section master set group root; ' +
|
102
|
+
'/opt/puppetlabs/bin/puppet config --section master set codedir /opt/test/puppet/code; ' +
|
103
|
+
'/opt/puppetlabs/bin/puppet config --section master set confdir /opt/test/puppet/code'
|
104
|
+
|
105
|
+
|
106
|
+
comment 'build and install mock RPMs'
|
107
|
+
mock_pkg_dir = '/home/build_user/host_files/spec/acceptance/files/mock_packages'
|
108
|
+
on hosts, %Q[#{run_cmd} "cd #{mock_pkg_dir}; rm -rf pkg"]
|
109
|
+
on hosts, %Q[#{run_cmd} "cd #{mock_pkg_dir}; bash rpmbuild.sh simp-adapter.spec"]
|
110
|
+
on hosts, %Q[#{run_cmd} "cd #{mock_pkg_dir}; bash rpmbuild.sh pupmod-puppetlabs-stdlib.spec"]
|
111
|
+
on hosts, %Q[#{run_cmd} "cd #{mock_pkg_dir}; bash rpmbuild.sh pupmod-simp-simplib.spec"]
|
112
|
+
on hosts, %Q[#{run_cmd} "cd #{mock_pkg_dir}; bash rpmbuild.sh pupmod-simp-foo.spec"]
|
113
|
+
|
114
|
+
on hosts, %Q[rpm -Uvh "#{mock_pkg_dir}/pkg/dist/*.noarch.rpm"], acceptable_exit_codes: [0,1]
|
115
|
+
end
|
116
|
+
|
117
|
+
hosts.each do |_host|
|
118
|
+
context "on #{_host}" do
|
119
|
+
let!(:host){ _host }
|
120
|
+
|
121
|
+
# This tests standard upgrades, which should
|
122
|
+
context 'with normal module RPM upgrades' do
|
123
|
+
let(:pkg_root_dir) do
|
124
|
+
'/home/build_user/host_files/spec/acceptance/files/package_upgrades'
|
125
|
+
end
|
126
|
+
|
127
|
+
let(:testpackages) do
|
128
|
+
[
|
129
|
+
'pupmod-simp-testpackage-1.0',
|
130
|
+
'pupmod-simp-testpackage-2.0',
|
131
|
+
]
|
132
|
+
end
|
133
|
+
|
134
|
+
context 'RPM build' do
|
135
|
+
it_should_behave_like('RPM generator')
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'RPM upgrades' do
|
139
|
+
it_should_behave_like('an upgrade path that works safely with simp_rpm_helper',
|
140
|
+
'pupmod-simp-testpackage-1.0.0-0.noarch.rpm',
|
141
|
+
'pupmod-simp-testpackage-2.0.0-0.noarch.rpm')
|
142
|
+
end
|
143
|
+
|
144
|
+
context 'RPM erase' do
|
145
|
+
it 'should remove copied files on an erase' do
|
146
|
+
on host, 'rpm -e pupmod-simp-testpackage'
|
147
|
+
on host, 'ls /opt/test/puppet/code/testpackage', acceptable_exit_codes: [2]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# These tests demonstrate custom RPM triggers that work around the obsolete
|
153
|
+
# module RPM upgrate + simp_rpm_helper problem described in SIMP-3895:
|
154
|
+
#
|
155
|
+
# https://simp-project.atlassian.net/browse/SIMP-3988
|
156
|
+
#
|
157
|
+
# The expected outcome is that simp_rpm_helper always ensures the correct
|
158
|
+
# content is installed after an upgrade, even during after a package has been
|
159
|
+
# obsoleted. This is accomplished via %triggerpostun -- <name of old package>
|
160
|
+
#
|
161
|
+
# old 1.0 -> old 2.0 = no need for a trigger
|
162
|
+
# old 1.0 -> new 2.0 = must re-run simp_rpm_helper
|
163
|
+
# old 1.0 -> new 3.0 = must re-run simp_rpm_helper
|
164
|
+
# old 2.0 -> new 2.0 = must re-run simp_rpm_helper
|
165
|
+
# old 2.0 -> new 3.0 = must re-run simp_rpm_helper
|
166
|
+
# new 2.0 -> new 3.0 = no need for a trigger
|
167
|
+
#
|
168
|
+
context 'with module RPMs that are susceptible to SIMP-3895' do
|
169
|
+
let(:pkg_root_dir) do
|
170
|
+
'/home/build_user/host_files/spec/acceptance/files/custom_scriptlet_triggers'
|
171
|
+
end
|
172
|
+
|
173
|
+
let(:testpackages) do
|
174
|
+
[
|
175
|
+
'pupmod-old-package-1.0',
|
176
|
+
'pupmod-old-package-2.0',
|
177
|
+
'pupmod-old-package-2.2',
|
178
|
+
'pupmod-new-package-2.1',
|
179
|
+
'pupmod-new-package-3.0',
|
180
|
+
]
|
181
|
+
end
|
182
|
+
|
183
|
+
context 'RPM build' do
|
184
|
+
it_should_behave_like('RPM generator')
|
185
|
+
end
|
186
|
+
|
187
|
+
context 'RPM upgrades' do
|
188
|
+
it_should_behave_like('an upgrade path that works safely with simp_rpm_helper',
|
189
|
+
'pupmod-old-package-1.0.0-0.noarch.rpm',
|
190
|
+
'pupmod-old-package-2.0.0-0.noarch.rpm')
|
191
|
+
|
192
|
+
it_should_behave_like('an upgrade path that works safely with simp_rpm_helper',
|
193
|
+
'pupmod-old-package-1.0.0-0.noarch.rpm',
|
194
|
+
'pupmod-new-package-2.1.0-0.noarch.rpm')
|
195
|
+
|
196
|
+
it_should_behave_like('an upgrade path that works safely with simp_rpm_helper',
|
197
|
+
'pupmod-new-package-2.1.0-0.noarch.rpm',
|
198
|
+
'pupmod-old-package-2.2.0-0.noarch.rpm')
|
199
|
+
|
200
|
+
it_should_behave_like('an upgrade path that works safely with simp_rpm_helper',
|
201
|
+
'pupmod-old-package-1.0.0-0.noarch.rpm',
|
202
|
+
'pupmod-new-package-3.0.0-0.noarch.rpm')
|
203
|
+
|
204
|
+
it_should_behave_like('an upgrade path that works safely with simp_rpm_helper',
|
205
|
+
'pupmod-old-package-2.0.0-0.noarch.rpm',
|
206
|
+
'pupmod-new-package-2.1.0-0.noarch.rpm')
|
207
|
+
|
208
|
+
it_should_behave_like('an upgrade path that works safely with simp_rpm_helper',
|
209
|
+
'pupmod-old-package-2.0.0-0.noarch.rpm',
|
210
|
+
'pupmod-new-package-3.0.0-0.noarch.rpm')
|
211
|
+
|
212
|
+
it_should_behave_like('an upgrade path that works safely with simp_rpm_helper',
|
213
|
+
'pupmod-old-package-2.2.0-0.noarch.rpm',
|
214
|
+
'pupmod-new-package-3.0.0-0.noarch.rpm')
|
215
|
+
|
216
|
+
it_should_behave_like('an upgrade path that works safely with simp_rpm_helper',
|
217
|
+
'pupmod-new-package-2.1.0-0.noarch.rpm',
|
218
|
+
'pupmod-new-package-3.0.0-0.noarch.rpm')
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
@@ -3,8 +3,8 @@
|
|
3
3
|
# Purpose
|
4
4
|
# -------
|
5
5
|
#
|
6
|
-
# This script is meant to be called by the %preun and %
|
7
|
-
# various SIMP Puppet module RPMs.
|
6
|
+
# This script is meant to be called by the %preun and %postttrans sections of
|
7
|
+
# the various SIMP Puppet module RPMs.
|
8
8
|
#
|
9
9
|
# The purpose of the script is to provide helper methods that correctly
|
10
10
|
# scaffold the system in such a way that all SIMP Puppet Modules can be
|
@@ -56,279 +56,307 @@ require 'find'
|
|
56
56
|
# Make sure we can find the Puppet executables
|
57
57
|
ENV['PATH'] += ':/opt/puppetlabs/bin'
|
58
58
|
|
59
|
-
|
60
|
-
def
|
61
|
-
|
59
|
+
class SimpRpmHelper
|
60
|
+
def initialize
|
61
|
+
@program_name = File.basename(__FILE__)
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
system_config.each_line do |line|
|
66
|
-
k,v = line.split('=')
|
67
|
-
config_hash[k.strip] = v.strip
|
63
|
+
# A list of modules that should never be touched once installed
|
64
|
+
@safe_modules = ['site']
|
68
65
|
end
|
69
66
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
#
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
end
|
67
|
+
def debug(msg)
|
68
|
+
# SIMP RPMs do not enable debug when they call this script. So, if
|
69
|
+
# you want to debug an RPM problem with this script, comment out
|
70
|
+
# the line below.
|
71
|
+
return unless @options.debug
|
72
|
+
msg.split("\n").each do |line|
|
73
|
+
puts ">>>#{@program_name} DEBUG: #{line}"
|
74
|
+
end
|
75
|
+
end
|
78
76
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
77
|
+
def info(msg)
|
78
|
+
# When these messages get written out in an RPM upgrade, name of program
|
79
|
+
# is helpful to end user
|
80
|
+
puts "#{@program_name}: #{msg}"
|
81
|
+
end
|
83
82
|
|
84
|
-
|
85
|
-
|
83
|
+
# Get the Puppet configuration parameters currently in use
|
84
|
+
def get_puppet_config
|
85
|
+
system_config = %x{puppet config --section master print}
|
86
86
|
|
87
|
-
|
88
|
-
if git
|
89
|
-
%x{#{git} ls-files . --error-unmatch &> /dev/null}
|
87
|
+
config_hash = Hash.new
|
90
88
|
|
91
|
-
|
89
|
+
system_config.each_line do |line|
|
90
|
+
k,v = line.split('=')
|
91
|
+
config_hash[k.strip] = v.strip
|
92
92
|
end
|
93
93
|
|
94
|
-
|
95
|
-
%x{#{svn} info &> /dev/null}
|
96
|
-
|
97
|
-
return true if $?.success?
|
98
|
-
end
|
94
|
+
return config_hash
|
99
95
|
end
|
100
96
|
|
101
|
-
|
102
|
-
|
97
|
+
# Determine whether the passed path is under management by git or svn
|
98
|
+
def is_managed?(path)
|
99
|
+
# Short circuit if the directory is not present
|
100
|
+
return false unless File.directory?(path)
|
103
101
|
|
104
|
-
|
105
|
-
|
106
|
-
options.config_file = '/etc/simp/adapter_config.yaml'
|
107
|
-
options.preserve = false
|
108
|
-
# These are not settable, but it's nice to have all this material in one place
|
109
|
-
options.puppet_user = @puppet_config['user']
|
110
|
-
options.puppet_group = @puppet_config['group']
|
111
|
-
|
112
|
-
all_opts = OptionParser.new do |opts|
|
113
|
-
opts.banner = "Usage: #{$0} [options]"
|
114
|
-
|
115
|
-
opts.separator ""
|
116
|
-
|
117
|
-
opts.on(
|
118
|
-
"--rpm_dir PATH",
|
119
|
-
"The directory into which the RPM source material is installed"
|
120
|
-
) do |arg|
|
121
|
-
options.rpm_dir = arg.strip
|
122
|
-
options.module_name = File.basename(options.rpm_dir)
|
123
|
-
end
|
102
|
+
git = Facter::Core::Execution.which('git')
|
103
|
+
svn = Facter::Core::Execution.which('svn')
|
124
104
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
" Must be one of 'pre', 'post', 'preun', 'postun'"
|
129
|
-
) do |arg|
|
130
|
-
options.rpm_section = arg.strip
|
131
|
-
end
|
105
|
+
Dir.chdir(path) do
|
106
|
+
if git
|
107
|
+
%x{#{git} ls-files . --error-unmatch &> /dev/null}
|
132
108
|
|
133
|
-
|
134
|
-
|
135
|
-
"The status code passed to the RPM section"
|
136
|
-
) do |arg|
|
137
|
-
options.rpm_status = arg.strip
|
138
|
-
end
|
109
|
+
return true if $?.success?
|
110
|
+
end
|
139
111
|
|
140
|
-
|
141
|
-
|
142
|
-
"--config CONFIG_FILE",
|
143
|
-
"The configuration file to use",
|
144
|
-
" Default: #{options.config_file}"
|
145
|
-
) do |arg|
|
146
|
-
options.config_file = arg.strip
|
147
|
-
end
|
112
|
+
if svn
|
113
|
+
%x{#{svn} info &> /dev/null}
|
148
114
|
|
149
|
-
|
150
|
-
|
151
|
-
"--preserve",
|
152
|
-
"Preserve material in 'target_dir' that is not in 'rpm_dir'"
|
153
|
-
) do |arg|
|
154
|
-
options.preserve = true
|
115
|
+
return true if $?.success?
|
116
|
+
end
|
155
117
|
end
|
156
118
|
|
157
|
-
|
158
|
-
|
159
|
-
"--enforce",
|
160
|
-
"If set, enforce the copy, regardless of the setting in the config file",
|
161
|
-
" Default: false"
|
162
|
-
) do |arg|
|
163
|
-
options.copy_rpm_data = true
|
164
|
-
end
|
119
|
+
return false
|
120
|
+
end
|
165
121
|
|
166
|
-
|
167
|
-
"-t DIR",
|
168
|
-
"--target_dir DIR",
|
169
|
-
"The subdirectory of #{simp_target_dir('')}",
|
170
|
-
"into which to copy the materials.",
|
171
|
-
" Default: #{simp_target_dir.gsub(/#{simp_target_dir('')}/,'')}"
|
172
|
-
) do |arg|
|
173
|
-
options.target_dir = simp_target_dir(arg.strip)
|
174
|
-
end
|
122
|
+
def parse_options(args)
|
175
123
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
"Help Message"
|
180
|
-
) do
|
181
|
-
puts opts
|
182
|
-
exit(0)
|
183
|
-
end
|
184
|
-
end
|
124
|
+
@options = OpenStruct.new
|
125
|
+
@options.config_file = '/etc/simp/adapter_config.yaml'
|
126
|
+
@options.preserve = false
|
185
127
|
|
186
|
-
|
187
|
-
|
188
|
-
rescue OptionParser::ParseError => e
|
189
|
-
puts e
|
190
|
-
puts all_opts
|
191
|
-
exit 1
|
192
|
-
end
|
128
|
+
all_opts = OptionParser.new do |opts|
|
129
|
+
opts.banner = "Usage: #{@program_name} [options]"
|
193
130
|
|
194
|
-
|
131
|
+
opts.separator ''
|
195
132
|
|
196
|
-
|
197
|
-
|
133
|
+
opts.on(
|
134
|
+
'--rpm_dir PATH',
|
135
|
+
'The directory into which the RPM source material is installed'
|
136
|
+
) do |arg|
|
137
|
+
@options.rpm_dir = arg.strip
|
138
|
+
@options.module_name = File.basename(@options.rpm_dir)
|
139
|
+
end
|
198
140
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
}
|
141
|
+
opts.on(
|
142
|
+
'--rpm_section SECTION',
|
143
|
+
'The section of the RPM from which the script is being called.',
|
144
|
+
" Must be one of 'pre', 'preun', 'postun', 'posttrans'"
|
145
|
+
) do |arg|
|
146
|
+
@options.rpm_section = arg.strip
|
147
|
+
end
|
207
148
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
149
|
+
opts.on(
|
150
|
+
'--rpm_status STATUS',
|
151
|
+
'The status code passed to the RPM section'
|
152
|
+
) do |arg|
|
153
|
+
@options.rpm_status = arg.strip
|
213
154
|
end
|
214
|
-
rescue
|
215
|
-
fail("Error: Config file '#{config_file}' could not be processed")
|
216
|
-
end
|
217
|
-
end
|
218
155
|
|
219
|
-
|
220
|
-
|
221
|
-
|
156
|
+
opts.on(
|
157
|
+
'-f CONFIG_FILE',
|
158
|
+
'--config CONFIG_FILE',
|
159
|
+
'The configuration file to use.',
|
160
|
+
" Default: #{@options.config_file}"
|
161
|
+
) do |arg|
|
162
|
+
@options.config_file = arg.strip
|
163
|
+
end
|
222
164
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
165
|
+
opts.on(
|
166
|
+
'-p',
|
167
|
+
'--preserve',
|
168
|
+
"Preserve material in 'target_dir' that is not in 'rpm_dir'"
|
169
|
+
) do |arg|
|
170
|
+
@options.preserve = true
|
229
171
|
end
|
230
172
|
|
231
|
-
|
232
|
-
|
233
|
-
|
173
|
+
opts.on(
|
174
|
+
'-e',
|
175
|
+
'--enforce',
|
176
|
+
'If set, enforce the copy, regardless of the setting in the config file',
|
177
|
+
' Default: false'
|
178
|
+
) do |arg|
|
179
|
+
@options.copy_rpm_data = true
|
180
|
+
end
|
234
181
|
|
235
|
-
|
236
|
-
|
182
|
+
opts.on(
|
183
|
+
'-t DIR',
|
184
|
+
'--target_dir DIR',
|
185
|
+
"The subdirectory of #{simp_target_dir('')}",
|
186
|
+
'into which to copy the materials.',
|
187
|
+
" Default: #{simp_target_dir.gsub(/#{simp_target_dir('')}/,'')}"
|
188
|
+
) do |arg|
|
189
|
+
@options.target_dir = simp_target_dir(arg.strip)
|
190
|
+
end
|
237
191
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
192
|
+
opts.on(
|
193
|
+
'-v',
|
194
|
+
'--verbose',
|
195
|
+
'Print out debug info when processing.'
|
196
|
+
) do
|
197
|
+
@options.debug = true
|
198
|
+
end
|
245
199
|
|
246
|
-
|
247
|
-
|
200
|
+
opts.on(
|
201
|
+
'-h',
|
202
|
+
'--help',
|
203
|
+
'Help Message'
|
204
|
+
) do
|
205
|
+
puts opts
|
206
|
+
@options.help_requested = true
|
207
|
+
end
|
208
|
+
end
|
248
209
|
|
249
|
-
|
250
|
-
|
251
|
-
|
210
|
+
begin
|
211
|
+
all_opts.parse!(args)
|
212
|
+
rescue OptionParser::ParseError => e
|
213
|
+
msg = "Error: #{e}\n\n#{all_opts}"
|
214
|
+
raise(msg)
|
215
|
+
end
|
252
216
|
|
253
|
-
|
254
|
-
fail('Error: Could not find a Puppet code directory for installation')
|
217
|
+
validate_options(all_opts.to_s)
|
255
218
|
end
|
256
219
|
|
257
|
-
|
220
|
+
# Process the config, validate the entries and do some munging
|
221
|
+
# Sets @options hash.
|
222
|
+
def process_config
|
223
|
+
# Defaults
|
224
|
+
config = {
|
225
|
+
'target_directory' => 'auto',
|
226
|
+
'copy_rpm_data' => false
|
227
|
+
}
|
228
|
+
|
229
|
+
if File.exist?(@options.config_file)
|
230
|
+
begin
|
231
|
+
system_config = YAML.load_file(@options.config_file)
|
232
|
+
if system_config
|
233
|
+
config.merge!(system_config)
|
234
|
+
end
|
235
|
+
rescue
|
236
|
+
raise("Error: Config file '#{@options.config_file}' could not be processed")
|
237
|
+
end
|
238
|
+
end
|
258
239
|
|
259
|
-
|
260
|
-
|
240
|
+
if @options.copy_rpm_data.nil?
|
241
|
+
@options.copy_rpm_data = (config['copy_rpm_data'].to_s == 'true')
|
242
|
+
end
|
261
243
|
|
262
|
-
|
263
|
-
|
244
|
+
if @options.target_dir.nil? && config['target_directory']
|
245
|
+
if config['target_directory'] == 'auto'
|
246
|
+
@options.target_dir = simp_target_dir
|
247
|
+
else
|
248
|
+
unless config['target_directory'][0].chr == '/'
|
249
|
+
raise("Error: 'target_directory' in '#{@options.config_file}' must be an absolute path")
|
250
|
+
end
|
264
251
|
|
265
|
-
|
266
|
-
|
252
|
+
@options.target_dir = config['target_directory'].strip
|
253
|
+
end
|
254
|
+
end
|
267
255
|
end
|
268
256
|
|
269
|
-
|
270
|
-
|
257
|
+
def puppet_codedir
|
258
|
+
# Figure out where the Puppet code should go
|
259
|
+
# Puppet 4+
|
260
|
+
code_dir = puppet_config['codedir']
|
261
|
+
if !code_dir || code_dir.empty?
|
262
|
+
code_dir = puppet_config['confdir']
|
263
|
+
end
|
264
|
+
|
265
|
+
return code_dir
|
271
266
|
end
|
272
267
|
|
273
|
-
|
274
|
-
|
268
|
+
def puppet_config
|
269
|
+
unless @puppet_config
|
270
|
+
@puppet_config = get_puppet_config
|
271
|
+
end
|
272
|
+
@puppet_config
|
275
273
|
end
|
276
274
|
|
277
|
-
valid_rpm_sections = ['pre','post','preun','postun']
|
278
275
|
|
279
|
-
|
280
|
-
|
276
|
+
def puppet_group
|
277
|
+
puppet_config['group']
|
281
278
|
end
|
282
279
|
|
283
|
-
|
284
|
-
|
285
|
-
|
280
|
+
# Return the target installation directory
|
281
|
+
def simp_target_dir(subdir=File.join('simp','modules'))
|
282
|
+
install_target = puppet_codedir
|
283
|
+
|
284
|
+
if install_target.empty?
|
285
|
+
raise('Error: Could not find a Puppet code directory for installation')
|
286
286
|
end
|
287
|
-
end
|
288
287
|
|
289
|
-
|
290
|
-
|
288
|
+
install_target = File.join(install_target,'environments', subdir)
|
289
|
+
|
290
|
+
return install_target
|
291
291
|
end
|
292
292
|
|
293
|
-
|
294
|
-
|
295
|
-
|
293
|
+
# Input Validation
|
294
|
+
def validate_options(usage)
|
295
|
+
return if @options.help_requested
|
296
296
|
|
297
|
-
|
297
|
+
unless @options.rpm_dir
|
298
|
+
raise("Error: 'rpm_dir' is required\n#{usage}")
|
299
|
+
end
|
298
300
|
|
299
|
-
@
|
300
|
-
|
301
|
-
|
301
|
+
unless @options.rpm_status
|
302
|
+
raise("Error: 'rpm_status' is required\n#{usage}")
|
303
|
+
end
|
302
304
|
|
303
|
-
|
304
|
-
|
305
|
+
unless @options.rpm_section
|
306
|
+
raise("Error: 'rpm_section' is required\n#{usage}")
|
307
|
+
end
|
305
308
|
|
306
|
-
#
|
307
|
-
|
309
|
+
# We allow 'post' for backward compatibility with SIMP RPMs that use
|
310
|
+
# this, but copying over files in the 'post' during an upgrade is
|
311
|
+
# problematic. If the old package has files that are not in the new
|
312
|
+
# package, these files will not be removed in the destination directory.
|
313
|
+
# This is because during %post, the old package files have not yet
|
314
|
+
# been removed from the source directory by RPM. So, the 'rsync'
|
315
|
+
# operation copies over the OBE files from the old package.
|
316
|
+
valid_rpm_sections = ['pre','post','preun','postun', 'posttrans']
|
317
|
+
|
318
|
+
unless valid_rpm_sections.include?(@options.rpm_section)
|
319
|
+
raise("Error: 'rpm_section' must be one of '#{valid_rpm_sections.join("', '")}'\n#{usage}")
|
320
|
+
end
|
308
321
|
|
309
|
-
|
310
|
-
|
322
|
+
if (@options.rpm_section == 'posttrans') || (@options.rpm_section == 'preun') || (@options.rpm_section == 'post')
|
323
|
+
unless File.directory?(@options.rpm_dir)
|
324
|
+
raise("Error: Could not find 'rpm_dir': '#{@options.rpm_dir}'")
|
325
|
+
end
|
326
|
+
end
|
311
327
|
|
312
|
-
|
313
|
-
|
314
|
-
|
328
|
+
unless @options.rpm_status =~ /^\d+$/
|
329
|
+
raise("Error: 'rpm_status' must be an integer\n#{usage}")
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
|
334
|
+
def handle_install
|
335
|
+
debug("Processing install, upgrade, or downgrade of #{@options.module_name}")
|
336
|
+
if @safe_modules.include?(@options.module_name)
|
315
337
|
# Make sure that we preserve anything in the safe modules on installation
|
316
|
-
options.preserve = true
|
338
|
+
@options.preserve = true
|
317
339
|
|
318
|
-
if options.rpm_status == '2'
|
340
|
+
if @options.rpm_status == '2'
|
319
341
|
# Short circuit on upgrading safe modules, just don't touch them!
|
320
|
-
|
321
|
-
|
342
|
+
target_module_dir = File.join(@options.target_dir, @options.module_name)
|
343
|
+
if File.directory?(target_module_dir)
|
344
|
+
debug("Skipping upgrade of 'safe' module directory #{target_module_dir}")
|
345
|
+
return
|
322
346
|
end
|
323
347
|
end
|
324
348
|
end
|
325
349
|
|
350
|
+
raise('Error: Could not determine puppet group') if puppet_group.empty?
|
351
|
+
rsync = Facter::Core::Execution.which('rsync')
|
352
|
+
raise("Error: Could not find 'rsync' command!") unless rsync
|
353
|
+
|
326
354
|
# Create the directories, with the proper mode, all the way down
|
327
|
-
dir_paths = options.target_dir.split(File::SEPARATOR).reject(&:empty?)
|
355
|
+
dir_paths = @options.target_dir.split(File::SEPARATOR).reject(&:empty?)
|
328
356
|
top_dir = File::SEPARATOR + dir_paths.shift
|
329
357
|
unless File.directory?(top_dir)
|
330
358
|
FileUtils.mkdir(top_dir, :mode => 0750)
|
331
|
-
FileUtils.chown('root',
|
359
|
+
FileUtils.chown('root', puppet_group, top_dir)
|
332
360
|
end
|
333
361
|
|
334
362
|
orig_dir = Dir.pwd
|
@@ -336,7 +364,7 @@ unless is_managed?(File.join(options.target_dir, options.module_name)) || !optio
|
|
336
364
|
dir_paths.each do |dir|
|
337
365
|
unless File.directory?(dir)
|
338
366
|
FileUtils.mkdir(dir, :mode => 0750)
|
339
|
-
FileUtils.chown('root',
|
367
|
+
FileUtils.chown('root', puppet_group, dir)
|
340
368
|
end
|
341
369
|
|
342
370
|
Dir.chdir(dir)
|
@@ -345,62 +373,123 @@ unless is_managed?(File.join(options.target_dir, options.module_name)) || !optio
|
|
345
373
|
|
346
374
|
cmd = %(#{rsync} -a --force)
|
347
375
|
|
348
|
-
if options.preserve
|
376
|
+
if @options.preserve
|
349
377
|
cmd += %( --ignore-existing)
|
350
378
|
else
|
351
|
-
|
379
|
+
cmd += %( --delete)
|
352
380
|
end
|
353
381
|
|
354
|
-
cmd += %(
|
382
|
+
cmd += %( --verbose) if @options.debug
|
383
|
+
|
384
|
+
cmd += %( #{@options.rpm_dir} #{@options.target_dir})
|
355
385
|
cmd += %( 2>&1)
|
356
386
|
|
387
|
+
info("Copying '#{@options.module_name}' files into '#{@options.target_dir}'")
|
388
|
+
debug("Executing: #{cmd}")
|
357
389
|
output = %x{#{cmd}}
|
390
|
+
debug("Output:\n#{output}")
|
358
391
|
unless $?.success?
|
359
|
-
|
392
|
+
raise(%(Error: Copy of '#{@options.module_name}' into '#{@options.target_dir}' using '#{cmd}' failed with the following error:\n #{output.gsub("\n","\n ")}))
|
360
393
|
end
|
361
394
|
|
362
|
-
FileUtils.chown_R(nil, "#{
|
395
|
+
FileUtils.chown_R(nil, "#{puppet_group}", @options.target_dir)
|
363
396
|
end
|
364
397
|
|
365
|
-
|
366
|
-
|
367
|
-
if options.rpm_section == 'preun' && options.rpm_status == '0'
|
398
|
+
def handle_uninstall
|
399
|
+
debug("Processing uninstall of #{@options.module_name}")
|
368
400
|
# Play it safe, this needs to have at least 'environments/simp' in it!
|
369
|
-
if options.target_dir.split(File::SEPARATOR).reject(&:empty?).size < 3
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
401
|
+
if @options.target_dir.split(File::SEPARATOR).reject(&:empty?).size < 3
|
402
|
+
raise("Error: Not removing directory '#{@options.target_dir}' for safety")
|
403
|
+
end
|
404
|
+
|
405
|
+
if @safe_modules.include?(@options.module_name)
|
406
|
+
target_module_dir = File.join(@options.target_dir, @options.module_name)
|
407
|
+
debug("Skipping removal of 'safe' module directory #{target_module_dir}")
|
408
|
+
return
|
409
|
+
end
|
410
|
+
|
411
|
+
info("Removing '#{@options.module_name}' files from '#{@options.target_dir}'")
|
412
|
+
|
413
|
+
# Find out what we have
|
414
|
+
ref_list = []
|
415
|
+
Dir.chdir(@options.rpm_dir) do
|
416
|
+
Find.find('.').each do |file|
|
417
|
+
if File.symlink?(file)
|
418
|
+
ref_list << file
|
419
|
+
Find.prune
|
384
420
|
end
|
385
421
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
422
|
+
ref_list << file
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
# Delete from the bottom up to clear out the directories first
|
427
|
+
# before removing them
|
428
|
+
ref_list.reverse!
|
429
|
+
ref_list.map{|x| x.sub!(/^./, @options.module_name)}
|
430
|
+
|
431
|
+
# Only delete items that are in the reference repo
|
432
|
+
Dir.chdir(@options.target_dir) do
|
433
|
+
ref_list.each do |to_rm|
|
434
|
+
if File.symlink?(to_rm)
|
435
|
+
debug("Removing symlink #{to_rm}")
|
436
|
+
FileUtils.rm_f(to_rm)
|
437
|
+
elsif File.directory?(to_rm) && (Dir.entries(to_rm).delete_if {|dir|
|
438
|
+
dir == '.' || dir == '..'}.size == 0)
|
439
|
+
debug("Removing directory #{to_rm}")
|
440
|
+
FileUtils.rmdir(to_rm)
|
441
|
+
elsif File.exist?(to_rm)
|
442
|
+
debug("Removing file #{to_rm}")
|
443
|
+
FileUtils.rm_f(to_rm)
|
402
444
|
end
|
403
445
|
end
|
404
446
|
end
|
405
447
|
end
|
448
|
+
|
449
|
+
def run(args)
|
450
|
+
parse_options(args)
|
451
|
+
return 0 if @options.help_requested
|
452
|
+
|
453
|
+
process_config
|
454
|
+
debug("Running with config=#{@options.to_s}")
|
455
|
+
|
456
|
+
# If the target directory is managed, we're done
|
457
|
+
target_module_dir = File.join(@options.target_dir, @options.module_name)
|
458
|
+
unless is_managed?(target_module_dir) || !@options.copy_rpm_data
|
459
|
+
|
460
|
+
debug("Processing unmanaged target directory #{target_module_dir}")
|
461
|
+
|
462
|
+
if (@options.rpm_section == 'posttrans') || (@options.rpm_section == 'post')
|
463
|
+
# A regular installation, upgrade or downgrade
|
464
|
+
# This *should* happen in the RPM %posttrans, but we allow this to
|
465
|
+
# occur in the %post for backward compatibility with SIMP RPMs that
|
466
|
+
# erroneously try to affect a copy in the %post. (Copying over the
|
467
|
+
# files in the RPM %post during an upgrade/downgrade is problematic.
|
468
|
+
# If the old package has files that are not in the new package,
|
469
|
+
# these files will not yet have been removed in the source
|
470
|
+
# directory, and thus end up in the target directory.)
|
471
|
+
handle_install
|
472
|
+
elsif @options.rpm_section == 'preun' && @options.rpm_status == '0'
|
473
|
+
# A regular uninstall
|
474
|
+
# This needs to happen *before* RPM removes the files (%preun with
|
475
|
+
# status 0), since we need to compare with what's on disk to undo
|
476
|
+
# the copy done during the RPM install via handle_install()
|
477
|
+
handle_uninstall
|
478
|
+
end
|
479
|
+
end
|
480
|
+
return 0
|
481
|
+
rescue RuntimeError => e
|
482
|
+
$stderr.puts(e)
|
483
|
+
return 1
|
484
|
+
rescue Exception => e
|
485
|
+
$stderr.puts(e)
|
486
|
+
e.backtrace.first(10).each{|l| $stderr.puts l }
|
487
|
+
return 1
|
488
|
+
end
|
406
489
|
end
|
490
|
+
|
491
|
+
if __FILE__ == $0
|
492
|
+
helper = SimpRpmHelper.new
|
493
|
+
exit helper.run(ARGV)
|
494
|
+
end
|
495
|
+
|