Class: Rubber::Configuration::Generator

Inherits:
Object
  • Object
show all
Defined in:
lib/rubber/generator.rb

Overview

Handles selection and transformation of a set of config files based on the host/role they belong to

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config_dir, roles, host, options = {}) ⇒ Generator

Returns a new instance of Generator.



19
20
21
22
23
24
25
# File 'lib/rubber/generator.rb', line 19

def initialize(config_dir, roles, host, options={})
  @config_dir = config_dir
  @roles = roles.to_a.reverse #First roles take precedence
  @host = host || 'no_host'
  @options=options
  @delayed_post_commands = []
end

Instance Attribute Details

#fake_rootObject

Returns the value of attribute fake_root.



16
17
18
# File 'lib/rubber/generator.rb', line 16

def fake_root
  @fake_root
end

#file_patternObject

Returns the value of attribute file_pattern.



13
14
15
# File 'lib/rubber/generator.rb', line 13

def file_pattern
  @file_pattern
end

#forceObject

Returns the value of attribute force.



15
16
17
# File 'lib/rubber/generator.rb', line 15

def force
  @force
end

#no_postObject

Returns the value of attribute no_post.



14
15
16
# File 'lib/rubber/generator.rb', line 14

def no_post
  @no_post
end

#stop_on_error_cmdObject

Returns the value of attribute stop_on_error_cmd.



17
18
19
# File 'lib/rubber/generator.rb', line 17

def stop_on_error_cmd
  @stop_on_error_cmd
end

Instance Method Details

#runObject



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
# File 'lib/rubber/generator.rb', line 27

def run
  config_dirs = []
  config_dirs << "#{@config_dir}/common/**/**"
  @roles.sort.each {|role| config_dirs <<  "#{@config_dir}/role/#{role}/**/**" }
  config_dirs << "#{@config_dir}/host/#{@host}/**/**"

  pat = Regexp.new(file_pattern) if file_pattern
  
  config_dirs.each do |dir|
    Dir[dir].sort.each do |f|
      next if f =~ /\/(CVS|\.svn)\//
      if (! pat || pat.match(f)) && File.file?(f)
        Rubber.logger.info{"Transforming #{f}"}
        begin
          transform(IO.read(f), @options)
        rescue Exception => e
          lines = e.backtrace.grep(/^\(erb\):([0-9]+)/) {|b| Regexp.last_match(1) }
          Rubber.logger.error{"Transformation failed for #{f}#{':' + lines.first if lines.first}"}
          Rubber.logger.error e.message
          exit 1
        end
      end
    end
  end

  @delayed_post_commands = @delayed_post_commands.uniq
  if @delayed_post_commands.size > 0
    Rubber.logger.info("Running delayed post commands")

    @delayed_post_commands.each do |delayed_post_command|
      run_post_command(delayed_post_command)
    end
  end
end

#run_post_command(cmd) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/rubber/generator.rb', line 62

def run_post_command(cmd)
  if fake_root
    Rubber.logger.info("Not running post command as a fake root was given: #{cmd}")
  elsif no_post
    Rubber.logger.info("Not running post command as no post specified: #{cmd}")
  else
    # this lets us abort a script if a command in the middle of it errors out
    # stop_on_error_cmd = "function error_exit { exit 99; }; trap error_exit ERR"
    cmd_with_error_handler = stop_on_error_cmd ? "#{stop_on_error_cmd}\n#{cmd}" : cmd

    Rubber.logger.info{"Transformation executing post config command: #{cmd}"}
    Rubber.logger.info `#{cmd_with_error_handler}`
    if $?.exitstatus != 0
      raise "Post command failed execution:  #{cmd_with_error_handler}"
    end
  end
end

#transform(src_data, options = {}) ⇒ Object

Transforms the ERB template given in srcfile and writes the result to dest_file (if not nil) before returning it



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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/rubber/generator.rb', line 82

def transform(src_data, options={})
  config = ConfigDescriptor.new
  config.generator = self

  # for development/test, if we have a fake root, echo any
  # calls to system
  if fake_root
    class << config
      def system(*args)
        puts ("Not running system command during a fake_root transformation: #{args.inspect}")
      end
      def open(*args)
        if args.first && args.first =~ /^|/
          puts ("Not running open/pipe command during a fake_root transformation: #{args.inspect}")
        else
          super
        end
      end
      alias ` system
      alias exec system
      alias fork system
    end
  end

  config.options = options
  template = ERB.new(src_data, nil, "-")
  result = template.result(config.get_binding())
  
  return if config.skip

  config_path = config.path

  # for development/test, if we have a fake root, then send config
  # output there, and also put write_cmd output there
  if fake_root
    config_path = "write_cmd_" + config.write_cmd.gsub(/[^a-z0-9_-]/i, '') if config.write_cmd
    config_path = "#{fake_root}/#{config_path}" if config_path
  end

  if ! config_path && ! (config.read_cmd && config.write_cmd)
    raise "Transformation requires either a output filename or command"
  end

  reader = config_path || "|#{config.read_cmd}"
  orig = IO.read(reader) rescue ""

  # When additive is set we need to only replace between our delimiters
  if config.additive
    additive = ["# start rubber #{@host}", "# end rubber #{@host}"] unless additive.is_a? Array
    pat = /#{config.additive[0]}.*#{config.additive[1]}/m
    new = "#{config.additive[0]}#{result}#{config.additive[1]}"
    if orig =~ pat
      result = orig.gsub(pat, new)
    else
      result = orig + new + "\n"
    end
  end

  # Only do something if the transformed result is different than what
  # is currently in the destination file
  if orig != result || force
    # create dirs as needed
    FileUtils.mkdir_p(File.dirname(config_path)) if config_path

    # Write a backup of original
    open("#{config_path}.bak", 'w') { |f| f.write(orig) } if config_path && config.backup

    # Write out transformed file
    if config_path
      open(config_path, 'w') do |pipe|
        pipe.write(result)
      end

    # Handle write_cmd by dumping the body to a file and then piping that into the command.
    else
      file = Tempfile.new('rubber_write_cmd')

      begin
        file.write(result)
        file.close

        system("cat #{file.path} | #{config.write_cmd}")
      ensure
        file.unlink
      end
    end

    if config.write_cmd && ! fake_root && $?.exitstatus != 0
      raise "Config command failed execution:  #{config.write_cmd}"
    end

    # Set file permissions and owner if needed
    FileUtils.chmod(config.perms, config_path) if config.perms && config_path
    if config_path && (config.owner || config.group)
      if fake_root
        Rubber.logger.info("Not performing chown (#{config.owner}:#{config.group}) as a fake root was given")
      else
        FileUtils.chown(config.owner, config.group, config_path) 
      end
    end
    
    # Run post transform command if needed
    if config.post
      run_post_command(config.post)
    end

    # Schedule delayed_post transform command if needed
    if config.delayed_post
      Rubber.logger.info "Scheduling delayed_post: #{config.delayed_post}"
      @delayed_post_commands << config.delayed_post
    end
  end
end