abide_dev_utils 0.1.0
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.
- checksums.yaml +7 -0
 - data/.gitignore +12 -0
 - data/.rspec +3 -0
 - data/.rubocop.yml +136 -0
 - data/.rubocop_todo.yml +49 -0
 - data/Gemfile +8 -0
 - data/LICENSE.txt +21 -0
 - data/README.md +45 -0
 - data/Rakefile +12 -0
 - data/abide_dev_utils.gemspec +53 -0
 - data/bin/abide.rb +6 -0
 - data/bin/console +15 -0
 - data/bin/setup +8 -0
 - data/exe/abide +6 -0
 - data/lib/abide_dev_utils.rb +9 -0
 - data/lib/abide_dev_utils/cli.rb +35 -0
 - data/lib/abide_dev_utils/cli/jira.rb +113 -0
 - data/lib/abide_dev_utils/cli/puppet.rb +58 -0
 - data/lib/abide_dev_utils/cli/test.rb +84 -0
 - data/lib/abide_dev_utils/cli/xccdf.rb +48 -0
 - data/lib/abide_dev_utils/config.rb +24 -0
 - data/lib/abide_dev_utils/constants.rb +17 -0
 - data/lib/abide_dev_utils/errors.rb +5 -0
 - data/lib/abide_dev_utils/errors/base.rb +27 -0
 - data/lib/abide_dev_utils/errors/general.rb +52 -0
 - data/lib/abide_dev_utils/errors/jira.rb +21 -0
 - data/lib/abide_dev_utils/errors/xccdf.rb +12 -0
 - data/lib/abide_dev_utils/files.rb +47 -0
 - data/lib/abide_dev_utils/jira.rb +181 -0
 - data/lib/abide_dev_utils/output.rb +44 -0
 - data/lib/abide_dev_utils/ppt.rb +135 -0
 - data/lib/abide_dev_utils/prompt.rb +32 -0
 - data/lib/abide_dev_utils/utils/general.rb +9 -0
 - data/lib/abide_dev_utils/validate.rb +31 -0
 - data/lib/abide_dev_utils/version.rb +5 -0
 - data/lib/abide_dev_utils/xccdf.rb +24 -0
 - data/lib/abide_dev_utils/xccdf/cis.rb +3 -0
 - data/lib/abide_dev_utils/xccdf/cis/hiera.rb +138 -0
 - metadata +266 -0
 
| 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'abide_dev_utils/errors/base'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module AbideDevUtils
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Errors
         
     | 
| 
      
 7 
     | 
    
         
            +
                # Raised when an xpath search of an xccdf file fails
         
     | 
| 
      
 8 
     | 
    
         
            +
                class XPathSearchError < GenericError
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @default = 'XPath seach failed to find anything at:'
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module AbideDevUtils
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Files
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Writer
         
     | 
| 
      
 6 
     | 
    
         
            +
                  MSG_EXT_APPEND = 'Appending %s extension to file'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  def write(content, file: nil, add_ext: true, file_ext: nil)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    valid_file = add_ext ? append_ext(file, file_ext) : file
         
     | 
| 
      
 10 
     | 
    
         
            +
                    File.open(valid_file, 'w') { |f| f.write(content) }
         
     | 
| 
      
 11 
     | 
    
         
            +
                    verify_write(valid_file)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def method_missing(m, *args, **kwargs, &_block)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    if m.to_s.match?(/^write_/)
         
     | 
| 
      
 16 
     | 
    
         
            +
                      ext = m.to_s.split('_')[-1]
         
     | 
| 
      
 17 
     | 
    
         
            +
                      write(args[0], **kwargs, file_ext: ext)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    else
         
     | 
| 
      
 19 
     | 
    
         
            +
                      super
         
     | 
| 
      
 20 
     | 
    
         
            +
                    end
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  def respond_to_missing?(method_name, include_private = false)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    method_name.to_s.start_with?('write_') || super
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  def append_ext(file_path, ext)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    return file_path if ext.nil?
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                    s_ext = ".#{ext}"
         
     | 
| 
      
 31 
     | 
    
         
            +
                    unless File.extname(file_path) == s_ext
         
     | 
| 
      
 32 
     | 
    
         
            +
                      puts MSG_EXT_APPEND % s_ext
         
     | 
