summaryrefslogtreecommitdiff
path: root/lib/puppet/provider/file_line/ruby.rb
blob: 42f287acf570937e0d6ded90563212512d17d73d (plain)
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
Puppet::Type.type(:file_line).provide(:ruby) do
  def exists?
    if resource[:replace].to_s != 'true' and count_matches(match_regex) > 0
      true
    else
      lines.find do |line|
        if resource[:ensure].to_s == 'absent' and resource[:match_for_absence].to_s == 'true'
          line.chomp =~ Regexp.new(resource[:match])
        else
          line.chomp == resource[:line].chomp
        end
      end
    end
  end

  def create
    unless resource[:replace].to_s != 'true' and count_matches(match_regex) > 0
      if resource[:match]
        handle_create_with_match
      elsif resource[:after]
        handle_create_with_after
      else
        append_line
      end
    end
  end

  def destroy
    if resource[:match_for_absence].to_s == 'true' and resource[:match]
      handle_destroy_with_match
    else
      handle_destroy_line
    end
  end

  private
  def lines
    # If this type is ever used with very large files, we should
    #  write this in a different way, using a temp
    #  file; for now assuming that this type is only used on
    #  small-ish config files that can fit into memory without
    #  too much trouble.
    begin
      @lines ||= File.readlines(resource[:path], :encoding => resource[:encoding])
    rescue TypeError => e
      # Ruby 1.8 doesn't support open_args
      @lines ||= File.readlines(resource[:path])
    end
  end

  def match_regex
    resource[:match] ? Regexp.new(resource[:match]) : nil
  end

  def handle_create_with_match()
    regex_after = resource[:after] ? Regexp.new(resource[:after]) : nil
    match_count = count_matches(match_regex)

    if match_count > 1 && resource[:multiple].to_s != 'true'
     raise Puppet::Error, "More than one line in file '#{resource[:path]}' matches pattern '#{resource[:match]}'"
    end

    File.open(resource[:path], 'w') do |fh|
      lines.each do |l|
        fh.puts(match_regex.match(l) ? resource[:line] : l)
        if (match_count == 0 and regex_after)
          if regex_after.match(l)
            fh.puts(resource[:line])
            match_count += 1 #Increment match_count to indicate that the new line has been inserted.
          end
        end
      end

      if (match_count == 0)
        fh.puts(resource[:line])
      end
    end
  end

  def handle_create_with_after
    regex = Regexp.new(resource[:after])
    count = count_matches(regex)

    if count > 1 && resource[:multiple].to_s != 'true'
      raise Puppet::Error, "#{count} lines match pattern '#{resource[:after]}' in file '#{resource[:path]}'.  One or no line must match the pattern."
    end

    File.open(resource[:path], 'w') do |fh|
      lines.each do |l|
        fh.puts(l)
        if regex.match(l) then
          fh.puts(resource[:line])
        end
      end
    end

    if (count == 0) # append the line to the end of the file
      append_line
    end
  end

  def count_matches(regex)
    lines.select{|l| l.match(regex)}.size
  end

  def handle_destroy_with_match
    match_count = count_matches(match_regex)
    if match_count > 1 && resource[:multiple].to_s != 'true'
     raise Puppet::Error, "More than one line in file '#{resource[:path]}' matches pattern '#{resource[:match]}'"
    end

    local_lines = lines
    File.open(resource[:path],'w') do |fh|
      fh.write(local_lines.reject{|l| match_regex.match(l) }.join(''))
    end
  end

  def handle_destroy_line
    local_lines = lines
    File.open(resource[:path],'w') do |fh|
      fh.write(local_lines.reject{|l| l.chomp == resource[:line] }.join(''))
    end
  end

  ##
  # append the line to the file.
  #
  # @api private
  def append_line
    File.open(resource[:path], 'w') do |fh|
      lines.each do |l|
        fh.puts(l)
      end
      fh.puts resource[:line]
    end
  end
end