2 changes: 1 addition & 1 deletion .sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,6 @@ Gemfile:
from_env: BEAKER_RSPEC_VERSION
- gem: beaker-puppet
from_env: BEAKER_PUPPET_VERSION
version: '~> 0.14'
version: '~> 0.16'
':development':
- gem: puppet-strings
19 changes: 15 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@

All notable changes to this project will be documented in this file.

## Release 0.1.0
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org).

**Features**
## [1.0.1] - 2018-08-15
### Added
- (MODULE-7443) Safely deserialize stringified array
- (PUP-9053) Enable localization
### Changed
- (maint) Move array parser logic into a util module
- (maint) Update beaker-puppet version to 0.16
- Include puppet 6 and remove default role
- (PUP-9052) Bump puppet req to at least puppet 6

**Bugfixes**
## [1.0.0] - 2018-06-07
### Summary
This is the initial release of the extracted augeas module

**Known Issues**
[1.0.1]: https://github.com/puppetlabs/puppetlabs-augeas_core/compare/1.0.0...1.0.1
[1.0.0]: https://github.com/puppetlabs/puppetlabs-augeas_core/releases/tag/1.0.0
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ group :system_tests do
gem "beaker-pe", require: false
gem "beaker-hostgenerator"
gem "beaker-rspec"
gem "beaker-puppet", *location_for(ENV['BEAKER_PUPPET_VERSION'] || '~> 0.14')
gem "beaker-puppet", *location_for(ENV['BEAKER_PUPPET_VERSION'] || '~> 0.16')
end

puppet_version = ENV['PUPPET_GEM_VERSION']
Expand Down
16 changes: 6 additions & 10 deletions lib/puppet/provider/augeas/augeas.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@
require 'puppet/util'
require 'puppet/util/diff'
require 'puppet/util/package'
require 'json'
require 'puppet_x/augeas/util/parser'

Puppet::Type.type(:augeas).provide(:augeas) do
include Puppet::Util
include Puppet::Util::Diff
include Puppet::Util::Package
include PuppetX::Augeas::Util::Parser

confine feature: :augeas

Expand Down Expand Up @@ -281,15 +282,15 @@ def process_values(cmd_array)
when '=='
begin
arg = clause_array.shift
new_array = to_array(arg)
new_array = parse_to_array(arg)
return_value = (values == new_array)
rescue
fail(_('Invalid array in command: %{cmd}') % { cmd: cmd_array.join(' ') })
end
when '!='
begin
arg = clause_array.shift
new_array = to_array(arg)
new_array = parse_to_array(arg)
return_value = (values != new_array)
rescue
fail(_('Invalid array in command: %{cmd}') % { cmd: cmd_array.join(' ') })
Expand Down Expand Up @@ -337,15 +338,15 @@ def process_match(cmd_array)
when '=='
begin
arg = clause_array.shift
new_array = to_array(arg)
new_array = parse_to_array(arg)
return_value = (result == new_array)
rescue
fail(_('Invalid array in command: %{cmd}') % { cmd: cmd_array.join(' ') })
end
when '!='
begin
arg = clause_array.shift
new_array = to_array(arg)
new_array = parse_to_array(arg)
return_value = (result != new_array)
rescue
fail(_('Invalid array in command: %{cmd}') % { cmd: cmd_array.join(' ') })
Expand Down Expand Up @@ -572,9 +573,4 @@ def do_execute_changes
end
end
# rubocop:enable Style/GuardClause

def to_array(string)
JSON.parse(string.tr("'", '"'))
end
private :to_array
end
215 changes: 215 additions & 0 deletions lib/puppet_x/augeas/util/parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
# rubocop:disable Style/Documentation
module PuppetX; end
module PuppetX::Augeas; end
module PuppetX::Augeas::Util; end
# rubocop:enable Style/Documentation