| 
      
 33 
     | 
    
         
            +
                      file_path << s_ext
         
     | 
| 
      
 34 
     | 
    
         
            +
                    end
         
     | 
| 
      
 35 
     | 
    
         
            +
                    file_path
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  def verify_write(file_path)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    if File.file?(file_path)
         
     | 
| 
      
 40 
     | 
    
         
            +
                      puts "Successfully wrote to #{file_path}"
         
     | 
| 
      
 41 
     | 
    
         
            +
                    else
         
     | 
| 
      
 42 
     | 
    
         
            +
                      puts "Something went wrong! Failed writing to #{file_path}!"
         
     | 
| 
      
 43 
     | 
    
         
            +
                    end
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,181 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'jira-ruby'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'abide_dev_utils/output'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'abide_dev_utils/prompt'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'abide_dev_utils/config'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'abide_dev_utils/errors/jira'
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            module AbideDevUtils
         
     | 
| 
      
 10 
     | 
    
         
            +
              module Jira
         
     | 
| 
      
 11 
     | 
    
         
            +
                ERRORS = AbideDevUtils::Errors::Jira
         
     | 
| 
      
 12 
     | 
    
         
            +
                COV_PARENT_SUMMARY_PREFIX = '::BENCHMARK:: '
         
     | 
| 
      
 13 
     | 
    
         
            +
                COV_CHILD_SUMMARY_PREFIX = '::CONTROL:: '
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def self.project(client, project)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  client.Project.find(project)
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def self.issue(client, issue)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  client.Issue.find(issue)
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def self.myself(client)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  client.User.myself
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                def self.issuetype(client, id)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  client.Issuetype.find(id)
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def self.priority(client, id)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  client.Priority.find(id)
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def self.all_project_issues_attrs(project)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  raw_issues = project.issues
         
     | 
| 
      
 37 
     | 
    
         
            +
                  raw_issues.collect(&:attrs)
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def self.new_issue(client, project, summary, dry_run: false)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  if dry_run
         
     | 
| 
      
 42 
     | 
    
         
            +
                    sleep(0.2)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    return Dummy.new
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                  fields = {}
         
     | 
| 
      
 46 
     | 
    
         
            +
                  fields['summary'] = summary
         
     | 
| 
      
 47 
     | 
    
         
            +
                  fields['project'] = project(client, project)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  fields['reporter'] = myself(client)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  fields['issuetype'] = issuetype(client, '3')
         
     | 
| 
      
 50 
     | 
    
         
            +
                  fields['priority'] = priority(client, '6')
         
     | 
| 
      
 51 
     | 
    
         
            +
                  issue = client.Issue.build
         
     | 
| 
      
 52 
     | 
    
         
            +
                  raise ERRORS::CreateIssueError, issue.attrs unless issue.save({ 'fields' => fields })
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  issue
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                # This should probably be threaded in the future
         
     | 
| 
      
 58 
     | 
    
         
            +
                def self.bulk_new_issue(client, project, summaries, dry_run: false)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  summaries.each { |s| new_issue(client, project, s, dry_run: dry_run) }
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                def self.new_subtask(client, issue, summary, dry_run: false)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  if dry_run
         
     | 
| 
      
 64 
     | 
    
         
            +
                    sleep(0.2)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    return Dummy.new
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
                  issue_fields = issue.attrs['fields']
         
     | 
| 
      
 68 
     | 
    
         
            +
                  fields = {}
         
     | 
| 
      
 69 
     | 
    
         
            +
                  fields['parent'] = issue
         
     | 
| 
      
 70 
     | 
    
         
            +
                  fields['summary'] = summary
         
     | 
| 
      
 71 
     | 
    
         
            +
                  fields['project'] = issue_fields['project']
         
     | 
| 
      
 72 
     | 
    
         
            +
                  fields['reporter'] = myself(client)
         
     | 
| 
      
 73 
     | 
    
         
            +
                  fields['issuetype'] = issuetype(client, '5')
         
     | 
| 
      
 74 
     | 
    
         
            +
                  fields['priority'] = issue_fields['priority']
         
     | 
| 
      
 75 
     | 
    
         
            +
                  subtask = client.Issue.build
         
     | 
