Search code examples
mysqlregexdjangopuppet

Puppet: file_line evaluates os.path.join


I'm trying to replace the Django-Settings line

'NAME': os.path.join(BASE_DIR , 'db.sqlite3'),

by

'NAME': os.path.join(BASE_DIR , 'db.mysql'),

but it doesn't find it.

The other line with the ENGINE parameter works fine. The problem must be the "os.path.join"-part.

  # replace sqlite3 db with mysql
    file { '/var/www/mysite/mysite/settings.py':
      ensure => present,
    }
    file_line { 'replace db engine':
      path                => '/var/www/mysite/mysite/settings.py',
      replace             => true,
      line                => "'ENGINE': 'django.db.backends.mysql',",
      match               => "'ENGINE': 'django.db.backends.sqlite3',",
      append_on_no_match  => false,
    }

    file_line { 'replace db name':
      path                => '/var/www/mysite/mysite/settings.py',
      replace             => true,
      line                => "\'NAME\': os.path.join(BASE_DIR , \'db.mysql\'),",
      match               => "\'NAME\': os.path.join(BASE_DIR , \'db.sqlite3\'),",
      append_on_no_match  => false,
    }

I tried it with \' and without \ .

Can somebody please help?

EDIT: So if I add something like this beforehand:

  class { '::mysql::server':
    root_password    => 'strongpassword',
    override_options => { 'mysqld' => { 'max_connections' => '1024' } }
  }

  mysql::db { 'mynewDB':
    user     => 'admin',
    password => 'secret',
    host     => 'master.puppetlabs.vm',
    sql        => '/tmp/states.sql',
    require => File['/tmp/states.sql'],
  }

Then I would replace the NAME parameter with "mynewDB"? Did I understand it correctly?


Solution

  • You need to bear in mind that the match parameter to a file_line resource conveys a regular expression, not a plain string. Puppet uses the Ruby flavor of regular expressions. In that dialect, like in many others, the parentheses (()) are metacharacters, signifying grouping. You must escape them if you want them to be interpreted as literals. Moreover, because Ruby regexes use the same escape character that Puppet strings do, you must also escape the escape character to pass it through Puppet to the underlying regex engine. On the other hand, you do not need to escape single quotes inside a double-quoted string, or vise versa, though doing so should not be harmful.

    Example:

    file_line { 'replace db name':
      path                => '/var/www/mysite/mysite/settings.py',
      replace             => true,
      line                => "'NAME': os.path.join(BASE_DIR , 'db.mysql'),",
      match               => "'NAME': os.path.join\\(BASE_DIR , 'db.sqlite3'\\),",
      append_on_no_match  => false,
    }
    

    But that's a bit of a poor design. If you're trying to ensure that the database you want is properly named (regardless of what the actual name should be), then to the greatest degree possible, you should match the line you want to manage in a way that does not depend on the current database name.

    I'm not knowledgeable about Django specifics, but if you can rely on only one NAME property being specified in the settings file then you might instead do this:

    file_line { 'replace db name':
      path                => '/var/www/mysite/mysite/settings.py',
      replace             => true,
      line                => 'Whatever the line should really be',
      match               => "\\s*'NAME':.*",
      append_on_no_match  => false,
    }
    

    The match expression there matches a line with an arbitrary amount of leading whitespace, followed by the literal characters 'NAME':, followed by anything.

    But you should also consider whether file_line is the right tool for the job at all. It really makes sense only if you need to accommodate some parts of the file being managed outside Puppet, which is an uncomfortable situation, albeit one that sometimes we are stuck with. If possible, though, it is better to allow Puppet to manage the entire file, including its complete contents.