File: ruby.rb

package info (click to toggle)
puppet-module-puppetlabs-postgresql 10.5.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 940 kB
  • sloc: ruby: 731; sh: 66; makefile: 2
file content (158 lines) | stat: -rw-r--r-- 5,893 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# frozen_string_literal: true

# This provider is used to manage postgresql.conf files
# It uses ruby to parse the config file and
# to add, remove or modify settings.
#
# The provider is able to parse postgresql.conf files with the following format:
# key = value # comment

Puppet::Type.type(:postgresql_conf).provide(:ruby) do
  desc 'Set keys, values and comments in a postgresql config file.'

  # The function parses the postgresql.conf and figures out which active settings exist in a config file and returns an array of hashes
  #
  def parse_config
    # regex to match active keys, values and comments
    active_values_regex = %r{^\s*(?<key>[\w.]+)\s*=?\s*(?<value>.*?)(?:\s*#\s*(?<comment>.*))?\s*$}
    # empty array to be filled with hashes
    active_settings = []
    # iterate the file and construct a hash for every matching/active setting
    # the hash is pushed to the array and the array is returned
    File.foreach(resource[:target]).with_index(1) do |line, line_number|
      matches = line.match(active_values_regex)
      if matches
        value = if matches[:value].to_i.to_s == matches[:value]
                  matches[:value].to_i
                elsif matches[:value].to_f.to_s == matches[:value]
                  matches[:value].to_f
                else
                  matches[:value].delete("'")
                end
        attributes_hash = { line_number: line_number, key: matches[:key], ensure: 'present', value: value, comment: matches[:comment] }
        active_settings.push(attributes_hash)
      end
    end
    Puppet.debug("DEBUG: parse_config Active Settings found in PostgreSQL config file: #{active_settings}")
    active_settings
  end

  # Deletes an existing header from a parsed postgresql.conf configuration file
  #
  # @param [Array] lines of the parsed postgresql configuration file
  def delete_header(lines)
    header_regex = %r{^# HEADER:.*}
    lines.delete_if do |entry|
      entry.match?(header_regex)
    end
  end

  # Adds a header to a parsed postgresql.conf configuration file, after all other changes are made
  #
  # @param [Array] lines of the parsed postgresql configuration file
  def add_header(lines)
    timestamp = Time.now.strftime('%F %T %z')
    header = ["# HEADER: This file was autogenerated at #{timestamp}\n",
              "# HEADER: by puppet.  While it can still be managed manually, it\n",
              "# HEADER: is definitely not recommended.\n"]
    header + lines
  end

  # This function writes the config file, it removes the old header, adds a new one and writes the file
  #
  # @param [Array] lines of the parsed postgresql configuration file
  def write_config(lines)
    lines = delete_header(lines)
    lines = add_header(lines)
    File.write(resource[:target], lines.join)
  end

  # check, if resource exists in postgresql.conf file
  def exists?
    select = parse_config.select { |hash| hash[:key] == resource[:key] }
    raise Puppet::Error, "found multiple config items of #{resource[:key]}, please fix this" if select.length > 1
    return false if select.empty?

    @result = select.first
    Puppet.debug("DEBUG: exists? @result: #{@result}")
    true
  end

  # remove resource if exists and is set to absent
  def destroy
    entry_regex = %r{#{resource[:key]}.*=.*#{resource[:value]}}
    lines = File.readlines(resource[:target])

    lines.delete_if do |entry|
      entry.match?(entry_regex)
    end
    write_config(lines)
  end

  # create resource if it does not exists
  def create
    lines = File.readlines(resource[:target])
    new_line = line(key: resource[:key], value: resource[:value], comment: resource[:comment])

    lines.push(new_line)
    write_config(lines)
  end

  # getter - get value of a resource
  def value
    @result[:value]
  end

  # getter - get comment of a resource
  def comment
    @result[:comment]
  end

  # setter - set value of a resource
  def value=(_value)
    lines = File.readlines(resource[:target])
    active_values_regex = %r{^\s*(?<key>[\w.]+)\s*=?\s*(?<value>.*?)(?:\s*#\s*(?<comment>.*))?\s*$}
    new_line = line(key: resource[:key], value: resource[:value], comment: resource[:comment])

    lines.each_with_index do |line, index|
      matches = line.to_s.match(active_values_regex)
      lines[index] = new_line if matches && (matches[:key] == resource[:key] && matches[:value] != resource[:value])
    end
    write_config(lines)
  end

  # setter - set comment of a resource
  def comment=(_comment)
    lines = File.readlines(resource[:target])
    active_values_regex = %r{^\s*(?<key>[\w.]+)\s*=?\s*(?<value>.*?)(?:\s*#\s*(?<comment>.*))?\s*$}
    new_line = line(key: resource[:key], value: resource[:value], comment: resource[:comment])

    lines.each_with_index do |line, index|
      matches = line.to_s.match(active_values_regex)
      lines[index] = new_line if matches && (matches[:key] == resource[:key] && matches[:comment] != resource[:comment])
    end
    write_config(lines)
  end

  private

  # Takes elements for a postgresql.conf configuration line and formats them properly
  #
  # @param [String] key postgresql.conf configuration option
  # @param [String] value the value for the configuration option
  # @param [String] comment optional comment that will be added at the end of the line
  # @return [String] line the whole line for the config file, with \n
  def line(key: '', value: '', comment: nil)
    value = value.to_s if value.is_a?(Numeric)
    dontneedquote = value.match(%r{^(\d+.?\d+|\w+)$})
    dontneedequal = key.match(%r{^(include|include_if_exists)$}i)
    line =  key.downcase # normalize case
    line += dontneedequal ? ' ' : ' = '
    line += "'" unless dontneedquote && !dontneedequal
    line += value
    line += "'" unless dontneedquote && !dontneedequal
    line += " # #{comment}" unless comment.nil? || comment == :absent
    line += "\n"
    line
  end
end