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

spec[R]

returns the XML specification for the selected application

Public Class Methods

new(appname) click to toggle source

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

build_zip_file(zipfile) click to toggle source

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
clear_folder(path) click to toggle source

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
ensure_allfiles(path, owner, group, mode, source) click to toggle source

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
ensure_file(path, owner, group, mode, source, empty, overwrite=true, backup=false) click to toggle source

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
ensure_folder(path, owner, group, mode, clear) click to toggle source

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_group(gid, name) click to toggle source

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
ensure_user(uid, name, gid, uclass, gecos, home, shell, groups) click to toggle source

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
start() click to toggle source

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
svn_check_out(url, folder) click to toggle source

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