class Promotion::Enforcer
The Enforcer
handles the first half of the promotion process: moving the files into place, setting permissions, ensuring that users and groups are set up correctly, etc.
Constants
- DEFAULT_FILE_GROUP
wheel
- DEFAULT_FILE_MODE
0640
- DEFAULT_FILE_OWNER
root
- DEFAULT_FILE_SOURCE
‘.’ the current folder
- DEFAULT_FOLDER_CLEAR
false
- DEFAULT_FOLDER_GROUP
wheel
- DEFAULT_FOLDER_MODE
0750
- DEFAULT_FOLDER_OWNER
root
Attributes
returns the XML specification for the selected application
Public Class Methods
Creates a new Enforcer
for the selected application.
# File lib/promotion/enforcer.rb, line 33 def initialize(appname) appFolder = File.expand_path(appname, Folders::Staging) Dir.chdir(appFolder) specfile = File.expand_path(Files::Spec, appFolder) unless File.exist?(specfile) puts("\nSpecification file #{specfile} does not exist.\n" ) exit 1 end doc = REXML::Document.new(File.new(specfile)) @spec = doc.root $log.info("\n#{'_'*40}\nEnforcer created for #{appname}") end
Public Instance Methods
Creates a zip archive from the specified files
# File lib/promotion/enforcer.rb, line 338 def build_zip_file(zipfile) begin path = zipfile.elements["Zip"].text().strip() owner = zipfile.elements["Zip"].attributes["Owner"] || "root" group = zipfile.elements["Zip"].attributes["Group"] || "wheel" mode = zipfile.elements["Zip"].attributes["Mode"] || "0644" mode = mode.oct() filelist = [] zipfile.elements.each("Source") { |src| sourceFile = src.text().strip() unless File.exist?(sourceFile) $log.error("Missing source file #{sourceFile} to add to zip archive #{path}") exit 1 end filelist << sourceFile } targetFolder = File.dirname(path) unless File.directory?(targetFolder) $log.warn("Missing folder #{targetFolder} is being created but may have wrong ownership and permissions.") FileUtils.mkdir_p(targetFolder) end system("#{Files::Zip} #{path} #{filelist.join(' ')} ") FileUtils.chown(owner, group, path) FileUtils.chmod(mode, path) $log.info("Created zip archive #{path}, owned by #{owner}:#{group} with mode #{sprintf('%04o',mode)}") rescue => e $log.error("Unable to create zip archive #{path}, owned by #{owner}:#{group} with mode #{sprintf('%04o',mode)}\n#{e.message}\n#{e.backtrace}") exit 1 end end
Removes a folder unconditionally
# File lib/promotion/enforcer.rb, line 210 def clear_folder(path) begin FileUtils.rm_rf(path) $log.info("Removed folder #{path} in preparation for a fresh installation.") rescue $log.error("Unable to remove folder #{path} prior to installation.") exit 1 end end
Ensures that all specified files are moved into place with the correct ownership and permissions
# File lib/promotion/enforcer.rb, line 236 def ensure_allfiles(path, owner, group, mode, source) unless File.directory?(source) $log.error("Missing source folder #{source} to install contents to #{path}") exit 1 end # modify the mode for folders so they are executable perms = sprintf('%9b', mode) perms[2] = "1" perms[5] = "1" perms[8] = "1" folderMode = perms.to_i(2) begin Dir.chdir(source) Dir["**/*"].each { |file| targetPath = File.expand_path(file, path) # file could be app/media/shared/mypic.png if File.directory?(file) ensure_folder(targetPath, owner, group, folderMode, false) else ensure_file(targetPath, owner, group, mode, File.dirname(file), false, true) end } rescue => e $log.error("Unable to install allfiles from #{source} to #{path}\n#{e.message}") exit 1 end end
Ensures that a file exists at the given path, with the ownership and permissions specified. The source file is derived from the source folder and path basename If the empty
parameter is true
, create an empty file If Overwrite="false"
then set ownerships and permissions but leave the contents alone (ie. create but no update) path
= full path to file to be created source
= folder within the project to copy file from If backup
, preserve a copy of the original file if it has never been backed up yet (good for preserving original system config files)
# File lib/promotion/enforcer.rb, line 273 def ensure_file(path, owner, group, mode, source, empty, overwrite=true, backup=false) unless empty sourceFile = File.expand_path(File.basename(path), source) unless File.exist?(sourceFile) $log.error("Missing source file #{sourceFile} to install to #{path}") exit 1 end end begin targetFolder = File.dirname(path) unless File.directory?(targetFolder) $log.warn("Missing folder #{targetFolder} is being created but may have wrong ownership and permissions.") FileUtils.mkdir_p(targetFolder) end if empty FileUtils.touch(path) elsif !overwrite and File.exists?(path) # preserve the contents else FileUtils.cp(path, path+"-original") if backup and not File.exist?(path+"-original") FileUtils.cp(sourceFile, path) end FileUtils.chown(owner, group, path) FileUtils.chmod(mode, path) $log.info("Installed file #{path}, owned by #{owner}:#{group} with mode #{sprintf('%04o',mode)}") rescue => e $log.error("Unable to install file #{path}, owned by #{owner}:#{group} with mode #{sprintf('%04o',mode)}\n#{e.message}") exit 1 end end
Creates a folder (and any intermediate parent folders), sets the ownership and permissions
# File lib/promotion/enforcer.rb, line 222 def ensure_folder(path, owner, group, mode, clear) begin FileUtils.mkdir_p(path) unless File.directory?(path) FileUtils.chown(owner, group, path) FileUtils.chmod(mode, path) $log.info("Ensured folder #{path} is owned by #{owner}:#{group} with mode #{sprintf('%04o',mode)}") rescue => e $log.error("Unable to ensure folder #{path} is owned by #{owner}:#{group} with mode #{sprintf('%04o',mode)}\n#{e.message}") exit 1 end end
Ensure the group adheres to the spec
# File lib/promotion/enforcer.rb, line 175 def ensure_group(gid, name) begin group1 = Etc.getgrgid(gid) # gid exists gidFound = true rescue gidFound = false end if gidFound if group1.name == name # name matches - all good $log.info("Group #{name}(#{gid}) ok.") else # we found the gid, but the name is wrong, so fix it command = Files::Groupmod + " -n #{name} #{group1.name}" raise unless system(command) $log.warn("Group #{name}(#{gid}) name modified.") end else # no gid found begin group2 = Etc.getgrnam(name) nameFound = true rescue nameFound = false end if nameFound # fix the gid command = Files::Groupmod + " -g #{gid} #{name}" raise unless system(command) $log.info("Group #{name}(#{gid}) gid modified.") else # create the group command = Files::Groupadd + " -g #{gid} #{name}" raise unless system(command) $log.info("Group #{name}(#{gid}) created.") end end end
Ensures that a link exists at the given path, with the ownership and permissions specified.
# File lib/promotion/enforcer.rb, line 305 def ensure_link(path, owner, group, mode, target) begin targetFolder = File.dirname(path) unless File.directory?(targetFolder) $log.warn("Missing folder #{targetFolder} is being created but may have wrong ownership and permissions.") FileUtils.mkdir_p(targetFolder) end FileUtils.ln_sf(target, path) FileUtils.chown(owner, group, path) FileUtils.chmod(mode, path) $log.info("Installed link #{path} --> #{target}, owned by #{owner}:#{group} with mode #{sprintf('%04o',mode)}") rescue => e $log.error("Unable to install link #{path} --> #{target}, owned by #{owner}:#{group} with mode #{sprintf('%04o',mode)}\n#{e.message}") exit 1 end end
Ensure the user adheres to the spec
# File lib/promotion/enforcer.rb, line 129 def ensure_user(uid, name, gid, uclass, gecos, home, shell, groups) begin user1 = Etc.getpwuid(uid) # uid exists uidFound = true rescue uidFound = false end if uidFound if user1.name == name # name matches - all good $log.info("user #{name}(#{uid}) ok.") else # we found the uid, but the name is wrong, so fix it command = Files::usermod + " -l #{name} #{user1.name}" raise unless system(command) $log.warn("user #{name}(#{uid}) name modified.") end else # no uid found begin user2 = Etc.getpwnam(name) nameFound = true rescue nameFound = false end if nameFound # fix the uid command = Files::usermod + " -u #{uid} #{name}" raise unless system(command) $log.info("user #{name}(#{uid}) uid modified.") else # create the user FileUtils.mkdir_p(home) unless File.directory?(home) # ensure no warning about missing folder command = Files::Useradd + " -u #{uid} -g #{gid} -L #{uclass} " command << "-c '#{gecos}' -d #{home} -s #{shell} -p '*************' " command << "-G #{groups} " if groups.length > 0 command << " #{name} " raise unless system(command) $log.info("User #{name}(#{uid})created.") return end end # now enforce the rest of the user spec command = "#{Files::Usermod} -g #{gid} -L #{uclass} -c '#{gecos}' -d #{home} -s #{shell}" command << " -S #{groups} " if groups.length > 0 command << " #{name} " raise unless system(command) $log.info("User #{name}(#{uid}) modified to conform to spec.") end
Enforces the conditions needed for the selected app to function properly, as specified in the deployment descriptor:
-
ensure that certain groups exist
-
ensure that certain users exist (belonging to certain groups)
-
ensure that certain folders exist, if not create them; set permissions correctly; if desired, make sure they are empty
-
move specified files into place, set ownerships and permissions; create empty, or make empty, or preserve contents as desired.
-
ensure symbolic link exists, create if missing
-
create a zip archive of certain files if desired.
# File lib/promotion/enforcer.rb, line 56 def start() @spec.elements.each("/Specification/Groups/Group") { |group| gid = group.attributes["Gid"].to_i name = group.attributes["Name"] ensure_group(gid, name) } @spec.elements.each("/Specification/Users/User") { |user| uid = user.attributes["Uid"].to_i name = user.attributes["Name"] gid = user.attributes["Gid"].to_i uclass = user.attributes["Class"] || "default" gecos = user.attributes["Gecos"] || "" home = user.attributes["Home"] || "/home/#{name}" shell = user.attributes["Shell"] || "/bin/ksh" groups = user.attributes["Groups"] || "" groups = groups.gsub(/\s+/, ",") # adduser needs comma-separated groups ensure_user(uid, name, gid, uclass, gecos, home, shell, groups) } @spec.elements.each("/Specification/Folders/Folder[@Clear='true']") { |folder| path = folder.text().strip() clear_folder(path) } @spec.elements.each("/Specification/Folders/Folder") { |folder| path = folder.text().strip() owner = folder.attributes["Owner"] || folder.parent.attributes["Owner"] || DEFAULT_FOLDER_OWNER group = folder.attributes["Group"] || folder.parent.attributes["Group"] || DEFAULT_FOLDER_GROUP mode = folder.attributes["Mode"] || folder.parent.attributes["Mode"] || DEFAULT_FOLDER_MODE clear = folder.attributes["Clear"] || folder.parent.attributes["Clear"] || DEFAULT_FOLDER_MODE mode = mode.oct() clear = (clear == "true") ensure_folder(path, owner, group, mode, clear) } @spec.elements.each("/Specification/Files/File") { |file| path = file.text().strip() owner = file.attributes["Owner"] || file.parent.attributes["Owner"] || DEFAULT_FILE_OWNER group = file.attributes["Group"] || file.parent.attributes["Group"] || DEFAULT_FILE_GROUP mode = file.attributes["Mode"] || file.parent.attributes["Mode"] || DEFAULT_FILE_MODE mode = mode.oct() source = file.attributes["Source"] || file.parent.attributes["Source"] || DEFAULT_FILE_SOURCE empty = file.attributes["Empty"] == "true" overwrite = !(file.attributes["Overwrite"] == "false") backup = file.attributes["Backup"] == "true" ensure_file(path, owner, group, mode, source, empty, overwrite, backup) } @spec.elements.each("/Specification/Files/Link") { |link| path = link.text().strip() owner = link.attributes["Owner"] || link.parent.attributes["Owner"] || DEFAULT_FILE_OWNER group = link.attributes["Group"] || link.parent.attributes["Group"] || DEFAULT_FILE_GROUP mode = link.attributes["Mode"] || link.parent.attributes["Mode"] || DEFAULT_FILE_MODE mode = mode.oct() target = link.attributes["Target"] || link.parent.attributes["Source"] || DEFAULT_FILE_SOURCE ensure_link(path, owner, group, mode, target) } @spec.elements.each("/Specification/Allfiles") { |file| path = file.text().strip() owner = file.attributes["Owner"] || file.parent.attributes["Owner"] || DEFAULT_FILE_OWNER group = file.attributes["Group"] || file.parent.attributes["Group"] || DEFAULT_FILE_GROUP mode = file.attributes["Mode"] || file.parent.attributes["Mode"] || DEFAULT_FILE_MODE mode = mode.oct() source = file.attributes["Source"] || file.parent.attributes["Source"] || DEFAULT_FILE_SOURCE ensure_allfiles(path, owner, group, mode, source) } @spec.elements.each("/Specification/Subversion") { |svn| folder = svn.elements["Folder"].text url = svn.elements["Url"].text svn_check_out(url, folder) } @spec.elements.each("/Specification/Zipfile") { |zipfile| build_zip_file(zipfile) } end
Check out a repository path to the specified folder or update it if it is already installed
# File lib/promotion/enforcer.rb, line 324 def svn_check_out(url, folder) begin if system("#{::Files::Svn} info #{folder}") # already exists system("#{::Files::Svn} up --force #{folder}") else system("#{::Files::Svn} co --force #{url} #{folder}") end rescue => e $log.error("Unable to deploy #{url} into #{folder}\n#{e.message}\n#{e.backtrace}") exit 1 end end