| 
      
 76 
     | 
    
         
            +
                  raise ERRORS::CreateSubtaskError, subtask.attrs unless subtask.save({ 'fields' => fields })
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                  subtask
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                def self.bulk_new_subtask(client, issue, summaries, dry_run: false)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  summaries.each do |s|
         
     | 
| 
      
 83 
     | 
    
         
            +
                    new_subtask(client, issue, s, dry_run: dry_run)
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                def self.client(options: {})
         
     | 
| 
      
 88 
     | 
    
         
            +
                  opts = merge_options(options)
         
     | 
| 
      
 89 
     | 
    
         
            +
                  opts[:username] = AbideDevUtils::Prompt.username if opts[:username].nil?
         
     | 
| 
      
 90 
     | 
    
         
            +
                  opts[:password] = AbideDevUtils::Prompt.password if opts[:password].nil?
         
     | 
| 
      
 91 
     | 
    
         
            +
                  opts[:site] = AbideDevUtils::Prompt.single_line('Jira URL') if opts[:site].nil?
         
     | 
| 
      
 92 
     | 
    
         
            +
                  opts[:context_path] = '' if opts[:context_path].nil?
         
     | 
| 
      
 93 
     | 
    
         
            +
                  opts[:auth_type] = :basic if opts[:auth_type].nil?
         
     | 
| 
      
 94 
     | 
    
         
            +
                  JIRA::Client.new(opts)
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                def self.client_from_prompts(http_debug: false)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  options = {}
         
     | 
| 
      
 99 
     | 
    
         
            +
                  options[:username] = AbideDevUtils::Prompt.username
         
     | 
| 
      
 100 
     | 
    
         
            +
                  options[:password] = AbideDevUtils::Prompt.password
         
     | 
| 
      
 101 
     | 
    
         
            +
                  options[:site] = AbideDevUtils::Prompt.single_line('Jira URL')
         
     | 
| 
      
 102 
     | 
    
         
            +
                  options[:context_path] = ''
         
     | 
| 
      
 103 
     | 
    
         
            +
                  options[:auth_type] = :basic
         
     | 
| 
      
 104 
     | 
    
         
            +
                  options[:http_debug] = http_debug
         
     | 
| 
      
 105 
     | 
    
         
            +
                  JIRA::Client.new(options)
         
     | 
| 
      
 106 
     | 
    
         
            +
                end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                def self.project_from_prompts(http_debug: false)
         
     | 
| 
      
 109 
     | 
    
         
            +
                  client = client_from_prompts(http_debug)
         
     | 
| 
      
 110 
     | 
    
         
            +
                  project = AbideDevUtils::Prompt.single_line('Project').upcase
         
     | 
| 
      
 111 
     | 
    
         
            +
                  client.Project.find(project)
         
     | 
| 
      
 112 
     | 
    
         
            +
                end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                def self.new_issues_from_coverage(client, project, report, dry_run: false)
         
     | 
| 
      
 115 
     | 
    
         
            +
                  dr_prefix = dry_run ? 'DRY RUN: ' : ''
         
     | 
| 
      
 116 
     | 
    
         
            +
                  i_attrs = all_project_issues_attrs(project)
         
     | 
| 
      
 117 
     | 
    
         
            +
                  rep_sums = summaries_from_coverage_report(report)
         
     | 
| 
      
 118 
     | 
    
         
            +
                  rep_sums.each do |k, v|
         
     | 
| 
      
 119 
     | 
    
         
            +
                    next if summary_exist?(k, i_attrs)
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                    parent = new_issue(client, project.attrs['key'], k.to_s, dry_run: dry_run)
         
     | 
| 
      
 122 
     | 
    
         
            +
                    AbideDevUtils::Output.simple("#{dr_prefix}Created parent issue #{k}")
         
     | 
| 
      
 123 
     | 
    
         
            +
                    parent_issue = issue(client, parent.attrs['key']) unless parent.respond_to?(:dummy)
         
     | 
| 
      
 124 
     | 
    
         
            +
                    AbideDevUtils::Output.simple("#{dr_prefix}Creating subtasks, this can take a while...")
         
     | 
| 
      
 125 
     | 
    
         
            +
                    progress = AbideDevUtils::Output.progress(title: "#{dr_prefix}Creating Subtasks", total: nil)
         
     | 
| 
      
 126 
     | 
    
         
            +
                    v.each do |s|
         
     | 