# Container for helpers to parse user provided data contained in manifests.
module PuppetX::Augeas::Util::Parser
TOKEN_ARRAY_CLOSE = %r{\s*\]\s*}
TOKEN_ARRAY_OPEN = %r{\s*\[\s*}
TOKEN_ARRAY_SEPARATOR = %r{\s*,\s*}
TOKEN_CLOSE_CURLY = %r|}|
TOKEN_DOUBLE_QUOTE = %r{"}
TOKEN_DOUBLE_QUOTE_ESCAPED_CHAR = %r{\\(["\\abtnvfres0-7xu])}
TOKEN_DOUBLE_QUOTE_UNESCAPED_CHAR = %r{[^"\\]}
TOKEN_HEX_CHAR = %r{[0-9a-fA-F]{1,2}}
TOKEN_OCTAL_CHAR = %r{[0-7]{1,3}}
TOKEN_OPEN_CURLY = %r|{|
TOKEN_SINGLE_QUOTE = %r{'}
TOKEN_SINGLE_QUOTE_ESCAPED_CHAR = %r{\\(['\\])}
TOKEN_SINGLE_QUOTE_UNESCAPED_CHAR = %r{[^'\\]}
TOKEN_SPACE = %r{\s}
TOKEN_UNICODE_LONG_HEX_CHAR = %r{[0-9a-fA-F]{1,6}}
TOKEN_UNICODE_SHORT_HEX_CHAR = %r{[0-9a-fA-F]{4}}

# Parse a string into the (nearly) equivalent Ruby array. This only handles
# arrays with string members (double-, or single-quoted), and does not
# support the full quite of escape sequences that Ruby allows in
# double-quoted strings.
#
# @param [String] The string to be parsed.
# @return [Array<String>] The parsed array elements, including handling any
# escape sequences.
def parse_to_array(string)
s = StringScanner.new(string)
match = array_open(s)
raise "Unexpected character in array at: #{s.rest}" if match.nil?

array_content = array_values(s)

match = array_close(s)
raise "Unexpected character in array at: #{s.rest}" if match.nil? || !s.empty?

array_content
end

def array_open(scanner)
scanner.scan(TOKEN_ARRAY_OPEN)
end
private :array_open

def array_close(scanner)
scanner.scan(TOKEN_ARRAY_CLOSE)
end
private :array_close

def array_separator(scanner)
scanner.scan(TOKEN_ARRAY_SEPARATOR)
end
private :array_separator

def single_quote_unescaped_char(scanner)
scanner.scan(TOKEN_SINGLE_QUOTE_UNESCAPED_CHAR)
end
private :single_quote_unescaped_char

def single_quote_escaped_char(scanner)
scanner.scan(TOKEN_SINGLE_QUOTE_ESCAPED_CHAR) && scanner[1]
end
private :single_quote_escaped_char

def single_quote_char(scanner)
single_quote_escaped_char(scanner) || single_quote_unescaped_char(scanner)
end
private :single_quote_char

def double_quote_unescaped_char(scanner)
scanner.scan(TOKEN_DOUBLE_QUOTE_UNESCAPED_CHAR)
end
private :double_quote_unescaped_char

# This handles the possible Ruby escape sequences in double-quoted strings,
# except for \M-x, \M-\C-x, \M-\cx, \c\M-x, \c?, and \C-?. The full list of
# escape sequences, and their meanings is taken from:
# https://github.com/ruby/ruby/blob/90fdfec11a4a42653722e2ce2a672d6e87a57b8e/doc/syntax/literals.rdoc#strings
def double_quote_escaped_char(scanner)
match = scanner.scan(TOKEN_DOUBLE_QUOTE_ESCAPED_CHAR)
return nil if match.nil?

case scanner[1]
when '\\' then return '\\'
when '"' then return '"'
when 'a' then return "\a"
when 'b' then return "\b"
when 't' then return "\t"
when 'n' then return "\n"
when 'v' then return "\v"
when 'f' then return "\f"
when 'r' then return "\r"
when 'e' then return "\e"
when 's' then return "\s"
when %r{[0-7]}
# Back the scanner up by one byte so we can grab all of the potential
# octal digits at the same time.
scanner.pos = scanner.pos - 1
octal_character = scanner.scan(TOKEN_OCTAL_CHAR)

return octal_character.to_i(8).chr
when 'x'
hex_character = scanner.scan(TOKEN_HEX_CHAR)
return nil if hex_character.nil?

hex_character.to_i(16).chr
when 'u'
return unicode_short_hex_character(scanner) || unicode_long_hex_characters(scanner)
else
# Not a valid escape sequence as far as we're concerned.
return nil
end
end
private :double_quote_escaped_char

def unicode_short_hex_character(scanner)
unicode_character = scanner.scan(TOKEN_UNICODE_SHORT_HEX_CHAR)
return nil if unicode_character.nil?

[unicode_character.hex].pack 'U'
end
private :unicode_short_hex_character

def unicode_long_hex_characters(scanner)
unicode_string = ''
return nil unless scanner.scan(TOKEN_OPEN_CURLY)

loop do
char = scanner.scan(TOKEN_UNICODE_LONG_HEX_CHAR)
break if char.nil?
unicode_string << [char.hex].pack('U')

separator = scanner.scan(TOKEN_SPACE)
break if separator.nil?
end

return nil if scanner.scan(TOKEN_CLOSE_CURLY).nil? || unicode_string.empty?

unicode_string
end
private :unicode_long_hex_characters

def single_quoted_string(scanner)
quoted_string = ''

match = scanner.scan(TOKEN_SINGLE_QUOTE)
return nil if match.nil?

loop do
match = single_quote_char(scanner)
break if match.nil?

quoted_string << match
end

match = scanner.scan(TOKEN_SINGLE_QUOTE)
return quoted_string if match

nil
end
private :single_quoted_string

def double_quote_char(scanner)
double_quote_escaped_char(scanner) || double_quote_unescaped_char(scanner)
end
private :double_quote_char

def double_quoted_string(scanner)
quoted_string = ''

match = scanner.scan(TOKEN_DOUBLE_QUOTE)
return nil if match.nil?

loop do
match = double_quote_char(scanner)
break if match.nil?

quoted_string << match
end

match = scanner.scan(TOKEN_DOUBLE_QUOTE)
return quoted_string if match

nil
end
private :double_quoted_string

def quoted_string(scanner)
single_quoted_string(scanner) || double_quoted_string(scanner)
end
private :quoted_string

def array_values(scanner)
values = []

loop do
match = quoted_string(scanner)
break if match.nil?
values << match

match = array_separator(scanner)
break if match.nil?
end

values
end
private :array_values
end
24 changes: 24 additions & 0 deletions locales/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# This is the project-specific configuration file for setting up
# fast_gettext for your project.
gettext:
# This is used for the name of the .pot and .po files; they will be
# called <project_name>.pot?
project_name: puppetlabs-augeas_core
# This is used in comments in the .pot and .po files to indicate what
# project the files belong to and should bea little more desctiptive than
# <project_name>
package_name: puppetlabs-augeas_core
# The locale that the default messages in the .pot file are in
default_locale: en
# The email used for sending bug reports.
bugs_address: docs@puppet.com
# The holder of the copyright.
copyright_holder: Puppet, Inc.
# This determines which comments in code should be eligible for translation.
# Any comments that start with this string will be externalized. (Leave
# empty to include all.)
comments_tag: TRANSLATOR
# Patterns for +Dir.glob+ used to find all files that might contain
# translatable content, relative to the project root directory
source_files:
- './lib/**/*.rb'
Loading