| 
      
 127 
     | 
    
         
            +
                      next if summary_exist?(s, i_attrs)
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                      progress.title = "#{dr_prefix}#{s}"
         
     | 
| 
      
 130 
     | 
    
         
            +
                      new_subtask(client, parent_issue, s, dry_run: dry_run)
         
     | 
| 
      
 131 
     | 
    
         
            +
                      progress.increment
         
     | 
| 
      
 132 
     | 
    
         
            +
                    end
         
     | 
| 
      
 133 
     | 
    
         
            +
                  end
         
     | 
| 
      
 134 
     | 
    
         
            +
                end
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                def self.merge_options(options)
         
     | 
| 
      
 137 
     | 
    
         
            +
                  config.merge(options)
         
     | 
| 
      
 138 
     | 
    
         
            +
                end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                def self.config
         
     | 
| 
      
 141 
     | 
    
         
            +
                  AbideDevUtils::Config.config_section(:jira)
         
     | 
| 
      
 142 
     | 
    
         
            +
                end
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                def self.summary_exist?(summary, issue_attrs)
         
     | 
| 
      
 145 
     | 
    
         
            +
                  issue_attrs.each do |i|
         
     | 
| 
      
 146 
     | 
    
         
            +
                    return true if i['fields']['summary'] == summary
         
     | 
| 
      
 147 
     | 
    
         
            +
                  end
         
     | 
| 
      
 148 
     | 
    
         
            +
                  false
         
     | 
| 
      
 149 
     | 
    
         
            +
                end
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                def self.summaries_from_coverage_report(report)
         
     | 
| 
      
 152 
     | 
    
         
            +
                  summaries = {}
         
     | 
| 
      
 153 
     | 
    
         
            +
                  benchmark = nil
         
     | 
| 
      
 154 
     | 
    
         
            +
                  report.each do |k, v|
         
     | 
| 
      
 155 
     | 
    
         
            +
                    benchmark = v if k == 'benchmark'
         
     | 
| 
      
 156 
     | 
    
         
            +
                    next unless k.match?(/^profile_/)
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                    parent_sum = k
         
     | 
| 
      
 159 
     | 
    
         
            +
                    v.each do |sk, sv|
         
     | 
| 
      
 160 
     | 
    
         
            +
                      next unless sk == 'uncovered'
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                      summaries[parent_sum] = sv.collect { |s| "#{COV_CHILD_SUMMARY_PREFIX}#{s}" }
         
     | 
| 
      
 163 
     | 
    
         
            +
                    end
         
     | 
| 
      
 164 
     | 
    
         
            +
                  end
         
     | 
| 
      
 165 
     | 
    
         
            +
                  summaries.transform_keys { |k| "#{COV_PARENT_SUMMARY_PREFIX}#{benchmark}-#{k}"}
         
     | 
| 
      
 166 
     | 
    
         
            +
                end
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                class Dummy
         
     | 
| 
      
 169 
     | 
    
         
            +
                  def attrs
         
     | 
| 
      
 170 
     | 
    
         
            +
                    { 'fields' => {
         
     | 
| 
      
 171 
     | 
    
         
            +
                      'project' => 'dummy',
         
     | 
| 
      
 172 
     | 
    
         
            +
                      'priority' => 'dummy'
         
     | 
| 
      
 173 
     | 
    
         
            +
                    } }
         
     | 
| 
      
 174 
     | 
    
         
            +
                  end
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
                  def dummy
         
     | 
| 
      
 177 
     | 
    
         
            +
                    true
         
     | 
| 
      
 178 
     | 
    
         
            +
                  end
         
     | 
| 
      
 179 
     | 
    
         
            +
                end
         
     | 
| 
      
 180 
     | 
    
         
            +
              end
         
     | 
| 
      
 181 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,44 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'pp'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'ruby-progressbar'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'abide_dev_utils/validate'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'abide_dev_utils/files'
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            module AbideDevUtils
         
     | 
| 
      
 11 
     | 
    
         
            +
              module Output
         
     | 
| 
      
 12 
     | 
    
         
            +
                FWRITER = AbideDevUtils::Files::Writer.new
         
     | 
| 
      
 13 
     | 
    
         
            +
                def self.simple(msg, stream: $stdout)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  stream.puts msg
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def self.json(in_obj, console: false, file: nil, pretty: true)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  AbideDevUtils::Validate.hashable(in_obj)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  json_out = pretty ? JSON.pretty_generate(in_obj) : JSON.generate(in_obj)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  simple(json_out) if console
         
     | 
| 
      
 21 
     | 
    
         
            +
                  FWRITER.write_json(json_out, file: file) unless file.nil?
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def self.yaml(in_obj, console: false, file: nil)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  AbideDevUtils::Validate.hashable(in_obj)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # Use object's #to_yaml method if it exists, convert to hash if not
         
     | 
| 
      
 27 
     | 
    
         
            +
                  yaml_out = in_obj.respond_to?(:to_yaml) ? in_obj.to_yaml : in_obj.to_h.to_yaml
         
     | 
| 
      
 28 
     | 
    
         
            +
                  simple(yaml_out) if console
         
     | 
| 
      
 29 
     | 
    
         
            +
                  FWRITER.write_yaml(yaml_out, file: file) unless file.nil?
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def self.yml(in_obj, console: false, file: nil)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  AbideDevUtils::Validate.hashable(in_obj)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # Use object's #to_yaml method if it exists, convert to hash if not
         
     | 
| 
      
 35 
     | 
    
         
            +
                  yml_out = in_obj.respond_to?(:to_yaml) ? in_obj.to_yaml : in_obj.to_h.to_yaml
         
     | 
| 
      
 36 
     | 
    
         
            +
                  simple(yml_out) if console
         
     | 
| 
      
 37 
     | 
    
         
            +
                  FWRITER.write_yml(yml_out, file: file) unless file.nil?
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def self.progress(title: 'Progress', start: 0, total: 100)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  ProgressBar.create(title: title, starting_at: start, total: total)
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,135 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'pathname'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'puppet_pal'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            module AbideDevUtils
         
     | 
| 
      
 9 
     | 
    
         
            +
              module Ppt
         
     | 
| 
      
 10 
     | 
    
         
            +
                def self.coverage_report(puppet_class_dir, hiera_path, profile = nil)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  coverage = {}
         
     | 
| 
      
 12 
     | 
    
         
            +
                  coverage['classes'] = {}
         
     | 
| 
      
 13 
     | 
    
         
            +
                  all_cap = find_all_classes_and_paths(puppet_class_dir)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  invalid_classes = find_invalid_classes(all_cap)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  valid_classes = all_cap.dup.transpose[0] - invalid_classes
         
     | 
| 
      
 16 
     | 
    
         
            +
                  coverage['classes']['invalid'] = invalid_classes
         
     | 
| 
      
 17 
     | 
    
         
            +
                  coverage['classes']['valid'] = valid_classes
         
     | 
| 
      
 18 
     | 
    
         
            +
                  hiera = YAML.safe_load(File.open(hiera_path))
         
     | 
| 
      
 19 
     | 
    
         
            +
                  matcher = profile.nil? ? /^profile_/ : /^profile_#{profile}/
         
     | 
| 
      
 20 
     | 
    
         
            +
                  hiera.each do |k, v|
         
     | 
| 
      
 21 
     | 
    
         
            +
                    key_base = k.split('::')[-1]
         
     | 
| 
      
 22 
     | 
    
         
            +
                    coverage['benchmark'] = v if key_base == 'title'
         
     | 
| 
      
 23 
     | 
    
         
            +
                    next unless key_base.match?(matcher)
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    coverage[key_base] = generate_uncovered_data(v, valid_classes)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                  coverage
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def self.generate_uncovered_data(ctrl_list, valid_classes)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  out_hash = {}
         
     | 
| 
      
 32 
     | 
    
         
            +
                  out_hash[:num_total] = ctrl_list.length
         
     | 
| 
      
 33 
     | 
    
         
            +
                  out_hash[:uncovered] = []
         
     | 
| 
      
 34 
     | 
    
         
            +
                  out_hash[:covered] = []
         
     | 
| 
      
 35 
     | 
    
         
            +
                  ctrl_list.each do |c|
         
     | 
| 
      
 36 
     | 
    
         
            +
                    if valid_classes.include?(c)
         
     | 
| 
      
 37 
     | 
    
         
            +
                      out_hash[:covered] << c
         
     | 
| 
      
 38 
     | 
    
         
            +
                    else
         
     | 
| 
      
 39 
     | 
    
         
            +
                      out_hash[:uncovered] << c
         
     | 
| 
      
 40 
     | 
    
         
            +
                    end
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  out_hash[:num_covered] = out_hash[:covered].length
         
     | 
| 
      
 43 
     | 
    
         
            +
                  out_hash[:num_uncovered] = out_hash[:uncovered].length
         
     | 
| 
      
 44 
     | 
    
         
            +
                  out_hash[:coverage] = Float(
         
     | 
| 
      
 45 
     | 
    
         
            +
                    (Float(out_hash[:num_covered]) / Float(out_hash[:num_total])) * 100.0
         
     | 
| 
      
 46 
     | 
    
         
            +
                  ).floor(3)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  out_hash
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                # Given a directory holding Puppet manifests, returns
         
     | 
| 
      
 51 
     | 
    
         
            +
                # the full namespace for all classes in that directory.
         
     | 
| 
      
 52 
     | 
    
         
            +
                # @param puppet_class_dir [String] path to a dir containing Puppet manifests
         
     | 
| 
      
 53 
     | 
    
         
            +
                # @return [String] The namespace for all classes in manifests in the dir
         
     | 
| 
      
 54 
     | 
    
         
            +
                def self.find_class_namespace(puppet_class_dir)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  path = Pathname.new(puppet_class_dir)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  mod_root = nil
         
     | 
| 
      
 57 
     | 
    
         
            +
                  ns_parts = []
         
     | 
| 
      
 58 
     | 
    
         
            +
                  found_manifests = false
         
     | 
| 
      
 59 
     | 
    
         
            +
                  path.ascend do |p|
         
     | 
| 
      
 60 
     | 
    
         
            +
                    if found_manifests
         
     | 
| 
      
 61 
     | 
    
         
            +
                      mod_root = find_mod_root(p)
         
     | 
| 
      
 62 
     | 
    
         
            +
                      break
         
     | 
| 
      
 63 
     | 
    
         
            +
                    end
         
     | 
| 
      
 64 
     | 
    
         
            +
                    if File.basename(p) == 'manifests'
         
     | 
| 
      
 65 
     | 
    
         
            +
                      found_manifests = true
         
     | 
| 
      
 66 
     | 
    
         
            +
                      next
         
     | 
| 
      
 67 
     | 
    
         
            +
                    else
         
     | 
| 
      
 68 
     | 
    
         
            +
                      ns_parts << File.basename(p)
         
     | 
| 
      
 69 
     | 
    
         
            +
                    end
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end
         
     | 
| 
      
 71 
     | 
    
         
            +
                  "#{mod_root}::#{ns_parts.reverse.join('::')}::"
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                # Given a Pathname object of the 'manifests' directory in a Puppet module,
         
     | 
| 
      
 75 
     | 
    
         
            +
                # determines the module namespace root. Does this by consulting
         
     | 
| 
      
 76 
     | 
    
         
            +
                # metadata.json, if it exists, or by using the parent directory name.
         
     | 
| 
      
 77 
     | 
    
         
            +
                # @param pathname [Pathname] A Pathname object of the module's manifests dir
         
     | 
| 
      
 78 
     | 
    
         
            +
                # @return [String] The module's namespace root
         
     | 
| 
      
 79 
     | 
    
         
            +
                def self.find_mod_root(pathname)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  metadata_file = nil
         
     | 
| 
      
 81 
     | 
    
         
            +
                  pathname.entries.each do |e|
         
     | 
| 
      
 82 
     | 
    
         
            +
                    metadata_file = "#{pathname}/metadata.json" if File.basename(e) == 'metadata.json'
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
                  if metadata_file.nil?
         
     | 
| 
      
 85 
     | 
    
         
            +
                    File.basename(p)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  else
         
     | 
| 
      
 87 
     | 
    
         
            +
                    File.open(metadata_file) do |f|
         
     | 
| 
      
 88 
     | 
    
         
            +
                      file = JSON.parse(f.read)
         
     | 
| 
      
 89 
     | 
    
         
            +
                      File.basename(p) unless file.key?('name')
         
     | 
| 
      
 90 
     | 
    
         
            +
                      file['name'].split('-')[-1]
         
     | 
| 
      
 91 
     | 
    
         
            +
                    end
         
     | 
| 
      
 92 
     | 
    
         
            +
                  end
         
     | 
| 
      
 93 
     | 
    
         
            +
                end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                # @return [Array] An array of frozen arrays where each sub-array's
         
     | 
| 
      
 96 
     | 
    
         
            +
                #   index 0 is class_name and index 1 is the full path to the file.
         
     | 
| 
      
 97 
     | 
    
         
            +
                def self.find_all_classes_and_paths(puppet_class_dir)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  all_cap = []
         
     | 
| 
      
 99 
     | 
    
         
            +
                  Dir.each_child(puppet_class_dir) do |c|
         
     | 
| 
      
 100 
     | 
    
         
            +
                    path = "#{puppet_class_dir}/#{c}"
         
     | 
| 
      
 101 
     | 
    
         
            +
                    next if File.directory?(path) || File.extname(path) != '.pp'
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                    all_cap << [File.basename(path, '.pp'), path].freeze
         
     | 
| 
      
 104 
     | 
    
         
            +
                  end
         
     | 
| 
      
 105 
     | 
    
         
            +
                  all_cap
         
     | 
| 
      
 106 
     | 
    
         
            +
                end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                def self.find_valid_classes(all_cap)
         
     | 
| 
      
 109 
     | 
    
         
            +
                  all_classes = all_cap.dup.transpose[0]
         
     | 
| 
      
 110 
     | 
    
         
            +
                  all_classes - find_invalid_classes(all_cap)
         
     | 
| 
      
 111 
     | 
    
         
            +
                end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                def self.find_invalid_classes(all_cap)
         
     | 
| 
      
 114 
     | 
    
         
            +
                  invalid_classes = []
         
     | 
| 
      
 115 
     | 
    
         
            +
                  all_cap.each do |cap|
         
     | 
| 
      
 116 
     | 
    
         
            +
                    invalid_classes << cap[0] unless class_valid?(cap[1])
         
     | 
| 
      
 117 
     | 
    
         
            +
                  end
         
     | 
| 
      
 118 
     | 
    
         
            +
                  invalid_classes
         
     | 
| 
      
 119 
     | 
    
         
            +
                end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                def self.class_valid?(manifest_path)
         
     | 
| 
      
 122 
     | 
    
         
            +
                  compiler = Puppet::Pal::Compiler.new(nil)
         
     | 
| 
      
 123 
     | 
    
         
            +
                  ast = compiler.parse_file(manifest_path)
         
     | 
| 
      
 124 
     | 
    
         
            +
                  ast.body.body.statements.each do |s|
         
     | 
| 
      
 125 
     | 
    
         
            +
                    next unless s.respond_to?(:arguments)
         
     | 
| 
      
 126 
     | 
    
         
            +
                    next unless s.arguments.respond_to?(:each)
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                    s.arguments.each do |i|
         
     | 
| 
      
 129 
     | 
    
         
            +
                      return false if i.value == 'Not implemented'
         
     | 
| 
      
 130 
     | 
    
         
            +
                    end
         
     | 
| 
      
 131 
     | 
    
         
            +
                  end
         
     | 
| 
      
 132 
     | 
    
         
            +
                  true
         
     | 
| 
      
 133 
     | 
    
         
            +
                end
         
     | 
| 
      
 134 
     | 
    
         
            +
              end
         
     | 
| 
      
 135 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'io/console'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module AbideDevUtils
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Prompt
         
     | 
| 
      
 7 
     | 
    
         
            +
                def self.yes_no(msg)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  print "#{msg} (Y/n): "
         
     | 
| 
      
 9 
     | 
    
         
            +
                  return true if $stdin.cooked(&:gets).match?(/^[Yy].*/)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  false
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def self.single_line(msg)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  print "#{msg}: "
         
     | 
| 
      
 16 
     | 
    
         
            +
                  $stdin.cooked(&:gets).chomp
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def self.username
         
     | 
| 
      
 20 
     | 
    
         
            +
                  print 'Username: '
         
     | 
| 
      
 21 
     | 
    
         
            +
                  $stdin.cooked(&:gets).chomp
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def self.password
         
     | 
| 
      
 25 
     | 
    
         
            +
                  $stdin.getpass('Password:')
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def self.secure(msg)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  $stdin.getpass(msg)
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     |