├── data └── common.yaml ├── .yardopts ├── pdk.yaml ├── .rspec ├── .gitattributes ├── CODEOWNERS ├── .vscode └── extensions.json ├── spec ├── fixtures │ ├── inherit_test1 │ │ └── manifests │ │ │ └── init.pp │ ├── inherit_ini_setting │ │ └── lib │ │ │ └── puppet │ │ │ ├── type │ │ │ └── inherit_ini_setting.rb │ │ │ └── provider │ │ │ └── inherit_ini_setting │ │ │ └── ini_setting.rb │ ├── create_ini_settings_test │ │ └── manifests │ │ │ └── init.pp │ └── create_multiple_ini_settings │ │ └── manifests │ │ └── init.pp ├── functions │ ├── create_ini_settings_spec.rb │ └── inifile_create_ini_settings_spec.rb ├── spec_helper_acceptance.rb ├── acceptance │ ├── nodesets │ │ ├── centos-7-x64.yml │ │ ├── debian-8-x64.yml │ │ ├── default.yml │ │ └── docker │ │ │ ├── debian-8.yml │ │ │ ├── centos-7.yml │ │ │ └── ubuntu-14.04.yml │ ├── ini_subsetting_spec.rb │ └── ini_setting_spec.rb ├── default_facts.yml ├── classes │ ├── inherit_test1_spec.rb │ ├── create_ini_settings_test_spec.rb │ └── create_multiple_ini_settings_spec.rb ├── unit │ └── puppet │ │ ├── util │ │ ├── external_iterator_spec.rb │ │ ├── setting_value_spec.rb │ │ └── ini_file_spec.rb │ │ ├── provider │ │ ├── ini_setting │ │ │ └── inheritance_spec.rb │ │ └── ini_subsetting │ │ │ └── ruby_spec.rb │ │ └── type │ │ ├── ini_subetting_spec.rb │ │ └── ini_setting_spec.rb ├── spec_helper_acceptance_local.rb ├── spec_helper_local.rb └── spec_helper.rb ├── .gitpod.yml ├── CONTRIBUTING.md ├── .github ├── workflows │ ├── release.yml │ ├── mend.yml │ ├── nightly.yml │ ├── release_prep.yml │ └── ci.yml └── pull_request_template.md ├── .devcontainer ├── Dockerfile ├── devcontainer.json └── README.md ├── .puppet-lint.rc ├── .sync.yml ├── .gitignore ├── lib └── puppet │ ├── functions │ ├── create_ini_settings.rb │ └── inifile │ │ └── create_ini_settings.rb │ ├── util │ ├── external_iterator.rb │ ├── ini_file │ │ └── section.rb │ ├── setting_value.rb │ └── ini_file.rb │ ├── provider │ ├── ini_subsetting │ │ └── ruby.rb │ └── ini_setting │ │ └── ruby.rb │ └── type │ ├── ini_subsetting.rb │ └── ini_setting.rb ├── .fixtures.yml ├── examples ├── ini_setting.pp └── ini_subsetting.pp ├── NOTICE ├── .rubocop_todo.yml ├── hiera.yaml ├── .pdkignore ├── Rakefile ├── .gitpod.Dockerfile ├── provision.yaml ├── metadata.json ├── Gemfile ├── REFERENCE.md ├── README.md ├── LICENSE ├── HISTORY.md └── .rubocop.yml /data/common.yaml: -------------------------------------------------------------------------------- 1 | --- {} 2 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --markup markdown 2 | -------------------------------------------------------------------------------- /pdk.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | ignore: [] 3 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.rb eol=lf 2 | *.erb eol=lf 3 | *.pp eol=lf 4 | *.sh eol=lf 5 | *.epp eol=lf 6 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Setting ownership to the modules team 2 | * @puppetlabs/modules @bastelfreak @ekohl @smortex 3 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "puppet.puppet-vscode", 4 | "Shopify.ruby-lsp" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /spec/fixtures/inherit_test1/manifests/init.pp: -------------------------------------------------------------------------------- 1 | # Manufest inheriting ini_settings 2 | class inherit_test1 { 3 | inherit_ini_setting { 'valid_type': 4 | value => true, 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: .gitpod.Dockerfile 3 | 4 | tasks: 5 | - init: pdk bundle install 6 | 7 | vscode: 8 | extensions: 9 | - puppet.puppet-vscode@1.2.0:f5iEPbmOj6FoFTOV6q8LTg== 10 | -------------------------------------------------------------------------------- /spec/fixtures/inherit_ini_setting/lib/puppet/type/inherit_ini_setting.rb: -------------------------------------------------------------------------------- 1 | Puppet::Type.newtype(:inherit_ini_setting) do 2 | ensurable 3 | newparam(:setting, namevar: true) 4 | newproperty(:value) 5 | end 6 | -------------------------------------------------------------------------------- /spec/functions/create_ini_settings_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'create_ini_settings' do 6 | it_behaves_like 'create_ini_settings function' 7 | end 8 | -------------------------------------------------------------------------------- /spec/functions/inifile_create_ini_settings_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'inifile::create_ini_settings' do 6 | it_behaves_like 'create_ini_settings function' 7 | end 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Puppet modules 2 | 3 | Check out our [Contributing to Supported Modules Blog Post](https://puppetlabs.github.io/iac/docs/contributing_to_a_module.html) to find all the information that you will need. 4 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: "Publish module" 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | release: 8 | uses: "puppetlabs/cat-github-actions/.github/workflows/module_release.yml@main" 9 | secrets: "inherit" 10 | -------------------------------------------------------------------------------- /spec/spec_helper_acceptance.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'puppet_litmus' 4 | require 'spec_helper_acceptance_local' if File.file?(File.join(File.dirname(__FILE__), 'spec_helper_acceptance_local.rb')) 5 | 6 | PuppetLitmus.configure! 7 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/centos-7-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | centos-7-x64: 3 | roles: 4 | - agent 5 | - default 6 | platform: el-7-x86_64 7 | hypervisor: vagrant 8 | box: puppetlabs/centos-7.2-64-nocm 9 | CONFIG: 10 | type: foss 11 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/debian-8-x64.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | debian-8-x64: 3 | roles: 4 | - agent 5 | - default 6 | platform: debian-8-amd64 7 | hypervisor: vagrant 8 | box: puppetlabs/debian-8.2-64-nocm 9 | CONFIG: 10 | type: foss 11 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/default.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | ubuntu-1404-x64: 3 | roles: 4 | - agent 5 | - default 6 | platform: ubuntu-14.04-amd64 7 | hypervisor: vagrant 8 | box: puppetlabs/ubuntu-14.04-64-nocm 9 | CONFIG: 10 | type: foss 11 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM puppet/pdk:latest 2 | 3 | # [Optional] Uncomment this section to install additional packages. 4 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 5 | # && apt-get -y install --no-install-recommends 6 | 7 | -------------------------------------------------------------------------------- /spec/default_facts.yml: -------------------------------------------------------------------------------- 1 | # Use default_module_facts.yml for module specific facts. 2 | # 3 | # Facts specified here will override the values provided by rspec-puppet-facts. 4 | --- 5 | networking: 6 | ip: "172.16.254.254" 7 | ip6: "FE80:0000:0000:0000:AAAA:AAAA:AAAA" 8 | mac: "AA:AA:AA:AA:AA:AA" 9 | is_pe: false 10 | -------------------------------------------------------------------------------- /.github/workflows/mend.yml: -------------------------------------------------------------------------------- 1 | name: "mend" 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "main" 7 | schedule: 8 | - cron: "0 0 * * *" 9 | workflow_dispatch: 10 | 11 | jobs: 12 | 13 | mend: 14 | uses: "puppetlabs/cat-github-actions/.github/workflows/mend_ruby.yml@main" 15 | secrets: "inherit" 16 | -------------------------------------------------------------------------------- /spec/classes/inherit_test1_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | # We can't really test much here, apart from the type roundtrips though the 5 | # parser OK. 6 | describe 'inherit_test1' do 7 | it { 8 | expect(subject).to contain_inherit_ini_setting('valid_type').with('value' => 'true') 9 | } 10 | end 11 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Puppet Development Kit (Community)", 3 | "dockerFile": "Dockerfile", 4 | 5 | "settings": { 6 | "terminal.integrated.profiles.linux": { 7 | "bash": { 8 | "path": "bash" 9 | } 10 | } 11 | }, 12 | 13 | "extensions": [ 14 | "puppet.puppet-vscode", 15 | "rebornix.Ruby" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.puppet-lint.rc: -------------------------------------------------------------------------------- 1 | --fail-on-warnings 2 | --relative 3 | --no-80chars-check 4 | --no-140chars-check 5 | --no-class_inherits_from_params_class-check 6 | --no-autoloader_layout-check 7 | --no-documentation-check 8 | --no-single_quote_string_with_variables-check 9 | --ignore-paths=.vendor/**/*.pp,.bundle/**/*.pp,pkg/**/*.pp,spec/**/*.pp,tests/**/*.pp,types/**/*.pp,vendor/**/*.pp 10 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/docker/debian-8.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | debian-8-x64: 3 | platform: debian-8-amd64 4 | hypervisor: docker 5 | image: debian:8 6 | docker_preserve_image: true 7 | docker_cmd: '["/sbin/init"]' 8 | docker_image_commands: 9 | - 'apt-get update && apt-get install -y net-tools wget locales strace lsof && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen' 10 | CONFIG: 11 | trace_limit: 200 12 | -------------------------------------------------------------------------------- /spec/fixtures/create_ini_settings_test/manifests/init.pp: -------------------------------------------------------------------------------- 1 | # simple test class 2 | class create_ini_settings_test { 3 | $settings = { section1 => { 4 | setting1 => val1, 5 | }, 6 | section2 => { 7 | setting2 => val2, 8 | setting3 => { 9 | ensure => absent, 10 | }, 11 | }, 12 | } 13 | $defaults = { 14 | path => '/tmp/foo.ini', 15 | } 16 | create_ini_settings($settings,$defaults) 17 | } 18 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/docker/centos-7.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | centos-7-x64: 3 | platform: el-7-x86_64 4 | hypervisor: docker 5 | image: centos:7 6 | docker_preserve_image: true 7 | docker_cmd: '["/usr/sbin/init"]' 8 | # install various tools required to get the image up to usable levels 9 | docker_image_commands: 10 | - 'yum install -y crontabs tar wget openssl sysvinit-tools iproute which initscripts' 11 | CONFIG: 12 | trace_limit: 200 13 | -------------------------------------------------------------------------------- /.github/workflows/nightly.yml: -------------------------------------------------------------------------------- 1 | name: "nightly" 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * *" 6 | workflow_dispatch: 7 | 8 | jobs: 9 | Spec: 10 | uses: "puppetlabs/cat-github-actions/.github/workflows/module_ci.yml@main" 11 | secrets: "inherit" 12 | 13 | Acceptance: 14 | needs: Spec 15 | uses: "puppetlabs/cat-github-actions/.github/workflows/module_acceptance.yml@main" 16 | with: 17 | flags: "--nightly" 18 | secrets: "inherit" 19 | -------------------------------------------------------------------------------- /.github/workflows/release_prep.yml: -------------------------------------------------------------------------------- 1 | name: "Release Prep" 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: "Module version to be released. Must be a valid semver string. (1.2.3)" 8 | required: true 9 | 10 | jobs: 11 | release_prep: 12 | uses: "puppetlabs/cat-github-actions/.github/workflows/module_release_prep.yml@main" 13 | with: 14 | version: "${{ github.event.inputs.version }}" 15 | secrets: "inherit" 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: "ci" 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "main" 7 | workflow_dispatch: 8 | 9 | jobs: 10 | Spec: 11 | uses: "puppetlabs/cat-github-actions/.github/workflows/module_ci.yml@main" 12 | secrets: "inherit" 13 | 14 | Acceptance: 15 | needs: Spec 16 | uses: "puppetlabs/cat-github-actions/.github/workflows/module_acceptance.yml@main" 17 | with: 18 | flags: "--nightly" 19 | secrets: "inherit" 20 | -------------------------------------------------------------------------------- /spec/fixtures/inherit_ini_setting/lib/puppet/provider/inherit_ini_setting/ini_setting.rb: -------------------------------------------------------------------------------- 1 | Puppet::Type.type(:inherit_ini_setting).provide( 2 | :ini_setting, 3 | parent: Puppet::Type.type(:ini_setting).provider(:ruby), 4 | ) do 5 | def section 6 | '' # all global 7 | end 8 | 9 | # This type has no sections 10 | def self.namevar(_section_name, setting) 11 | setting 12 | end 13 | 14 | def self.file_path 15 | File.expand_path(File.dirname(__FILE__) + '/../../../../../../tmp/inherit_inifile.cfg') 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Summary 2 | Provide a detailed description of all the changes present in this pull request. 3 | 4 | ## Additional Context 5 | Add any additional context about the problem here. 6 | - [ ] Root cause and the steps to reproduce. (If applicable) 7 | - [ ] Thought process behind the implementation. 8 | 9 | ## Related Issues (if any) 10 | Mention any related issues or pull requests. 11 | 12 | ## Checklist 13 | - [ ] 🟢 Spec tests. 14 | - [ ] 🟢 Acceptance tests. 15 | - [ ] Manually verified. (For example `puppet apply`) -------------------------------------------------------------------------------- /spec/acceptance/nodesets/docker/ubuntu-14.04.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | ubuntu-1404-x64: 3 | platform: ubuntu-14.04-amd64 4 | hypervisor: docker 5 | image: ubuntu:14.04 6 | docker_preserve_image: true 7 | docker_cmd: '["/sbin/init"]' 8 | docker_image_commands: 9 | # ensure that upstart is booting correctly in the container 10 | - 'rm /usr/sbin/policy-rc.d && rm /sbin/initctl && dpkg-divert --rename --remove /sbin/initctl && apt-get update && apt-get install -y net-tools wget && locale-gen en_US.UTF-8' 11 | CONFIG: 12 | trace_limit: 200 13 | -------------------------------------------------------------------------------- /spec/fixtures/create_multiple_ini_settings/manifests/init.pp: -------------------------------------------------------------------------------- 1 | # Manifest creating multiple ini_settings 2 | class create_multiple_ini_settings { 3 | if $facts['os']['family'] == 'windows' { 4 | $defaults = { 'path' => 'C:/tmp/foo.ini' } 5 | } else { 6 | $defaults = { 'path' => '/tmp/foo.ini' } 7 | } 8 | 9 | $example = { 10 | 'section1' => { 11 | 'setting1' => 'value1', 12 | 'setting2' => { 13 | 'ensure' => 'absent', 14 | }, 15 | }, 16 | } 17 | 18 | inifile::create_ini_settings($example, $defaults) 19 | } 20 | -------------------------------------------------------------------------------- /.sync.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ".gitlab-ci.yml": 3 | delete: true 4 | appveyor.yml: 5 | delete: true 6 | .rubocop.yml: 7 | include_todos: true 8 | spec/spec_helper.rb: 9 | mock_with: ":rspec" 10 | coverage_report: true 11 | .gitpod.Dockerfile: 12 | unmanaged: false 13 | .gitpod.yml: 14 | unmanaged: false 15 | .github/workflows/auto_release.yml: 16 | unmanaged: false 17 | .github/workflows/ci.yml: 18 | unmanaged: false 19 | .github/workflows/nightly.yml: 20 | unmanaged: false 21 | .github/workflows/release.yml: 22 | unmanaged: false 23 | .travis.yml: 24 | delete: true 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | .*.sw[op] 3 | .metadata 4 | .yardoc 5 | .yardwarns 6 | *.iml 7 | /.bundle/ 8 | /.idea/ 9 | /.vagrant/ 10 | /coverage/ 11 | /bin/ 12 | /doc/ 13 | /Gemfile.local 14 | /Gemfile.lock 15 | /junit/ 16 | /log/ 17 | /pkg/ 18 | /spec/fixtures/manifests/ 19 | /spec/fixtures/modules/* 20 | /tmp/ 21 | /vendor/ 22 | /.vendor/ 23 | /convert_report.txt 24 | /update_report.txt 25 | .DS_Store 26 | .project 27 | .envrc 28 | /inventory.yaml 29 | /spec/fixtures/litmus_inventory.yaml 30 | .resource_types 31 | .modules 32 | .task_cache.json 33 | .plan_cache.json 34 | .rerun.json 35 | bolt-debug.log 36 | -------------------------------------------------------------------------------- /lib/puppet/functions/create_ini_settings.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # @summary DEPRECATED. Use the namespaced function [`inifile::create_ini_settings`](#inifilecreate_ini_settings) instead. 4 | Puppet::Functions.create_function(:create_ini_settings) do 5 | dispatch :deprecation_gen do 6 | repeated_param 'Any', :args 7 | end 8 | def deprecation_gen(*args) 9 | call_function('deprecation', 'create_ini_settings', 'This method is deprecated, please use inifile::create_ini_settings instead.') 10 | call_function('inifile::create_ini_settings', *args) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/puppet/util/external_iterator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Puppet::Util # rubocop:disable Style/ClassAndModuleChildren 4 | # 5 | # external_iterator.rb 6 | # 7 | class ExternalIterator 8 | def initialize(coll) 9 | @coll = coll 10 | @cur_index = -1 11 | end 12 | 13 | def next 14 | @cur_index += 1 15 | item_at(@cur_index) 16 | end 17 | 18 | def peek 19 | item_at(@cur_index + 1) 20 | end 21 | 22 | private 23 | 24 | def item_at(index) 25 | if @coll.length > index 26 | [@coll[index], index] 27 | else 28 | [nil, nil] 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /.fixtures.yml: -------------------------------------------------------------------------------- 1 | fixtures: 2 | symlinks: 3 | inifile: "#{source_dir}" 4 | create_ini_settings_test: "#{source_dir}/spec/fixtures/create_ini_settings_test" 5 | create_multiple_ini_settings: "#{source_dir}/spec/fixtures/create_multiple_ini_settings" 6 | inherit_ini_setting: "#{source_dir}/spec/fixtures/inherit_ini_setting" 7 | inherit_test1: "#{source_dir}/spec/fixtures/inherit_test1" 8 | repositories: 9 | stdlib: 'https://github.com/puppetlabs/puppetlabs-stdlib.git' 10 | facts: 'https://github.com/puppetlabs/puppetlabs-facts.git' 11 | puppet_agent: 12 | repo: 'https://github.com/puppetlabs/puppetlabs-puppet_agent.git' 13 | provision: 'https://github.com/puppetlabs/provision.git' 14 | -------------------------------------------------------------------------------- /examples/ini_setting.pp: -------------------------------------------------------------------------------- 1 | ini_setting { 'sample setting': 2 | ensure => present, 3 | path => '/tmp/foo.ini', 4 | section => 'foo', 5 | setting => 'foosetting', 6 | value => 'FOO!', 7 | } 8 | 9 | ini_setting { 'sample setting2': 10 | ensure => present, 11 | path => '/tmp/foo.ini', 12 | section => 'bar', 13 | setting => 'barsetting', 14 | value => 'BAR!', 15 | key_val_separator => '=', 16 | require => Ini_setting['sample setting'], 17 | } 18 | 19 | ini_setting { 'sample setting3': 20 | ensure => absent, 21 | path => '/tmp/foo.ini', 22 | section => 'bar', 23 | setting => 'bazsetting', 24 | require => Ini_setting['sample setting2'], 25 | } 26 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | inifile puppet module 2 | 3 | Copyright (C) 2012-2016 Puppet Labs, Inc. 4 | Copyright (C) 2012 Chris Price 5 | 6 | Puppet Labs can be contacted at: info@puppetlabs.com 7 | 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | -------------------------------------------------------------------------------- /.rubocop_todo.yml: -------------------------------------------------------------------------------- 1 | # This configuration was generated by 2 | # `rubocop --auto-gen-config` 3 | # on 2023-11-29 05:19:27 UTC using RuboCop version 1.48.1. 4 | # The point is for the user to remove these configuration records 5 | # one by one as the offenses are removed from the code base. 6 | # Note that changes in the inspected code, or installation of new 7 | # versions of RuboCop, may require this file to be generated again. 8 | 9 | # Offense count: 6 10 | # Configuration parameters: EnforcedStyle, IgnoreSharedExamples. 11 | # SupportedStyles: always, named_only 12 | RSpec/NamedSubject: 13 | Exclude: 14 | - 'spec/classes/create_ini_settings_test_spec.rb' 15 | - 'spec/classes/create_multiple_ini_settings_spec.rb' 16 | - 'spec/classes/inherit_test1_spec.rb' 17 | -------------------------------------------------------------------------------- /hiera.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 5 3 | 4 | defaults: # Used for any hierarchy level that omits these keys. 5 | datadir: data # This path is relative to hiera.yaml's directory. 6 | data_hash: yaml_data # Use the built-in YAML backend. 7 | 8 | hierarchy: 9 | - name: "osfamily/major release" 10 | paths: 11 | # Used to distinguish between Debian and Ubuntu 12 | - "os/%{facts.os.name}/%{facts.os.release.major}.yaml" 13 | - "os/%{facts.os.family}/%{facts.os.release.major}.yaml" 14 | # Used for Solaris 15 | - "os/%{facts.os.family}/%{facts.kernelrelease}.yaml" 16 | - name: "osfamily" 17 | paths: 18 | - "os/%{facts.os.name}.yaml" 19 | - "os/%{facts.os.family}.yaml" 20 | - name: 'common' 21 | path: 'common.yaml' 22 | -------------------------------------------------------------------------------- /.pdkignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | .*.sw[op] 3 | .metadata 4 | .yardoc 5 | .yardwarns 6 | *.iml 7 | /.bundle/ 8 | /.idea/ 9 | /.vagrant/ 10 | /coverage/ 11 | /bin/ 12 | /doc/ 13 | /Gemfile.local 14 | /Gemfile.lock 15 | /junit/ 16 | /log/ 17 | /pkg/ 18 | /spec/fixtures/manifests/ 19 | /spec/fixtures/modules/* 20 | /tmp/ 21 | /vendor/ 22 | /.vendor/ 23 | /convert_report.txt 24 | /update_report.txt 25 | .DS_Store 26 | .project 27 | .envrc 28 | /inventory.yaml 29 | /spec/fixtures/litmus_inventory.yaml 30 | .resource_types 31 | .modules 32 | .task_cache.json 33 | .plan_cache.json 34 | .rerun.json 35 | bolt-debug.log 36 | /.fixtures.yml 37 | /Gemfile 38 | /.gitattributes 39 | /.github/ 40 | /.gitignore 41 | /.pdkignore 42 | /.puppet-lint.rc 43 | /Rakefile 44 | /rakelib/ 45 | /.rspec 46 | /..yml 47 | /.yardopts 48 | /spec/ 49 | /.vscode/ 50 | /.sync.yml 51 | /.devcontainer/ 52 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler' 4 | require 'puppet_litmus/rake_tasks' if Gem.loaded_specs.key? 'puppet_litmus' 5 | require 'puppetlabs_spec_helper/rake_tasks' 6 | require 'puppet-syntax/tasks/puppet-syntax' 7 | require 'puppet-strings/tasks' if Gem.loaded_specs.key? 'puppet-strings' 8 | 9 | PuppetLint.configuration.send('disable_relative') 10 | PuppetLint.configuration.send('disable_80chars') 11 | PuppetLint.configuration.send('disable_140chars') 12 | PuppetLint.configuration.send('disable_class_inherits_from_params_class') 13 | PuppetLint.configuration.send('disable_autoloader_layout') 14 | PuppetLint.configuration.send('disable_documentation') 15 | PuppetLint.configuration.send('disable_single_quote_string_with_variables') 16 | PuppetLint.configuration.fail_on_warnings = true 17 | PuppetLint.configuration.ignore_paths = [".vendor/**/*.pp", ".bundle/**/*.pp", "pkg/**/*.pp", "spec/**/*.pp", "tests/**/*.pp", "types/**/*.pp", "vendor/**/*.pp"] 18 | 19 | -------------------------------------------------------------------------------- /spec/classes/create_ini_settings_test_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | # end-to-end test of the create_init_settings function 5 | describe 'create_ini_settings_test' do 6 | it { is_expected.to have_ini_setting_resource_count(3) } 7 | 8 | it { 9 | expect(subject).to contain_ini_setting('/tmp/foo.ini [section1] setting1').with( 10 | ensure: 'present', section: 'section1', 11 | setting: 'setting1', value: 'val1', 12 | path: '/tmp/foo.ini' 13 | ) 14 | } 15 | 16 | it { 17 | expect(subject).to contain_ini_setting('/tmp/foo.ini [section2] setting2').with( 18 | ensure: 'present', section: 'section2', 19 | setting: 'setting2', value: 'val2', 20 | path: '/tmp/foo.ini' 21 | ) 22 | } 23 | 24 | it { 25 | expect(subject).to contain_ini_setting('/tmp/foo.ini [section2] setting3').with( 26 | ensure: 'absent', section: 'section2', 27 | setting: 'setting3', path: '/tmp/foo.ini' 28 | ) 29 | } 30 | end 31 | -------------------------------------------------------------------------------- /.devcontainer/README.md: -------------------------------------------------------------------------------- 1 | # devcontainer 2 | 3 | 4 | For format details, see https://aka.ms/devcontainer.json. 5 | 6 | For config options, see the README at: 7 | https://github.com/microsoft/vscode-dev-containers/tree/v0.140.1/containers/puppet 8 | 9 | ``` json 10 | { 11 | "name": "Puppet Development Kit (Community)", 12 | "dockerFile": "Dockerfile", 13 | 14 | // Set *default* container specific settings.json values on container create. 15 | "settings": { 16 | "terminal.integrated.profiles.linux": { 17 | "bash": { 18 | "path": "bash", 19 | } 20 | } 21 | }, 22 | 23 | // Add the IDs of extensions you want installed when the container is created. 24 | "extensions": [ 25 | "puppet.puppet-vscode", 26 | "rebornix.Ruby" 27 | ], 28 | 29 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 30 | "forwardPorts": [], 31 | 32 | // Use 'postCreateCommand' to run commands after the container is created. 33 | "postCreateCommand": "pdk --version", 34 | } 35 | ``` 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /examples/ini_subsetting.pp: -------------------------------------------------------------------------------- 1 | ini_subsetting { 'sample subsetting': 2 | ensure => 'present', 3 | section => '', 4 | key_val_separator => '=', 5 | path => '/etc/default/pe-puppetdb', 6 | setting => 'JAVA_ARGS', 7 | subsetting => '-Xmx', 8 | value => '512m', 9 | } 10 | 11 | ini_subsetting { 'sample subsetting2': 12 | ensure => 'absent', 13 | section => '', 14 | key_val_separator => '=', 15 | path => '/etc/default/pe-puppetdb', 16 | setting => 'JAVA_ARGS', 17 | subsetting => '-Xms', 18 | } 19 | 20 | ini_subsetting { 'sample subsetting3': 21 | ensure => 'present', 22 | section => '', 23 | key_val_separator => '=', 24 | subsetting_key_val_separator => ':', 25 | path => '/etc/default/pe-puppetdb', 26 | setting => 'JAVA_ARGS', 27 | subsetting => '-XX', 28 | value => '+HeapDumpOnOutOfMemoryError', 29 | insert_type => 'after', 30 | insert_value => '-Xmx', 31 | } 32 | -------------------------------------------------------------------------------- /spec/unit/puppet/util/external_iterator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'puppet/util/external_iterator' 5 | 6 | describe Puppet::Util::ExternalIterator do 7 | subject_class = nil 8 | expected_values = nil 9 | 10 | before(:each) do 11 | subject_class = described_class.new(['a', 'b', 'c']) 12 | expected_values = [['a', 0], ['b', 1], ['c', 2]] 13 | end 14 | 15 | describe '#next' do 16 | it 'iterates over the items' do 17 | expected_values.each do |expected_pair| 18 | expect(subject_class.next).to eq(expected_pair) 19 | end 20 | end 21 | end 22 | 23 | describe '#peek' do 24 | it 'returns the 0th item repeatedly' do 25 | 3.times do |_i| 26 | expect(subject_class.peek).to eq(expected_values[0]) 27 | end 28 | end 29 | 30 | it 'does not advance the iterator, but should reflect calls to #next' do 31 | expected_values.each do |expected_pair| 32 | expect(subject_class.peek).to eq(expected_pair) 33 | expect(subject_class.peek).to eq(expected_pair) 34 | expect(subject_class.next).to eq(expected_pair) 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /.gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gitpod/workspace-full 2 | RUN sudo wget https://apt.puppet.com/puppet-tools-release-bionic.deb && \ 3 | wget https://apt.puppetlabs.com/puppet6-release-bionic.deb && \ 4 | sudo dpkg -i puppet6-release-bionic.deb && \ 5 | sudo dpkg -i puppet-tools-release-bionic.deb && \ 6 | sudo apt-get update && \ 7 | sudo apt-get install -y pdk zsh puppet-agent && \ 8 | sudo apt-get clean && \ 9 | sudo rm -rf /var/lib/apt/lists/* 10 | RUN sudo usermod -s $(which zsh) gitpod && \ 11 | sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" && \ 12 | echo "plugins=(git gitignore github gem pip bundler python ruby docker docker-compose)" >> /home/gitpod/.zshrc && \ 13 | echo 'PATH="$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/opt/puppetlabs/bin:/opt/puppetlabs/puppet/bin"' >> /home/gitpod/.zshrc && \ 14 | sudo /opt/puppetlabs/puppet/bin/gem install puppet-debugger hub -N && \ 15 | mkdir -p /home/gitpod/.config/puppet && \ 16 | /opt/puppetlabs/puppet/bin/ruby -r yaml -e "puts ({'disabled' => true}).to_yaml" > /home/gitpod/.config/puppet/analytics.yml 17 | RUN rm -f puppet6-release-bionic.deb puppet-tools-release-bionic.deb 18 | ENTRYPOINT /usr/bin/zsh 19 | -------------------------------------------------------------------------------- /spec/spec_helper_acceptance_local.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'singleton' 4 | 5 | class LitmusHelper 6 | include Singleton 7 | include PuppetLitmus 8 | end 9 | 10 | def setup_test_directory 11 | basedir = case os[:family] 12 | when 'windows' 13 | 'C:/inifile_test' 14 | else 15 | '/tmp/inifile_test' 16 | end 17 | pp = <<-MANIFEST 18 | file { '#{basedir}': 19 | ensure => directory, 20 | force => true, 21 | purge => true, 22 | recurse => true, 23 | } 24 | file { '#{basedir}/file': 25 | content => "file exists\n", 26 | force => true, 27 | } 28 | MANIFEST 29 | LitmusHelper.instance.apply_manifest(pp) 30 | basedir 31 | end 32 | 33 | def setup_puppet_config_file 34 | config_path = case os[:family] 35 | when 'windows' 36 | 'C:\ProgramData\PuppetLabs\puppet\etc\puppet.conf' 37 | else 38 | '/etc/puppetlabs/puppet/puppet.conf' 39 | end 40 | config_pp = <<-MANIFEST 41 | file { '#{config_path}': 42 | content => "[main]\nshow_diff = true", 43 | force => true, 44 | } 45 | MANIFEST 46 | LitmusHelper.instance.apply_manifest(config_pp) 47 | end 48 | -------------------------------------------------------------------------------- /spec/spec_helper_local.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | if ENV['COVERAGE'] == 'yes' 4 | require 'simplecov' 5 | require 'simplecov-console' 6 | require 'codecov' 7 | 8 | SimpleCov.formatters = [ 9 | SimpleCov::Formatter::HTMLFormatter, 10 | SimpleCov::Formatter::Console, 11 | SimpleCov::Formatter::Codecov, 12 | ] 13 | SimpleCov.start do 14 | track_files 'lib/**/*.rb' 15 | 16 | add_filter '/spec' 17 | 18 | # do not track vendored files 19 | add_filter '/vendor' 20 | add_filter '/.vendor' 21 | 22 | # do not track gitignored files 23 | # this adds about 4 seconds to the coverage check 24 | # this could definitely be optimized 25 | add_filter do |f| 26 | # system returns true if exit status is 0, which with git-check-ignore means file is ignored 27 | system("git check-ignore --quiet #{f.filename}") 28 | end 29 | end 30 | end 31 | 32 | shared_examples 'create_ini_settings function' do 33 | it { is_expected.not_to be_nil } 34 | it { is_expected.to run.with_params({}) } 35 | it { is_expected.to run.with_params({}, {}) } 36 | 37 | it { is_expected.to run.with_params('section' => { 'setting' => 'value' }).and_raise_error(Puppet::ParseError, %r{must pass the path parameter}) } 38 | it { is_expected.to run.with_params(1 => 2).and_raise_error(Puppet::ParseError, %r{Section 1 must contain a Hash}) } 39 | end 40 | -------------------------------------------------------------------------------- /spec/classes/create_multiple_ini_settings_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'create_multiple_ini_settings' do 6 | context 'when on a non-Windows platform', if: !Puppet::Util::Platform.windows? do 7 | let(:facts) do 8 | { 'os' => { 'family' => 'RedHat', 9 | 'release' => { 'major' => '7', 10 | 'minor' => '1', 11 | 'full' => '7.1.1503' } } } 12 | end 13 | 14 | it { is_expected.to compile } 15 | it { is_expected.to have_ini_setting_resource_count(2) } 16 | 17 | it { 18 | expect(subject).to contain_ini_setting('/tmp/foo.ini [section1] setting1').with( 19 | ensure: 'present', 20 | section: 'section1', 21 | setting: 'setting1', 22 | value: 'value1', 23 | path: '/tmp/foo.ini', 24 | ) 25 | } 26 | 27 | it { 28 | expect(subject).to contain_ini_setting('/tmp/foo.ini [section1] setting2').with( 29 | ensure: 'absent', 30 | section: 'section1', 31 | setting: 'setting2', 32 | path: '/tmp/foo.ini', 33 | ) 34 | } 35 | end 36 | 37 | context 'when on a Windows platform', if: Puppet::Util::Platform.windows? do 38 | let(:facts) do 39 | { 'os' => { 'family' => 'windows' } } 40 | end 41 | 42 | it { is_expected.to compile } 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/puppet/functions/inifile/create_ini_settings.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # @summary This function is used to create a set of ini_setting resources from a hash 4 | Puppet::Functions.create_function(:'inifile::create_ini_settings') do 5 | # @param settings 6 | # A hash of settings you want to create ini_setting resources from 7 | # @param defaults 8 | # A hash of defaults you would like to use in the ini_setting resources 9 | dispatch :default_impl do 10 | param 'Hash', :settings 11 | optional_param 'Hash', :defaults 12 | end 13 | 14 | def default_impl(settings, defaults = {}) 15 | resources = settings.keys.each_with_object({}) do |section, res| 16 | unless settings[section].is_a?(Hash) 17 | raise(Puppet::ParseError, 18 | _('create_ini_settings(): Section %{section} must contain a Hash') % { section: }) 19 | end 20 | 21 | path = defaults.merge(settings)['path'] 22 | raise Puppet::ParseError, _('create_ini_settings(): must pass the path parameter to the Ini_setting resource!') unless path 23 | 24 | settings[section].each do |setting, value| 25 | res["#{path} [#{section}] #{setting}"] = { 26 | 'ensure' => 'present', 27 | 'section' => section, 28 | 'setting' => setting 29 | }.merge(if value.is_a?(Hash) 30 | value 31 | else 32 | { 'value' => value } 33 | end) 34 | end 35 | end 36 | 37 | call_function('create_resources', 'ini_setting', resources, defaults) 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /provision.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | default: 3 | provisioner: docker 4 | images: 5 | - litmusimage/centos:7 6 | vagrant: 7 | provisioner: vagrant 8 | images: 9 | - centos/7 10 | - generic/ubuntu1804 11 | - gusztavvargadr/windows-server 12 | docker_deb: 13 | provisioner: docker 14 | images: 15 | - litmusimage/debian:8 16 | - litmusimage/debian:9 17 | - litmusimage/debian:10 18 | docker_ub_6: 19 | provisioner: docker 20 | images: 21 | - litmusimage/ubuntu:14.04 22 | - litmusimage/ubuntu:16.04 23 | - litmusimage/ubuntu:18.04 24 | - litmusimage/ubuntu:20.04 25 | docker_el7: 26 | provisioner: docker 27 | images: 28 | - litmusimage/centos:7 29 | - litmusimage/oraclelinux:7 30 | - litmusimage/scientificlinux:7 31 | docker_el8: 32 | provisioner: docker 33 | images: 34 | - litmusimage/centos:8 35 | release_checks_6: 36 | provisioner: abs 37 | images: 38 | - redhat-6-x86_64 39 | - redhat-7-x86_64 40 | - redhat-8-x86_64 41 | - centos-6-x86_64 42 | - centos-7-x86_64 43 | - centos-8-x86_64 44 | - oracle-5-x86_64 45 | - oracle-6-x86_64 46 | - oracle-7-x86_64 47 | - scientific-6-x86_64 48 | - scientific-7-x86_64 49 | - debian-8-x86_64 50 | - debian-9-x86_64 51 | - debian-10-x86_64 52 | - sles-12-x86_64 53 | - sles-15-x86_64 54 | - ubuntu-1404-x86_64 55 | - ubuntu-1604-x86_64 56 | - ubuntu-1804-x86_64 57 | - ubuntu-2004-x86_64 58 | - win-2008r2-x86_64 59 | - win-2012r2-x86_64 60 | - win-2016-x86_64 61 | - win-2019-x86_64 62 | - win-10-pro-x86_64 63 | release_checks_7: 64 | provisioner: abs 65 | images: 66 | - redhat-7-x86_64 67 | - redhat-8-x86_64 68 | - centos-7-x86_64 69 | - centos-8-x86_64 70 | - oracle-7-x86_64 71 | - scientific-7-x86_64 72 | - sles-12-x86_64 73 | - sles-15-x86_64 74 | - debian-9-x86_64 75 | - debian-10-x86_64 76 | - ubuntu-1804-x86_64 77 | - ubuntu-2004-x86_64 78 | - win-2012r2-x86_64 79 | - win-2016-x86_64 80 | - win-2019-x86_64 81 | - win-10-pro-x86_64 82 | -------------------------------------------------------------------------------- /spec/unit/puppet/provider/ini_setting/inheritance_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | # This is a reduced version of ruby_spec.rb just to ensure we can subclass as 6 | # documented 7 | $LOAD_PATH << './spec/fixtures/inherit_ini_setting/lib' 8 | 9 | describe Puppet::Type.type(:inherit_ini_setting).provider(:ini_setting) do 10 | include PuppetlabsSpec::Files 11 | 12 | let(:tmpfile) { tmpfilename('inherit_ini_setting_test') } 13 | 14 | def validate_file(expected_content, tmpfile) 15 | expect(File.read(tmpfile)).to eq(expected_content) 16 | end 17 | 18 | before :each do 19 | File.write(tmpfile, orig_content) 20 | end 21 | 22 | context 'when calling instances' do 23 | let(:orig_content) { '' } 24 | 25 | it 'parses nothing when the file is empty' do 26 | allow(described_class).to receive(:file_path).and_return(tmpfile) 27 | expect(described_class.instances).to eq([]) 28 | end 29 | 30 | context 'when the file has contents' do 31 | let(:orig_content) do 32 | <<-INIFILE 33 | # A comment 34 | red = blue 35 | green = purple 36 | INIFILE 37 | end 38 | 39 | it 'parses the results' do 40 | allow(described_class).to receive(:file_path).and_return(tmpfile) 41 | instances = described_class.instances 42 | expect(instances.size).to eq(2) 43 | # inherited version of namevar flattens the names 44 | names = instances.map do |instance| instance.instance_variable_get(:@property_hash)[:name] end # rubocop:disable Style/BlockDelimiters 45 | expect(names.sort).to eq(['green', 'red']) 46 | end 47 | end 48 | end 49 | 50 | context 'when ensuring that a setting is present' do 51 | let(:orig_content) { '' } 52 | 53 | it 'adds a value to the file' do 54 | allow(described_class).to receive(:file_path).and_return(tmpfile) 55 | resource = Puppet::Type::Inherit_ini_setting.new(setting: 'set_this', value: 'to_that') 56 | provider = described_class.new(resource) 57 | provider.create 58 | expect(validate_file("set_this=to_that\n", tmpfile)).to be_truthy 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.configure do |c| 4 | c.mock_with :rspec 5 | end 6 | 7 | require 'puppetlabs_spec_helper/module_spec_helper' 8 | require 'rspec-puppet-facts' 9 | 10 | require 'spec_helper_local' if File.file?(File.join(File.dirname(__FILE__), 'spec_helper_local.rb')) 11 | 12 | include RspecPuppetFacts 13 | 14 | default_facts = { 15 | puppetversion: Puppet.version, 16 | facterversion: Facter.version, 17 | } 18 | 19 | default_fact_files = [ 20 | File.expand_path(File.join(File.dirname(__FILE__), 'default_facts.yml')), 21 | File.expand_path(File.join(File.dirname(__FILE__), 'default_module_facts.yml')), 22 | ] 23 | 24 | default_fact_files.each do |f| 25 | next unless File.exist?(f) && File.readable?(f) && File.size?(f) 26 | 27 | begin 28 | require 'deep_merge' 29 | default_facts.deep_merge!(YAML.safe_load(File.read(f), permitted_classes: [], permitted_symbols: [], aliases: true)) 30 | rescue StandardError => e 31 | RSpec.configuration.reporter.message "WARNING: Unable to load #{f}: #{e}" 32 | end 33 | end 34 | 35 | # read default_facts and merge them over what is provided by facterdb 36 | default_facts.each do |fact, value| 37 | add_custom_fact fact, value, merge_facts: true 38 | end 39 | 40 | RSpec.configure do |c| 41 | c.default_facts = default_facts 42 | c.before :each do 43 | # set to strictest setting for testing 44 | # by default Puppet runs at warning level 45 | Puppet.settings[:strict] = :warning 46 | Puppet.settings[:strict_variables] = true 47 | end 48 | c.filter_run_excluding(bolt: true) unless ENV['GEM_BOLT'] 49 | c.after(:suite) do 50 | RSpec::Puppet::Coverage.report!(0) 51 | end 52 | 53 | # Filter backtrace noise 54 | backtrace_exclusion_patterns = [ 55 | %r{spec_helper}, 56 | %r{gems}, 57 | ] 58 | 59 | if c.respond_to?(:backtrace_exclusion_patterns) 60 | c.backtrace_exclusion_patterns = backtrace_exclusion_patterns 61 | elsif c.respond_to?(:backtrace_clean_patterns) 62 | c.backtrace_clean_patterns = backtrace_exclusion_patterns 63 | end 64 | end 65 | 66 | # Ensures that a module is defined 67 | # @param module_name Name of the module 68 | def ensure_module_defined(module_name) 69 | module_name.split('::').reduce(Object) do |last_module, next_module| 70 | last_module.const_set(next_module, Module.new) unless last_module.const_defined?(next_module, false) 71 | last_module.const_get(next_module, false) 72 | end 73 | end 74 | 75 | # 'spec_overrides' from sync.yml will appear below this line 76 | -------------------------------------------------------------------------------- /lib/puppet/provider/ini_subsetting/ruby.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require File.expand_path('../../util/ini_file', __dir__) 4 | require File.expand_path('../../util/setting_value', __dir__) 5 | 6 | Puppet::Type.type(:ini_subsetting).provide(:ruby) do 7 | desc ' 8 | Creates new ini_subsetting file, a specific config file with a provider that uses 9 | this as its parent and implements the method 10 | self.file_path, and that will provide the value for the path to the 11 | ini file.' 12 | def exists? 13 | setting_value.get_subsetting_value(subsetting, resource[:use_exact_match]) 14 | end 15 | 16 | def create 17 | setting_value.add_subsetting( 18 | subsetting, resource[:value], resource[:use_exact_match], 19 | resource[:insert_type], resource[:insert_value] 20 | ) 21 | ini_file.set_value(section, setting, key_val_separator, setting_value.get_value) 22 | ini_file.save 23 | @ini_file = nil 24 | @setting_value = nil 25 | end 26 | 27 | def destroy 28 | setting_value.remove_subsetting(subsetting, resource[:use_exact_match]) 29 | if setting_value.get_value.empty? && resource[:delete_if_empty] 30 | ini_file.remove_setting(section, setting) 31 | else 32 | ini_file.set_value(section, setting, key_val_separator, setting_value.get_value) 33 | end 34 | ini_file.save 35 | @ini_file = nil 36 | @setting_value = nil 37 | end 38 | 39 | def value 40 | setting_value.get_subsetting_value(subsetting) 41 | end 42 | 43 | def value=(value) 44 | setting_value.add_subsetting( 45 | subsetting, value, resource[:use_exact_match], 46 | resource[:insert_type], resource[:insert_value] 47 | ) 48 | ini_file.set_value(section, setting, key_val_separator, setting_value.get_value) 49 | ini_file.save 50 | end 51 | 52 | def section 53 | resource[:section] 54 | end 55 | 56 | def setting 57 | resource[:setting] 58 | end 59 | 60 | def subsetting 61 | resource[:subsetting] 62 | end 63 | 64 | def subsetting_separator 65 | resource[:subsetting_separator] 66 | end 67 | 68 | def file_path 69 | resource[:path] 70 | end 71 | 72 | def key_val_separator 73 | resource[:key_val_separator] || '=' 74 | end 75 | 76 | def subsetting_key_val_separator 77 | resource[:subsetting_key_val_separator] || '' 78 | end 79 | 80 | def quote_char 81 | resource[:quote_char] 82 | end 83 | 84 | private 85 | 86 | def ini_file 87 | @ini_file ||= Puppet::Util::IniFile.new(file_path, key_val_separator) 88 | end 89 | 90 | def setting_value 91 | @setting_value ||= Puppet::Util::SettingValue.new( 92 | ini_file.get_value(section, setting), 93 | subsetting_separator, quote_char, subsetting_key_val_separator 94 | ) 95 | end 96 | end 97 | -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "puppetlabs-inifile", 3 | "version": "6.2.0", 4 | "author": "puppetlabs", 5 | "summary": "Resource types for managing settings in INI files", 6 | "license": "Apache-2.0", 7 | "source": "https://github.com/puppetlabs/puppetlabs-inifile", 8 | "project_page": "https://github.com/puppetlabs/puppetlabs-inifile", 9 | "issues_url": "https://github.com/puppetlabs/puppetlabs-inifile/issues", 10 | "dependencies": [ 11 | { 12 | "name": "puppetlabs/stdlib", 13 | "version_requirement": ">= 4.13.0 < 10.0.0" 14 | } 15 | ], 16 | "operatingsystem_support": [ 17 | { 18 | "operatingsystem": "RedHat", 19 | "operatingsystemrelease": [ 20 | "7", 21 | "8", 22 | "9" 23 | ] 24 | }, 25 | { 26 | "operatingsystem": "CentOS", 27 | "operatingsystemrelease": [ 28 | "7", 29 | "8" 30 | ] 31 | }, 32 | { 33 | "operatingsystem": "OracleLinux", 34 | "operatingsystemrelease": [ 35 | "7" 36 | ] 37 | }, 38 | { 39 | "operatingsystem": "Scientific", 40 | "operatingsystemrelease": [ 41 | "7" 42 | ] 43 | }, 44 | { 45 | "operatingsystem": "SLES", 46 | "operatingsystemrelease": [ 47 | "12", 48 | "15" 49 | ] 50 | }, 51 | { 52 | "operatingsystem": "Debian", 53 | "operatingsystemrelease": [ 54 | "10", 55 | "11", 56 | "12" 57 | ] 58 | }, 59 | { 60 | "operatingsystem": "Ubuntu", 61 | "operatingsystemrelease": [ 62 | "18.04", 63 | "20.04", 64 | "22.04", 65 | "24.04" 66 | ] 67 | }, 68 | { 69 | "operatingsystem": "Solaris", 70 | "operatingsystemrelease": [ 71 | "11" 72 | ] 73 | }, 74 | { 75 | "operatingsystem": "Windows", 76 | "operatingsystemrelease": [ 77 | "10", 78 | "2012 R2", 79 | "2016", 80 | "2019", 81 | "2022" 82 | ] 83 | }, 84 | { 85 | "operatingsystem": "AIX", 86 | "operatingsystemrelease": [ 87 | "7.1" 88 | ] 89 | }, 90 | { 91 | "operatingsystem": "Rocky", 92 | "operatingsystemrelease": [ 93 | "8" 94 | ] 95 | }, 96 | { 97 | "operatingsystem": "AlmaLinux", 98 | "operatingsystemrelease": [ 99 | "8" 100 | ] 101 | } 102 | ], 103 | "requirements": [ 104 | { 105 | "name": "puppet", 106 | "version_requirement": ">= 8.0.0 < 9.0.0" 107 | } 108 | ], 109 | "template-url": "https://github.com/puppetlabs/pdk-templates.git#main", 110 | "template-ref": "heads/main-0-g9d5b193", 111 | "pdk-version": "3.5.0" 112 | } 113 | -------------------------------------------------------------------------------- /lib/puppet/util/ini_file/section.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Puppet::Util::IniFile # rubocop:disable Style/ClassAndModuleChildren 4 | # 5 | # section.rb 6 | # 7 | class Section 8 | # Some implementation details: 9 | # 10 | # * `name` will be set to the empty string for the 'global' section. 11 | # * there will always be a 'global' section, with a `start_line` of 0, 12 | # but if the file actually begins with a real section header on 13 | # the first line, then the 'global' section will have an 14 | # `end_line` of `nil`. 15 | # * `start_line` and `end_line` will be set to `nil` for a new non-global 16 | # section. 17 | def initialize(name, start_line, end_line, settings, indentation, empty_line_count = 0) 18 | @name = name 19 | @start_line = start_line 20 | @end_line = end_line 21 | @existing_settings = settings.nil? ? {} : settings 22 | @additional_settings = {} 23 | @indentation = indentation 24 | @empty_line_count = empty_line_count 25 | end 26 | 27 | attr_reader :name, :start_line, :end_line, :additional_settings, :indentation 28 | 29 | def global? 30 | @name == '' 31 | end 32 | 33 | def new_section? 34 | # a new section (global or named) will always have `end_line` 35 | # set to `nil` 36 | @end_line.nil? 37 | end 38 | 39 | def setting_names 40 | @existing_settings.keys | @additional_settings.keys 41 | end 42 | 43 | def get_value(setting_name) 44 | @existing_settings[setting_name] || @additional_settings[setting_name] 45 | end 46 | 47 | def existing_setting?(setting_name) 48 | @existing_settings.key?(setting_name) 49 | end 50 | 51 | # the global section is empty whenever it's new; 52 | # other sections are empty when they have no lines 53 | def empty? 54 | global? ? new_section? : (start_line == end_line || (end_line && (end_line - @empty_line_count)) == start_line) 55 | end 56 | 57 | def update_existing_setting(setting_name, value) 58 | @existing_settings[setting_name] = value 59 | end 60 | 61 | def remove_existing_setting(setting_name) 62 | @end_line -= 1 if @existing_settings.delete(setting_name) && @end_line 63 | end 64 | 65 | # This is a hacky method; it's basically called when we need to insert 66 | # a new setting but we don't want it to appear at the very end of the 67 | # section. Instead we hack it into the existing settings list and 68 | # increment our end_line number--this assumes that the caller (`ini_file`) 69 | # is doing some babysitting w/rt the other sections and the actual data 70 | # of the lines. 71 | def insert_inline_setting(setting_name, value) 72 | @existing_settings[setting_name] = value 73 | @end_line += 1 if @end_line 74 | end 75 | 76 | def set_additional_setting(setting_name, value) 77 | @additional_settings[setting_name] = value 78 | end 79 | 80 | # Decrement the start and end line numbers for the section (if they are 81 | # defined); this is intended to be called when a setting is removed 82 | # from a section that comes before this section in the ini file. 83 | def decrement_line_nums 84 | @start_line -= 1 if @start_line 85 | @end_line -= 1 if @end_line 86 | end 87 | 88 | # Increment the start and end line numbers for the section (if they are 89 | # defined); this is intended to be called when an inline setting is added 90 | # to a section that comes before this section in the ini file. 91 | def increment_line_nums 92 | @start_line += 1 if @start_line 93 | @end_line += 1 if @end_line 94 | end 95 | end 96 | end 97 | -------------------------------------------------------------------------------- /lib/puppet/type/ini_subsetting.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'digest/md5' 4 | 5 | Puppet::Type.newtype(:ini_subsetting) do 6 | desc 'ini_subsettings is used to manage multiple values in a setting in an INI file' 7 | ensurable do 8 | desc 'Ensurable method handles modeling creation. It creates an ensure property' 9 | defaultvalues 10 | defaultto :present 11 | end 12 | 13 | def munge_boolean_md5(value) 14 | case value 15 | when true, :true, 'true', :yes, 'yes' 16 | :true 17 | when false, :false, 'false', :no, 'no' 18 | :false 19 | when :md5, 'md5' 20 | :md5 21 | else 22 | raise(_('expected a boolean value or :md5')) 23 | end 24 | end 25 | newparam(:name, namevar: true) do 26 | desc 'An arbitrary name used as the identity of the resource.' 27 | end 28 | 29 | newparam(:section) do 30 | desc 'The name of the section in the ini file in which the setting should be defined.' 31 | defaultto('') 32 | end 33 | 34 | newparam(:setting) do 35 | desc 'The name of the setting to be defined.' 36 | end 37 | 38 | newparam(:subsetting) do 39 | desc 'The name of the subsetting to be defined.' 40 | end 41 | 42 | newparam(:subsetting_separator) do 43 | desc 'The separator string between subsettings. Defaults to the empty string.' 44 | defaultto(' ') 45 | end 46 | 47 | newparam(:subsetting_key_val_separator) do 48 | desc 'The separator string between the subsetting name and its value. Defaults to the empty string.' 49 | defaultto('') 50 | end 51 | 52 | newparam(:path) do 53 | desc 'The ini file Puppet will ensure contains the specified setting.' 54 | validate do |value| 55 | raise(Puppet::Error, _("File paths must be fully qualified, not '%{value}'") % { value: }) unless Puppet::Util.absolute_path?(value) 56 | end 57 | end 58 | newparam(:show_diff) do 59 | desc 'Whether to display differences when the setting changes.' 60 | defaultto :true 61 | newvalues(:true, :md5, :false) 62 | 63 | munge do |value| 64 | @resource.munge_boolean_md5(value) 65 | end 66 | end 67 | 68 | newparam(:key_val_separator) do 69 | desc 'The separator string to use between each setting name and value.' 70 | defaultto(' = ') 71 | end 72 | 73 | newparam(:quote_char) do 74 | desc "The character used to quote the entire value of the setting. Valid values are '', '\"' and \"'\"" 75 | defaultto('') 76 | 77 | validate do |value| 78 | raise Puppet::Error, _(%q(:quote_char valid values are '', '"' and "'")) unless value.match?(%r{^["']?$}) 79 | end 80 | end 81 | 82 | newparam(:use_exact_match) do 83 | desc 'Set to true if your subsettings don\'t have values and you want to use exact matches to determine if the subsetting exists.' 84 | newvalues(:true, :false) 85 | defaultto(:false) 86 | end 87 | 88 | newproperty(:value) do 89 | desc 'The value of the subsetting to be defined.' 90 | 91 | def should_to_s(newvalue) 92 | if @resource[:show_diff] == :true && Puppet[:show_diff] 93 | newvalue 94 | elsif @resource[:show_diff] == :md5 && Puppet[:show_diff] 95 | "{md5}#{Digest::MD5.hexdigest(newvalue.to_s)}" 96 | else 97 | '[redacted sensitive information]' 98 | end 99 | end 100 | 101 | def is_to_s(value) # rubocop:disable Naming/PredicateName : Changing breaks the code (./.bundle/gems/gems/puppet-5.3.3-universal-darwin/lib/puppet/parameter.rb:525:in `to_s') 102 | should_to_s(value) 103 | end 104 | end 105 | 106 | newparam(:insert_type) do 107 | desc <<-DOCUMENTATION 108 | Where the new subsetting item should be inserted 109 | 110 | * :start - insert at the beginning of the line. 111 | * :end - insert at the end of the line (default). 112 | * :before - insert before the specified element if possible. 113 | * :after - insert after the specified element if possible. 114 | * :index - insert at the specified index number. 115 | DOCUMENTATION 116 | 117 | newvalues(:start, :end, :before, :after, :index) 118 | defaultto(:end) 119 | end 120 | 121 | newparam(:insert_value) do 122 | desc 'The value for the insert types which require one.' 123 | end 124 | 125 | newparam(:delete_if_empty) do 126 | desc 'Set to true to delete the parent setting when the subsetting is empty instead of writing an empty string' 127 | newvalues(:true, :false) 128 | defaultto(:false) 129 | end 130 | end 131 | -------------------------------------------------------------------------------- /lib/puppet/type/ini_setting.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'digest/md5' 4 | require 'puppet/parameter/boolean' 5 | 6 | Puppet::Type.newtype(:ini_setting) do 7 | desc 'ini_settings is used to manage a single setting in an INI file' 8 | ensurable do 9 | desc 'Ensurable method handles modeling creation. It creates an ensure property' 10 | newvalue(:present) do 11 | provider.create 12 | end 13 | newvalue(:absent) do 14 | provider.destroy 15 | end 16 | def insync?(current) 17 | if @resource[:refreshonly] 18 | true 19 | else 20 | current == should 21 | end 22 | end 23 | defaultto :present 24 | end 25 | 26 | def munge_boolean_md5(value) 27 | case value 28 | when true, :true, 'true', :yes, 'yes' 29 | :true 30 | when false, :false, 'false', :no, 'no' 31 | :false 32 | when :md5, 'md5' 33 | :md5 34 | else 35 | raise(_('expected a boolean value or :md5')) 36 | end 37 | end 38 | newparam(:name, namevar: true) do 39 | desc 'An arbitrary name used as the identity of the resource.' 40 | end 41 | 42 | newparam(:section) do 43 | desc 'The name of the section in the ini file in which the setting should be defined.' 44 | defaultto('') 45 | end 46 | 47 | newparam(:setting) do 48 | desc 'The name of the setting to be defined.' 49 | munge do |value| 50 | Puppet.warn('Settings should not have spaces in the value, we are going to strip the whitespace') if value.match?(%r{(^\s|\s$)}) 51 | value.strip 52 | end 53 | end 54 | 55 | newparam(:force_new_section_creation, boolean: true, parent: Puppet::Parameter::Boolean) do 56 | desc 'Create setting only if the section exists' 57 | defaultto(true) 58 | end 59 | 60 | newparam(:path) do 61 | desc 'The ini file Puppet will ensure contains the specified setting.' 62 | validate do |value| 63 | raise(Puppet::Error, _("File paths must be fully qualified, not '%{value}'") % { value: }) unless Puppet::Util.absolute_path?(value) 64 | end 65 | end 66 | 67 | newparam(:show_diff) do 68 | desc 'Whether to display differences when the setting changes.' 69 | 70 | defaultto :true 71 | 72 | newvalues(:true, :md5, :false) 73 | 74 | munge do |value| 75 | @resource.munge_boolean_md5(value) 76 | end 77 | end 78 | 79 | newparam(:key_val_separator) do 80 | desc 'The separator string to use between each setting name and value.' 81 | defaultto(' = ') 82 | end 83 | 84 | newproperty(:value) do 85 | desc 'The value of the setting to be defined.' 86 | 87 | munge do |value| 88 | value = value.unwrap if value.respond_to?(:unwrap) 89 | if ([true, false].include? value) || value.is_a?(Numeric) 90 | value.to_s 91 | else 92 | value.strip.to_s 93 | end 94 | end 95 | 96 | def should_to_s(newvalue) 97 | if @resource[:show_diff] == :true && Puppet[:show_diff] 98 | newvalue 99 | elsif @resource[:show_diff] == :md5 && Puppet[:show_diff] 100 | "{md5}#{Digest::MD5.hexdigest(newvalue.to_s)}" 101 | else 102 | '[redacted sensitive information]' 103 | end 104 | end 105 | 106 | def is_to_s(value) # rubocop:disable Naming/PredicateName : Changing breaks the code (./.bundle/gems/gems/puppet-5.3.3-universal-darwin/lib/puppet/parameter.rb:525:in `to_s') 107 | should_to_s(value) 108 | end 109 | 110 | def insync?(current) 111 | if @resource[:refreshonly] 112 | true 113 | else 114 | current == should 115 | end 116 | end 117 | end 118 | 119 | newparam(:section_prefix) do 120 | desc 'The prefix to the section name\'s header.' 121 | defaultto('[') 122 | end 123 | 124 | newparam(:section_suffix) do 125 | desc 'The suffix to the section name\'s header.' 126 | defaultto(']') 127 | end 128 | 129 | newparam(:indent_char) do 130 | desc 'The character to indent new settings with.' 131 | defaultto(' ') 132 | end 133 | 134 | newparam(:indent_width) do 135 | desc 'The number of indent_chars to use to indent a new setting.' 136 | end 137 | 138 | newparam(:refreshonly, boolean: true, parent: Puppet::Parameter::Boolean) do 139 | desc 'A flag indicating whether or not the ini_setting should be updated only when called as part of a refresh event' 140 | defaultto false 141 | end 142 | 143 | def refresh 144 | return provider.destroy if self[:ensure] == :absent && self[:refreshonly] 145 | 146 | # update the value in the provider, which will save the value to the ini file 147 | provider.value = self[:value] if self[:refreshonly] 148 | end 149 | 150 | autorequire(:file) do 151 | Pathname.new(self[:path]).parent.to_s 152 | end 153 | end 154 | -------------------------------------------------------------------------------- /lib/puppet/provider/ini_setting/ruby.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require File.expand_path('../../util/ini_file', __dir__) 4 | 5 | Puppet::Type.type(:ini_setting).provide(:ruby) do 6 | def self.instances 7 | desc ' 8 | Creates new ini_setting file, a specific config file with a provider that uses 9 | this as its parent and implements the method 10 | self.file_path, and that will provide the value for the path to the 11 | ini file.' 12 | raise(Puppet::Error, 'Ini_settings only support collecting instances when a file path is hard coded') unless respond_to?(:file_path) 13 | 14 | # figure out what to do about the seperator 15 | ini_file = Puppet::Util::IniFile.new(file_path, '=') 16 | resources = [] 17 | ini_file.section_names.each do |section_name| 18 | ini_file.get_settings(section_name).each do |setting, value| 19 | resources.push( 20 | new( 21 | name: namevar(section_name, setting), 22 | value:, 23 | ensure: :present, 24 | ), 25 | ) 26 | end 27 | end 28 | resources 29 | end 30 | 31 | def self.namevar(section_name, setting) 32 | setting.nil? ? section_name : "#{section_name}/#{setting}" 33 | end 34 | 35 | def exists? 36 | if setting.nil? 37 | ini_file.section_names.include?(section) 38 | elsif ini_file.section?(section) 39 | !ini_file.get_value(section, setting).nil? 40 | elsif resource.parameters.key?(:force_new_section_creation) && !resource[:force_new_section_creation] 41 | # for backwards compatibility, if a user is using their own ini_setting 42 | # types but does not have this parameter, we need to fall back to the 43 | # previous functionality which was to create the section. Anyone 44 | # wishing to leverage this setting must define it in their provider 45 | # type. See comments on 46 | # https://github.com/puppetlabs/puppetlabs-inifile/pull/286 47 | resource[:ensure] = :absent 48 | resource[:force_new_section_creation] 49 | elsif resource.parameters.key?(:force_new_section_creation) && resource[:force_new_section_creation] 50 | !resource[:force_new_section_creation] 51 | else 52 | false 53 | end 54 | end 55 | 56 | def create 57 | if setting.nil? && resource[:value].nil? 58 | ini_file.set_value(section) 59 | else 60 | ini_file.set_value(section, setting, separator, resource[:value]) 61 | end 62 | ini_file.save 63 | @ini_file = nil 64 | end 65 | 66 | def destroy 67 | ini_file.remove_setting(section, setting) 68 | ini_file.save 69 | @ini_file = nil 70 | end 71 | 72 | def value 73 | ini_file.get_value(section, setting) 74 | end 75 | 76 | def value=(_value) 77 | if setting.nil? && resource[:value].nil? 78 | ini_file.set_value(section) 79 | else 80 | ini_file.set_value(section, setting, separator, resource[:value]) 81 | end 82 | ini_file.save 83 | end 84 | 85 | def section 86 | # this method is here so that it can be overridden by a child provider 87 | resource[:section] 88 | end 89 | 90 | def setting 91 | # this method is here so that it can be overridden by a child provider 92 | resource[:setting] 93 | end 94 | 95 | def file_path 96 | # this method is here to support purging and sub-classing. 97 | # if a user creates a type and subclasses our provider and provides a 98 | # 'file_path' method, then they don't have to specify the 99 | # path as a parameter for every ini_setting declaration. 100 | # This implementation allows us to support that while still 101 | # falling back to the parameter value when necessary. 102 | if self.class.respond_to?(:file_path) 103 | self.class.file_path 104 | else 105 | resource[:path] 106 | end 107 | end 108 | 109 | def separator 110 | if resource.class.validattr?(:key_val_separator) 111 | resource[:key_val_separator] || '=' 112 | else 113 | '=' 114 | end 115 | end 116 | 117 | def section_prefix 118 | if resource.class.validattr?(:section_prefix) 119 | resource[:section_prefix] || '[' 120 | else 121 | '[' 122 | end 123 | end 124 | 125 | def section_suffix 126 | if resource.class.validattr?(:section_suffix) 127 | resource[:section_suffix] || ']' 128 | else 129 | ']' 130 | end 131 | end 132 | 133 | def indent_char 134 | if resource.class.validattr?(:indent_char) 135 | resource[:indent_char] || ' ' 136 | else 137 | ' ' 138 | end 139 | end 140 | 141 | def indent_width 142 | if resource.class.validattr?(:indent_width) 143 | resource[:indent_width] || nil 144 | else 145 | nil 146 | end 147 | end 148 | 149 | private 150 | 151 | def ini_file 152 | @ini_file ||= Puppet::Util::IniFile.new(file_path, separator, section_prefix, section_suffix, indent_char, indent_width) 153 | end 154 | end 155 | -------------------------------------------------------------------------------- /spec/unit/puppet/util/setting_value_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'puppet/util/setting_value' 5 | 6 | INIT_VALUE_SPACE = '"-Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof"' 7 | INIT_VALUE_COMMA = '"-Xmx192m,-XX:+HeapDumpOnOutOfMemoryError,-XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof"' 8 | INIT_VALUE_UNQUOTED = '-Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof' 9 | QUOTE_CHAR = '"' 10 | 11 | describe Puppet::Util::SettingValue do 12 | describe 'space subsetting separator' do 13 | let(:setting_value) { described_class.new(INIT_VALUE_SPACE, ' ') } 14 | 15 | it 'gets the original value' do 16 | expect(setting_value.get_value).to eq(INIT_VALUE_SPACE) 17 | end 18 | 19 | it 'gets the correct value' do 20 | expect(setting_value.get_subsetting_value('-Xmx')).to eq('192m') 21 | end 22 | 23 | it 'adds a new value #correct' do 24 | setting_value.add_subsetting('-Xms', '256m') 25 | expect(setting_value.get_subsetting_value('-Xms')).to eq('256m') 26 | end 27 | 28 | it 'adds a new value #original' do 29 | setting_value.add_subsetting('-Xms', '256m') 30 | expect(setting_value.get_value).to eq("#{INIT_VALUE_SPACE[0, INIT_VALUE_SPACE.length - 1]} -Xms256m\"") 31 | end 32 | 33 | it 'changes existing value' do 34 | setting_value.add_subsetting('-Xmx', '512m') 35 | expect(setting_value.get_subsetting_value('-Xmx')).to eq('512m') 36 | end 37 | 38 | it 'removes existing value' do 39 | setting_value.remove_subsetting('-Xmx') 40 | expect(setting_value.get_subsetting_value('-Xmx')).to be_nil 41 | end 42 | end 43 | 44 | describe 'comma subsetting separator' do 45 | let(:setting_value) { described_class.new(INIT_VALUE_COMMA, ',') } 46 | 47 | it 'gets the original value' do 48 | expect(setting_value.get_value).to eq(INIT_VALUE_COMMA) 49 | end 50 | 51 | it 'gets the correct value' do 52 | expect(setting_value.get_subsetting_value('-Xmx')).to eq('192m') 53 | end 54 | 55 | it 'adds a new value #actual' do 56 | setting_value.add_subsetting('-Xms', '256m') 57 | expect(setting_value.get_subsetting_value('-Xms')).to eq('256m') 58 | end 59 | 60 | it 'adds a new value #original' do 61 | setting_value.add_subsetting('-Xms', '256m') 62 | expect(setting_value.get_value).to eq("#{INIT_VALUE_COMMA[0, INIT_VALUE_COMMA.length - 1]},-Xms256m\"") 63 | end 64 | 65 | it 'changes existing value' do 66 | setting_value.add_subsetting('-Xmx', '512m') 67 | expect(setting_value.get_subsetting_value('-Xmx')).to eq('512m') 68 | end 69 | 70 | it 'removes existing value' do 71 | setting_value.remove_subsetting('-Xmx') 72 | expect(setting_value.get_subsetting_value('-Xmx')).to be_nil 73 | end 74 | end 75 | 76 | describe 'quote_char parameter' do 77 | it 'gets quoted empty string if original value was empty' do 78 | setting_value = described_class.new(nil, ' ', QUOTE_CHAR) 79 | expect(setting_value.get_value).to eq(QUOTE_CHAR * 2) 80 | end 81 | 82 | it 'quotes the setting when adding a value #actual' do 83 | setting_value = described_class.new(INIT_VALUE_UNQUOTED, ' ', QUOTE_CHAR) 84 | setting_value.add_subsetting('-Xms', '256m') 85 | 86 | expect(setting_value.get_subsetting_value('-Xms')).to eq('256m') 87 | end 88 | 89 | it 'quotes the setting when adding a value #original' do 90 | setting_value = described_class.new(INIT_VALUE_UNQUOTED, ' ', QUOTE_CHAR) 91 | setting_value.add_subsetting('-Xms', '256m') 92 | 93 | expect(setting_value.get_value).to eq("#{QUOTE_CHAR}#{INIT_VALUE_UNQUOTED} -Xms256m#{QUOTE_CHAR}") 94 | end 95 | 96 | it 'quotes the setting when changing an existing value #value' do 97 | setting_value = described_class.new(INIT_VALUE_UNQUOTED, ' ', QUOTE_CHAR) 98 | setting_value.add_subsetting('-Xmx', '512m') 99 | 100 | expect(setting_value.get_subsetting_value('-Xmx')).to eq('512m') 101 | end 102 | 103 | it 'quotes the setting when changing an existing value #quotes' do 104 | setting_value = described_class.new(INIT_VALUE_UNQUOTED, ' ', QUOTE_CHAR) 105 | setting_value.add_subsetting('-Xmx', '512m') 106 | 107 | expect(setting_value.get_value).to match(%r{^#{Regexp.quote(QUOTE_CHAR)}.*#{Regexp.quote(QUOTE_CHAR)}$}) 108 | end 109 | 110 | it 'quotes the setting when removing an existing value #value' do 111 | setting_value = described_class.new(INIT_VALUE_UNQUOTED, ' ', QUOTE_CHAR) 112 | setting_value.remove_subsetting('-Xmx') 113 | 114 | expect(setting_value.get_subsetting_value('-Xmx')).to be_nil 115 | end 116 | end 117 | 118 | it 'quotes the setting when removing an existing value #quotes' do 119 | setting_value = described_class.new(INIT_VALUE_UNQUOTED, ' ', QUOTE_CHAR) 120 | setting_value.remove_subsetting('-Xmx') 121 | 122 | expect(setting_value.get_value).to match(%r{^#{Regexp.quote(QUOTE_CHAR)}.*#{Regexp.quote(QUOTE_CHAR)}$}) 123 | end 124 | end 125 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # For puppetcore, set GEM_SOURCE_PUPPETCORE = 'https://rubygems-puppetcore.puppet.com' 4 | gemsource_default = ENV['GEM_SOURCE'] || 'https://rubygems.org' 5 | gemsource_puppetcore = if ENV['PUPPET_FORGE_TOKEN'] 6 | 'https://rubygems-puppetcore.puppet.com' 7 | else 8 | ENV['GEM_SOURCE_PUPPETCORE'] || gemsource_default 9 | end 10 | source gemsource_default 11 | 12 | def location_for(place_or_constraint, fake_constraint = nil, opts = {}) 13 | git_url_regex = /\A(?(?:https?|git)[:@][^#]*)(?:#(?.*))?/ 14 | file_url_regex = %r{\Afile://(?.*)} 15 | 16 | if place_or_constraint && (git_url = place_or_constraint.match(git_url_regex)) 17 | # Git source → ignore :source, keep fake_constraint 18 | [fake_constraint, { git: git_url[:url], branch: git_url[:branch], require: false }].compact 19 | 20 | elsif place_or_constraint && (file_url = place_or_constraint.match(file_url_regex)) 21 | # File source → ignore :source, keep fake_constraint or default >= 0 22 | [fake_constraint || '>= 0', { path: File.expand_path(file_url[:path]), require: false }] 23 | 24 | else 25 | # Plain version constraint → merge opts (including :source if provided) 26 | [place_or_constraint, { require: false }.merge(opts)] 27 | end 28 | end 29 | 30 | # Print debug information if DEBUG_GEMS or VERBOSE is set 31 | def print_gem_statement_for(gems) 32 | puts 'DEBUG: Gem definitions that will be generated:' 33 | gems.each do |gem_name, gem_params| 34 | puts "DEBUG: gem #{([gem_name.inspect] + gem_params.map(&:inspect)).join(', ')}" 35 | end 36 | end 37 | 38 | group :development do 39 | gem "json", '= 2.6.1', require: false if Gem::Requirement.create(['>= 3.1.0', '< 3.1.3']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 40 | gem "json", '= 2.6.3', require: false if Gem::Requirement.create(['>= 3.2.0', '< 4.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 41 | gem "racc", '~> 1.4.0', require: false if Gem::Requirement.create(['>= 2.7.0', '< 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 42 | gem "deep_merge", '~> 1.2.2', require: false 43 | gem "voxpupuli-puppet-lint-plugins", '~> 5.0', require: false 44 | gem "facterdb", '~> 2.1', require: false if Gem::Requirement.create(['< 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 45 | gem "facterdb", '~> 3.0', require: false if Gem::Requirement.create(['>= 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 46 | gem "metadata-json-lint", '~> 4.0', require: false 47 | gem "json-schema", '< 5.1.1', require: false 48 | gem "rspec-puppet-facts", '~> 4.0', require: false if Gem::Requirement.create(['< 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 49 | gem "rspec-puppet-facts", '~> 5.0', require: false if Gem::Requirement.create(['>= 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 50 | gem "dependency_checker", '~> 1.0.0', require: false 51 | gem "parallel_tests", '= 3.12.1', require: false 52 | gem "pry", '~> 0.10', require: false 53 | gem "simplecov-console", '~> 0.9', require: false 54 | gem "puppet-debugger", '~> 1.6', require: false 55 | gem "rubocop", '~> 1.50.0', require: false 56 | gem "rubocop-performance", '= 1.16.0', require: false 57 | gem "rubocop-rspec", '= 2.19.0', require: false 58 | gem "rb-readline", '= 0.5.5', require: false, platforms: [:mswin, :mingw, :x64_mingw] 59 | gem "bigdecimal", '< 3.2.2', require: false, platforms: [:mswin, :mingw, :x64_mingw] 60 | end 61 | group :development, :release_prep do 62 | gem "puppet-strings", '~> 4.0', require: false 63 | gem "puppetlabs_spec_helper", '~> 8.0', require: false 64 | gem "puppet-blacksmith", '~> 7.0', require: false 65 | end 66 | group :system_tests do 67 | gem "puppet_litmus", '~> 2.0', require: false, platforms: [:ruby, :x64_mingw] if !ENV['PUPPET_FORGE_TOKEN'].to_s.empty? 68 | gem "puppet_litmus", '~> 1.0', require: false, platforms: [:ruby, :x64_mingw] if ENV['PUPPET_FORGE_TOKEN'].to_s.empty? 69 | gem "CFPropertyList", '< 3.0.7', require: false, platforms: [:mswin, :mingw, :x64_mingw] 70 | gem "serverspec", '~> 2.41', require: false 71 | end 72 | 73 | gems = {} 74 | puppet_version = ENV.fetch('PUPPET_GEM_VERSION', nil) 75 | facter_version = ENV.fetch('FACTER_GEM_VERSION', nil) 76 | hiera_version = ENV.fetch('HIERA_GEM_VERSION', nil) 77 | 78 | gems['puppet'] = location_for(puppet_version, nil, { source: gemsource_puppetcore }) 79 | gems['facter'] = location_for(facter_version, nil, { source: gemsource_puppetcore }) 80 | gems['hiera'] = location_for(hiera_version, nil, {}) if hiera_version 81 | 82 | # Generate the gem definitions 83 | print_gem_statement_for(gems) if ENV['DEBUG'] 84 | gems.each do |gem_name, gem_params| 85 | gem gem_name, *gem_params 86 | end 87 | 88 | # Evaluate Gemfile.local and ~/.gemfile if they exist 89 | extra_gemfiles = [ 90 | "#{__FILE__}.local", 91 | File.join(Dir.home, '.gemfile') 92 | ] 93 | 94 | extra_gemfiles.each do |gemfile| 95 | next unless File.file?(gemfile) && File.readable?(gemfile) 96 | 97 | # rubocop:disable Security/Eval 98 | eval(File.read(gemfile), binding) 99 | # rubocop:enable Security/Eval 100 | end 101 | # vim: syntax=ruby 102 | -------------------------------------------------------------------------------- /spec/unit/puppet/type/ini_subetting_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | ini_subsetting = Puppet::Type.type(:ini_subsetting) 6 | 7 | describe ini_subsetting do 8 | describe 'quote_char validation' do 9 | subject(:ini_subsetting_test) { described_class.new(name: 'foo', path:, quote_char:) } 10 | 11 | context 'when on posix platforms' do 12 | let(:path) { '/absolute/path' } 13 | let(:quote_char) { '\”' } 14 | 15 | it { expect { ini_subsetting_test }.to raise_exception(Puppet::ResourceError) } 16 | end 17 | end 18 | 19 | describe 'path validation' do 20 | subject(:ini_subsetting_test) { described_class.new(name: 'foo', path:) } 21 | 22 | context 'when on posix platforms' do 23 | before(:each) do 24 | Puppet.features.stub(:posix?) { true } 25 | Puppet.features.stub(:microsoft_windows?) { false } 26 | Puppet::Util::Platform.stub(:windows?) { false } 27 | end 28 | 29 | context 'with an absolute path' do 30 | let(:path) { '/absolute/path' } 31 | 32 | it { expect { ini_subsetting_test }.not_to raise_exception } 33 | end 34 | 35 | context 'with a relative path' do 36 | let(:path) { 'relative/path' } 37 | 38 | it { expect { ini_subsetting_test }.to raise_exception(Puppet::ResourceError) } 39 | end 40 | end 41 | 42 | context 'when on windows platforms' do 43 | before(:each) do 44 | Puppet.features.stub(:posix?) { false } 45 | Puppet.features.stub(:microsoft_windows?) { true } 46 | Puppet::Util::Platform.stub(:windows?) { true } 47 | end 48 | 49 | context 'with an absolute path with front slashes' do 50 | let(:path) { 'c:/absolute/path' } 51 | 52 | it { expect { ini_subsetting_test }.not_to raise_exception } 53 | end 54 | 55 | context 'with a relative path with back slashes' do 56 | let(:path) { 'relative\path' } 57 | 58 | it { expect { ini_subsetting_test }.to raise_exception(Puppet::ResourceError) } 59 | end 60 | end 61 | end 62 | 63 | [true, false].product([true, false, :md5]).each do |cfg, param| 64 | describe "when Puppet[:show_diff] is #{cfg} and show_diff => #{param}" do 65 | before(:each) do 66 | Puppet[:show_diff] = cfg 67 | end 68 | 69 | let(:value) { described_class.new(name: 'foo', value: 'whatever', show_diff: param).property(:value) } 70 | 71 | if cfg && param == true 72 | it 'displays diff' do 73 | expect(value.change_to_s('not_secret', 'at_all')).to include('not_secret', 'at_all') 74 | end 75 | 76 | it 'tells current value' do 77 | expect(value.is_to_s('not_secret_at_all')).to eq('not_secret_at_all') 78 | end 79 | 80 | it 'tells new value' do 81 | expect(value.should_to_s('not_secret_at_all')).to eq('not_secret_at_all') 82 | end 83 | elsif cfg && param == :md5 84 | it 'tells correct md5 hashes for multiple values' do 85 | expect(value.change_to_s('not_secret', 'at_all')).to include('e9e8db547f8960ef32dbc34029735564', '46cd73a9509ba78c39f05faf078a8cbe') 86 | end 87 | 88 | it 'does not tell singular value one' do 89 | expect(value.change_to_s('not_secret', 'at_all')).not_to include('not_secret') 90 | end 91 | 92 | it 'does not tell singular value two' do 93 | expect(value.change_to_s('not_secret', 'at_all')).not_to include('at_all') 94 | end 95 | 96 | it 'tells md5 of current value' do 97 | expect(value.is_to_s('not_secret_at_all')).to eq('{md5}218fde79f501b8ab8d212f1059bb857f') 98 | end 99 | 100 | it 'does not tell the current value' do 101 | expect(value.is_to_s('not_secret_at_all')).not_to include('not_secret_at_all') 102 | end 103 | 104 | it 'tells md5 of new value' do 105 | expect(value.should_to_s('not_secret_at_all')).not_to include('not_secret_at_all') 106 | end 107 | 108 | it 'does not tell the new value' do 109 | expect(value.should_to_s('not_secret_at_all')).to eq('{md5}218fde79f501b8ab8d212f1059bb857f') 110 | end 111 | else 112 | it 'tells redaction warning in place of actual values' do 113 | expect(value.change_to_s('not_at', 'all_secret')).to include('[redacted sensitive information]') 114 | end 115 | 116 | it 'does not tell actual value one' do 117 | expect(value.change_to_s('not_at', 'all_secret')).not_to include('not_at') 118 | end 119 | 120 | it 'does not tell actual value two' do 121 | expect(value.change_to_s('not_at', 'all_secret')).not_to include('all_secret') 122 | end 123 | 124 | it 'tells redaction warning in place of current value' do 125 | expect(value.is_to_s('not_at_all_secret')).to eq('[redacted sensitive information]') 126 | end 127 | 128 | it 'does not tell current value' do 129 | expect(value.is_to_s('not_at_all_secret')).not_to include('not_at_all_secret') 130 | end 131 | 132 | it 'tells redaction warning in place of new value' do 133 | expect(value.should_to_s('not_at_all_secret')).to eq('[redacted sensitive information]') 134 | end 135 | 136 | it 'does not tell new value' do 137 | expect(value.should_to_s('not_at_all_secret')).not_to include('not_at_all_secret') 138 | end 139 | end 140 | end 141 | end 142 | end 143 | -------------------------------------------------------------------------------- /spec/unit/puppet/type/ini_setting_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | ini_setting = Puppet::Type.type(:ini_setting) 6 | 7 | describe ini_setting do 8 | describe 'path validation' do 9 | subject(:ini_setting_path) { described_class.new(name: 'foo', path:) } 10 | 11 | context 'when on posix platforms' do 12 | before(:each) do 13 | Puppet.features.stub(:posix?) { true } 14 | Puppet.features.stub(:microsoft_windows?) { false } 15 | Puppet::Util::Platform.stub(:windows?) { false } 16 | end 17 | 18 | context 'with an absolute path' do 19 | let(:path) { '/absolute/path' } 20 | 21 | it { expect { ini_setting_path }.not_to raise_exception } 22 | end 23 | 24 | context 'with a relative path' do 25 | let(:path) { 'relative/path' } 26 | 27 | it { expect { ini_setting_path }.to raise_exception(Puppet::ResourceError) } 28 | end 29 | end 30 | 31 | context 'when on windows platforms' do 32 | before(:each) do 33 | Puppet.features.stub(:posix?) { false } 34 | Puppet.features.stub(:microsoft_windows?) { true } 35 | Puppet::Util::Platform.stub(:windows?) { true } 36 | end 37 | 38 | context 'with an absolute path with front slashes' do 39 | let(:path) { 'c:/absolute/path' } 40 | 41 | it { expect { ini_setting_path }.not_to raise_exception } 42 | end 43 | 44 | context 'with an absolute path with backslashes' do 45 | let(:path) { 'c:\absolute\path' } 46 | 47 | it { expect { ini_setting_path }.not_to raise_exception } 48 | end 49 | 50 | context 'with an absolute path with mixed slashes' do 51 | let(:path) { 'c:/absolute\path' } 52 | 53 | it { expect { ini_setting_path }.not_to raise_exception } 54 | end 55 | 56 | context 'with a relative path with front slashes' do 57 | let(:path) { 'relative/path' } 58 | 59 | it { expect { ini_setting_path }.to raise_exception(Puppet::ResourceError) } 60 | end 61 | 62 | context 'with a relative path with back slashes' do 63 | let(:path) { 'relative\path' } 64 | 65 | it { expect { ini_setting_path }.to raise_exception(Puppet::ResourceError) } 66 | end 67 | end 68 | end 69 | 70 | [true, false].product([true, false, 'true', 'false', 'md5', :md5]).each do |cfg, param| 71 | describe "when Puppet[:show_diff] is #{cfg} and show_diff => #{param}" do 72 | before(:each) do 73 | Puppet[:show_diff] = cfg 74 | end 75 | 76 | let(:value) { described_class.new(name: 'foo', value: 'whatever', show_diff: param).property(:value) } 77 | 78 | true_array = [true, 'true'] 79 | md5_array = ['md5', :md5] 80 | if cfg && true_array.include?(param) 81 | it 'displays diff' do 82 | expect(value.change_to_s('not_secret', 'at_all')).to include('not_secret', 'at_all') 83 | end 84 | 85 | it 'tells current value' do 86 | expect(value.is_to_s('not_secret_at_all')).to eq('not_secret_at_all') 87 | end 88 | 89 | it 'tells new value' do 90 | expect(value.should_to_s('not_secret_at_all')).to eq('not_secret_at_all') 91 | end 92 | elsif cfg && md5_array.include?(param) 93 | it 'tells correct md5 hashes for multiple values' do 94 | expect(value.change_to_s('not_at', 'all_secret')).to include('6edef0c4f5ec664feff6ca6fbc290970', '1660308ab156754fa09af0e8dc2c6629') 95 | end 96 | 97 | it 'does not tell singular value one' do 98 | expect(value.change_to_s('not_at #', 'all_secret')).not_to include('not_at') 99 | end 100 | 101 | it 'does not tell singular value two' do 102 | expect(value.change_to_s('not_at', 'all_secret')).not_to include('all_secret') 103 | end 104 | 105 | it 'tells md5 of current value' do 106 | expect(value.is_to_s('not_at_all_secret')).to eq('{md5}858b46aee11b780b8f5c8853668efc05') 107 | end 108 | 109 | it 'does not tell the current value' do 110 | expect(value.is_to_s('not_at_all_secret')).not_to include('not_secret_at_all') 111 | end 112 | 113 | it 'tells md5 of new value' do 114 | expect(value.should_to_s('not_at_all_secret')).to eq('{md5}858b46aee11b780b8f5c8853668efc05') 115 | end 116 | 117 | it 'does not tell the new value' do 118 | expect(value.should_to_s('not_at_all_secret')).not_to include('not_secret_at_all') 119 | end 120 | else 121 | it 'tells redaction warning in place of actual values' do 122 | expect(value.change_to_s('at_all', 'not_secret')).to include('[redacted sensitive information]') 123 | end 124 | 125 | it 'does not tell actual value one' do 126 | expect(value.change_to_s('at_all', 'not_secret')).not_to include('not_secret') 127 | end 128 | 129 | it 'does not tell actual value two' do 130 | expect(value.change_to_s('at_all', 'not_secret')).not_to include('at_all') 131 | end 132 | 133 | it 'tells redaction warning in place of current value' do 134 | expect(value.is_to_s('not_secret_at_all')).to eq('[redacted sensitive information]') 135 | end 136 | 137 | it 'does not tell current value' do 138 | expect(value.is_to_s('not_secret_at_all')).not_to include('not_secret_at_all') 139 | end 140 | 141 | it 'tells redaction warning in place of new value' do 142 | expect(value.should_to_s('not_secret_at_all')).to eq('[redacted sensitive information]') 143 | end 144 | 145 | it 'does not tell new value' do 146 | expect(value.should_to_s('not_secret_at_all')).not_to include('not_secret_at_all') 147 | end 148 | end 149 | end 150 | end 151 | 152 | describe 'when parent of :path is in the catalog' do 153 | ['posix', 'windows'].each do |platform| 154 | context "when on #{platform} platforms" do 155 | before(:each) do 156 | Puppet.features.stub(:posix?) { platform == 'posix' } 157 | Puppet.features.stub(:microsoft_windows?) { platform == 'windows' } 158 | Puppet::Util::Platform.stub(:windows?) { platform == 'windows' } 159 | end 160 | 161 | let(:file_path) { (platform == 'posix') ? '/tmp' : 'c:/tmp' } 162 | let(:file_resource) { Puppet::Type.type(:file).new(name: file_path) } 163 | let(:ini_setting_resource) { described_class.new(name: 'foo', path: "#{file_path}/foo.ini") } 164 | let(:auto_req) do 165 | catalog = Puppet::Resource::Catalog.new 166 | catalog.add_resource(file_resource) 167 | catalog.add_resource(ini_setting_resource) 168 | 169 | ini_setting_resource.autorequire 170 | end 171 | 172 | it 'creates relationship' do 173 | expect(auto_req.size).to be 1 174 | end 175 | 176 | it 'links to ini_setting resource' do 177 | expect(auto_req[0].target).to eq(ini_setting_resource) 178 | end 179 | 180 | it 'autorequires parent directory' do 181 | expect(auto_req[0].source).to eq(file_resource) 182 | end 183 | end 184 | end 185 | end 186 | end 187 | -------------------------------------------------------------------------------- /lib/puppet/util/setting_value.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Puppet::Util # rubocop:disable Style/ClassAndModuleChildren 4 | # This class can work with a list of subsettings inside 5 | # an ini file setting string to add, remove, extract and set their values. 6 | class SettingValue 7 | # The constructor method 8 | # @param setting_value [String] The initial setting value 9 | # @param subsetting_separator [String] The character is used to separate 10 | # subsettings in the setting_value string. 11 | # @param default_quote_char [String] Quote the setting string with this character. 12 | def initialize(setting_value, subsetting_separator = ' ', default_quote_char = '', key_val_separator = '') 13 | @setting_value = setting_value 14 | @subsetting_separator = subsetting_separator 15 | @quote_char = default_quote_char 16 | @key_val_separator = key_val_separator 17 | @subsetting_items = [] 18 | 19 | return unless @setting_value 20 | 21 | unquoted, quote_char = unquote_setting_value(setting_value) 22 | @quote_char = quote_char unless quote_char.empty? 23 | # an item can contain escaped separator 24 | @subsetting_items = unquoted.scan(Regexp.new("(?:(?:[^\\#{@subsetting_separator}]|\\.)+)")) 25 | @subsetting_items.map!(&:strip) 26 | end 27 | 28 | # If the setting value is quoted, the quotes are 29 | # removed and the unquoted string and the quoting 30 | # character are returned. 31 | # @param setting_value [String] The input value 32 | # @return [Array] The unquoted string and the quoting character 33 | def unquote_setting_value(setting_value) 34 | quote_char = '' 35 | if setting_value.start_with?('"') && setting_value.end_with?('"') 36 | quote_char = '"' 37 | elsif setting_value.start_with?("'") && setting_value.end_with?("'") 38 | quote_char = "'" 39 | end 40 | 41 | unquoted = if quote_char == '' 42 | setting_value 43 | else 44 | setting_value[1, setting_value.length - 2] 45 | end 46 | 47 | [unquoted, quote_char] 48 | end 49 | 50 | # Get the resulting setting value by joining all the 51 | # subsettings, separator and quote characters. 52 | # @return [String] 53 | def get_value 54 | value = @subsetting_items.join @subsetting_separator 55 | @quote_char + value + @quote_char 56 | end 57 | 58 | # Get the value of the given subsetting item. 59 | # If the exact match is used the value will be true 60 | # if the item is found. 61 | # @param subsetting [String] The name of the subsetting to add. 62 | # @param use_exact_match [:true,:false] Should the full name match be used? 63 | # @return [nil,true,String] 64 | def get_subsetting_value(subsetting, use_exact_match = :false) 65 | index = find_subsetting(subsetting, use_exact_match) 66 | # the item is not found in the list 67 | return nil unless index 68 | # the exact match is set and the item is found, the value should be true 69 | return true if use_exact_match == :true 70 | 71 | item = @subsetting_items[index] 72 | item[(subsetting.length + @key_val_separator.length)..] 73 | end 74 | 75 | # Add a new subsetting item to the list of existing items 76 | # if such item is not already there. 77 | # @param subsetting [String] The name of the subsetting to add. 78 | # @param subsetting_value [String] The value of the subsetting. 79 | # It will be appended to the name. 80 | # @param use_exact_match [:true,:false] Should the full name match be used? 81 | # @param [Symbol] insert_type 82 | # @param [String,Integer] insert_value 83 | # @return [Array] The resulting subsettings list. 84 | def add_subsetting(subsetting, subsetting_value, use_exact_match = :false, insert_type = :end, insert_value = nil) 85 | index = find_subsetting(subsetting, use_exact_match) 86 | 87 | # update the existing values if the subsetting is found in the list 88 | return update_subsetting(subsetting, subsetting_value, use_exact_match) if index 89 | 90 | new_item = item_value(subsetting, subsetting_value) 91 | 92 | case insert_type 93 | when :start 94 | @subsetting_items.unshift(new_item) 95 | when :end 96 | @subsetting_items.push(new_item) 97 | when :before 98 | before_index = find_subsetting(insert_value, use_exact_match) 99 | if before_index 100 | @subsetting_items.insert(before_index, new_item) 101 | else 102 | @subsetting_items.push(new_item) 103 | end 104 | when :after 105 | after_index = find_subsetting(insert_value, use_exact_match) 106 | if after_index 107 | @subsetting_items.insert(after_index + 1, new_item) 108 | else 109 | @subsetting_items.push(new_item) 110 | end 111 | when :index 112 | before_index = insert_value.to_i 113 | before_index = @subsetting_items.length if before_index > @subsetting_items.length 114 | @subsetting_items.insert(before_index, new_item) 115 | else 116 | @subsetting_items.push(new_item) 117 | end 118 | 119 | @subsetting_items 120 | end 121 | 122 | # Update all matching items in the settings list to the new values. 123 | # @param subsetting [String] The name of the subsetting to add. 124 | # @param subsetting_value [String] The value of the subsetting. 125 | # @param use_exact_match [:true,:false] Should the full name match be used? 126 | # @return [Array] The resulting subsettings list. 127 | def update_subsetting(subsetting, subsetting_value, use_exact_match = :false) 128 | new_item = item_value(subsetting, subsetting_value) 129 | @subsetting_items.map! do |item| 130 | if match_subsetting?(item, subsetting, use_exact_match) 131 | new_item 132 | else 133 | item 134 | end 135 | end 136 | end 137 | 138 | # Find the first subsetting item matching the given name, 139 | # or, if the exact match is set, equal to the given name 140 | # and return its array index value. Returns nil if not found. 141 | # @param subsetting [String] The name of the subsetting to search. 142 | # @param use_exact_match [:true,:false] Look for the full string match? 143 | # @return [Integer, nil] 144 | def find_subsetting(subsetting, use_exact_match = :false) 145 | @subsetting_items.index do |item| 146 | match_subsetting?(item, subsetting, use_exact_match) 147 | end 148 | end 149 | 150 | # Check if the subsetting item matches the given name. 151 | # If the exact match is set the entire item is matched, 152 | # and only the item name and separator string if not. 153 | # @param item [String] The item value to check against the subsetting name. 154 | # @param subsetting [String] The subsetting name. 155 | # @param use_exact_match [:true,:false] Look for the full string match? 156 | # @return [true,false] 157 | def match_subsetting?(item, subsetting, use_exact_match = :false) 158 | if use_exact_match == :true 159 | item.eql?(subsetting) 160 | else 161 | item.start_with?(subsetting + @key_val_separator) 162 | end 163 | end 164 | 165 | # Remove all the subsetting items that match 166 | # the given subsetting name. 167 | # @param subsetting [String] The subsetting name to remove. 168 | # @param use_exact_match [:true,:false] Look for the full string match? 169 | # @return [Array] The resulting subsettings list. 170 | def remove_subsetting(subsetting, use_exact_match = :false) 171 | @subsetting_items.delete_if do |item| 172 | match_subsetting?(item, subsetting, use_exact_match) 173 | end 174 | end 175 | 176 | # The actual value of the subsetting item. 177 | # It's built from the subsetting name, its value and the separator 178 | # string if present. 179 | # @param subsetting [String] The subsetting name 180 | # @param subsetting_value [String] The value of the subsetting 181 | # @return [String] 182 | def item_value(subsetting, subsetting_value) 183 | (subsetting || '') + (@key_val_separator || '') + (subsetting_value || '') 184 | end 185 | end 186 | end 187 | -------------------------------------------------------------------------------- /spec/acceptance/ini_subsetting_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'ini_subsetting resource' do 6 | basedir = setup_test_directory 7 | 8 | after :all do 9 | run_shell("rm #{basedir}/*.ini", expect_failures: true) 10 | end 11 | 12 | shared_examples 'has_content' do |path, pp, content| 13 | before :all do 14 | run_shell("rm #{path}", expect_failures: true) 15 | end 16 | 17 | it 'applies the manifest twice' do 18 | expect { idempotent_apply(pp) }.not_to raise_error 19 | end 20 | 21 | describe file(path) do 22 | it { is_expected.to be_file } 23 | 24 | describe '#content' do 25 | subject { super().content } 26 | 27 | it { 28 | expect(subject).to match content 29 | } 30 | end 31 | end 32 | end 33 | 34 | shared_examples 'has_error' do |path, pp, error| 35 | before :all do 36 | run_shell("rm #{path}", expect_failures: true) 37 | end 38 | 39 | it 'applies the manifest and gets a failure message' do 40 | expect(apply_manifest(pp, expect_failures: true).stderr).to match(error) 41 | end 42 | 43 | describe file(path) do 44 | it { is_expected.not_to be_file } 45 | end 46 | end 47 | 48 | describe 'ensure, section, setting, subsetting, & value parameters => present with subsections' do 49 | pp = <<-MANIFEST 50 | ini_subsetting { 'ensure => present for alpha': 51 | ensure => present, 52 | path => "#{basedir}/ini_subsetting.ini", 53 | section => 'one', 54 | setting => 'key', 55 | subsetting => 'alpha', 56 | value => 'bet', 57 | } 58 | ini_subsetting { 'ensure => present for beta': 59 | ensure => present, 60 | path => "#{basedir}/ini_subsetting.ini", 61 | section => 'one', 62 | setting => 'key', 63 | subsetting => 'beta', 64 | value => 'trons', 65 | require => Ini_subsetting['ensure => present for alpha'], 66 | } 67 | MANIFEST 68 | 69 | describe file("#{basedir}/ini_subsetting.ini") do 70 | it_behaves_like 'has_content', "#{basedir}/ini_subsetting.ini", pp, %r{\[one\]\Rkey = alphabet betatrons} 71 | end 72 | end 73 | 74 | describe 'ensure => absent' do 75 | pp = <<-MANIFEST 76 | ini_subsetting { 'ensure => absent for subsetting': 77 | ensure => absent, 78 | path => "#{basedir}/ini_subsetting.ini", 79 | section => 'one', 80 | setting => 'key', 81 | subsetting => 'alpha', 82 | } 83 | MANIFEST 84 | 85 | it 'applies the manifest twice' do 86 | expect { idempotent_apply(pp) }.not_to raise_error 87 | end 88 | 89 | describe file("#{basedir}/ini_subsetting.ini") do 90 | it { is_expected.to be_file } 91 | 92 | describe '#content' do 93 | subject { super().content } 94 | 95 | it { is_expected.to match %r{\[one\]} } 96 | it { is_expected.to match %r{key = betatrons} } 97 | it { is_expected.not_to match %r{alphabet} } 98 | end 99 | end 100 | end 101 | 102 | describe 'delete_if_empty => true' do 103 | pp = <<-MANIFEST 104 | ini_subsetting { 'ensure => absent for alpha': 105 | ensure => absent, 106 | path => "#{basedir}/ini_subsetting.ini", 107 | section => 'one', 108 | setting => 'key', 109 | subsetting => 'alpha', 110 | delete_if_empty => true, 111 | } 112 | ini_subsetting { 'ensure => absent for beta': 113 | ensure => absent, 114 | path => "#{basedir}/ini_subsetting.ini", 115 | section => 'one', 116 | setting => 'key', 117 | subsetting => 'beta', 118 | delete_if_empty => true, 119 | } 120 | MANIFEST 121 | 122 | it 'applies the manifest twice' do 123 | run_shell("printf \"[one]\\nkey = alphabet betatrons\\n\" > #{basedir}/ini_subsetting.ini", expect_failures: true) 124 | expect { idempotent_apply(pp) }.not_to raise_error 125 | end 126 | 127 | describe file("#{basedir}/ini_subsetting.ini") do 128 | it { is_expected.to be_file } 129 | 130 | describe '#content' do 131 | subject { super().content } 132 | 133 | it { is_expected.not_to match %r{\[one\]} } 134 | it { is_expected.not_to match %r{key =} } 135 | it { is_expected.not_to match %r{alphabet} } 136 | it { is_expected.not_to match %r{betatrons} } 137 | end 138 | end 139 | end 140 | 141 | describe 'quote_char' do 142 | { 143 | ['-Xmx'] => %r{args=""}, 144 | ['-Xmx', '256m'] => %r{args=-Xmx256m}, 145 | ['-Xmx', '512m'] => %r{args="-Xmx512m"}, 146 | ['-Xms', '256m'] => %r{args="-Xmx256m -Xms256m"} 147 | }.each do |parameter, content| 148 | context %(with '#{parameter.first}' #{(parameter.length > 1) ? "=> '#{parameter[1]}'" : 'absent'} makes '#{content}') do 149 | path = File.join(basedir, 'ini_subsetting.ini') 150 | before :all do 151 | ipp = <<-MANIFEST 152 | file { '#{path}': 153 | content => "[java]\nargs=-Xmx256m", 154 | force => true, 155 | } 156 | MANIFEST 157 | 158 | apply_manifest(ipp) 159 | end 160 | 161 | after :all do 162 | run_shell("cat #{path}", expect_failures: true) 163 | run_shell("rm #{path}", expect_failures: true) 164 | end 165 | 166 | pp = <<-MANIFEST 167 | ini_subsetting { '#{parameter.first}': 168 | ensure => #{(parameter.length > 1) ? 'present' : 'absent'}, 169 | path => '#{path}', 170 | section => 'java', 171 | setting => 'args', 172 | quote_char => '"', 173 | subsetting => '#{parameter.first}', 174 | value => '#{(parameter.length > 1) ? parameter[1] : ''}' 175 | } 176 | MANIFEST 177 | 178 | it 'applies the manifest twice' do 179 | expect { idempotent_apply(pp) }.not_to raise_error 180 | end 181 | 182 | describe file("#{basedir}/ini_subsetting.ini") do 183 | it { is_expected.to be_file } 184 | 185 | describe '#content' do 186 | subject { super().content } 187 | 188 | it { is_expected.to match content } 189 | end 190 | end 191 | end 192 | end 193 | end 194 | 195 | describe 'show_diff parameter and logging:' do 196 | setup_puppet_config_file 197 | 198 | [{ value: 'initial_value', matcher: 'created', show_diff: true }, 199 | { value: 'public_value', matcher: %r{initial_value.*public_value}, show_diff: true }, 200 | { value: 'secret_value', matcher: %r{redacted sensitive information.*redacted sensitive information}, show_diff: false }, 201 | { value: 'md5_value', matcher: %r{\{md5\}881671aa2bbc680bc530c4353125052b.*\{md5\}ed0903a7fa5de7886ca1a7a9ad06cf51}, show_diff: :md5 }].each do |i| 202 | pp = <<-MANIFEST 203 | ini_subsetting { 'test_show_diff': 204 | ensure => present, 205 | section => 'test', 206 | setting => 'something', 207 | subsetting => 'xxx', 208 | value => '#{i[:value]}', 209 | path => "#{basedir}/test_show_diff.ini", 210 | show_diff => #{i[:show_diff]} 211 | } 212 | MANIFEST 213 | 214 | context "when show_diff => #{i[:show_diff]}" do 215 | res = apply_manifest(pp, expect_changes: true) 216 | it 'applies manifest and expects changed value to be logged in proper form' do 217 | expect(res.stdout).to match(i[:matcher]) 218 | end 219 | 220 | it 'applies manifest and expects changed value to be logged in proper form #optional test' do 221 | expect(res.stdout).not_to match(i[:value]) unless i[:show_diff] == true 222 | end 223 | end 224 | end 225 | end 226 | 227 | describe 'insert types:' do 228 | [ 229 | { 230 | insert_type: :start, 231 | content: %r{d a b c} 232 | }, 233 | { 234 | insert_type: :end, 235 | content: %r{a b c d} 236 | }, 237 | { 238 | insert_type: :before, 239 | insert_value: 'c', 240 | content: %r{a b d c} 241 | }, 242 | { 243 | insert_type: :after, 244 | insert_value: 'a', 245 | content: %r{a d b c} 246 | }, 247 | { 248 | insert_type: :index, 249 | insert_value: 2, 250 | content: %r{a b d c} 251 | }, 252 | ].each do |params| 253 | context "with '#{params[:insert_type]}' makes '#{params[:content]}'" do 254 | pp = <<-MANIFEST 255 | ini_subsetting { "a": 256 | ensure => present, 257 | section => 'one', 258 | setting => 'two', 259 | subsetting => 'a', 260 | path => "#{basedir}/insert_types.ini", 261 | } -> 262 | ini_subsetting { "b": 263 | ensure => present, 264 | section => 'one', 265 | setting => 'two', 266 | subsetting => 'b', 267 | path => "#{basedir}/insert_types.ini", 268 | } -> 269 | ini_subsetting { "c": 270 | ensure => present, 271 | section => 'one', 272 | setting => 'two', 273 | subsetting => 'c', 274 | path => "#{basedir}/insert_types.ini", 275 | } -> 276 | ini_subsetting { "insert makes #{params[:content]}": 277 | ensure => present, 278 | section => 'one', 279 | setting => 'two', 280 | subsetting => 'd', 281 | path => "#{basedir}/insert_types.ini", 282 | insert_type => '#{params[:insert_type]}', 283 | insert_value => '#{params[:insert_value]}', 284 | } 285 | MANIFEST 286 | 287 | it_behaves_like 'has_content', "#{basedir}/insert_types.ini", pp, params[:content] 288 | end 289 | end 290 | end 291 | end 292 | -------------------------------------------------------------------------------- /REFERENCE.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | 3 | 4 | 5 | ## Table of Contents 6 | 7 | ### Resource types 8 | 9 | * [`ini_setting`](#ini_setting): ini_settings is used to manage a single setting in an INI file 10 | * [`ini_subsetting`](#ini_subsetting): ini_subsettings is used to manage multiple values in a setting in an INI file 11 | 12 | ### Functions 13 | 14 | * [`create_ini_settings`](#create_ini_settings): DEPRECATED. Use the namespaced function [`inifile::create_ini_settings`](#inifilecreate_ini_settings) instead. 15 | * [`inifile::create_ini_settings`](#inifile--create_ini_settings): This function is used to create a set of ini_setting resources from a hash 16 | 17 | ## Resource types 18 | 19 | ### `ini_setting` 20 | 21 | ini_settings is used to manage a single setting in an INI file 22 | 23 | #### Properties 24 | 25 | The following properties are available in the `ini_setting` type. 26 | 27 | ##### `ensure` 28 | 29 | Valid values: `present`, `absent` 30 | 31 | Ensurable method handles modeling creation. It creates an ensure property 32 | 33 | Default value: `present` 34 | 35 | ##### `value` 36 | 37 | The value of the setting to be defined. 38 | 39 | #### Parameters 40 | 41 | The following parameters are available in the `ini_setting` type. 42 | 43 | * [`force_new_section_creation`](#-ini_setting--force_new_section_creation) 44 | * [`indent_char`](#-ini_setting--indent_char) 45 | * [`indent_width`](#-ini_setting--indent_width) 46 | * [`key_val_separator`](#-ini_setting--key_val_separator) 47 | * [`name`](#-ini_setting--name) 48 | * [`path`](#-ini_setting--path) 49 | * [`provider`](#-ini_setting--provider) 50 | * [`refreshonly`](#-ini_setting--refreshonly) 51 | * [`section`](#-ini_setting--section) 52 | * [`section_prefix`](#-ini_setting--section_prefix) 53 | * [`section_suffix`](#-ini_setting--section_suffix) 54 | * [`setting`](#-ini_setting--setting) 55 | * [`show_diff`](#-ini_setting--show_diff) 56 | 57 | ##### `force_new_section_creation` 58 | 59 | Valid values: `true`, `false`, `yes`, `no` 60 | 61 | Create setting only if the section exists 62 | 63 | Default value: `true` 64 | 65 | ##### `indent_char` 66 | 67 | The character to indent new settings with. 68 | 69 | Default value: ` ` 70 | 71 | ##### `indent_width` 72 | 73 | The number of indent_chars to use to indent a new setting. 74 | 75 | ##### `key_val_separator` 76 | 77 | The separator string to use between each setting name and value. 78 | 79 | Default value: ` = ` 80 | 81 | ##### `name` 82 | 83 | namevar 84 | 85 | An arbitrary name used as the identity of the resource. 86 | 87 | ##### `path` 88 | 89 | The ini file Puppet will ensure contains the specified setting. 90 | 91 | ##### `provider` 92 | 93 | The specific backend to use for this `ini_setting` resource. You will seldom need to specify this --- Puppet will 94 | usually discover the appropriate provider for your platform. 95 | 96 | ##### `refreshonly` 97 | 98 | Valid values: `true`, `false`, `yes`, `no` 99 | 100 | A flag indicating whether or not the ini_setting should be updated only when called as part of a refresh event 101 | 102 | Default value: `false` 103 | 104 | ##### `section` 105 | 106 | The name of the section in the ini file in which the setting should be defined. 107 | 108 | Default value: `''` 109 | 110 | ##### `section_prefix` 111 | 112 | The prefix to the section name\'s header. 113 | 114 | Default value: `[` 115 | 116 | ##### `section_suffix` 117 | 118 | The suffix to the section name\'s header. 119 | 120 | Default value: `]` 121 | 122 | ##### `setting` 123 | 124 | The name of the setting to be defined. 125 | 126 | ##### `show_diff` 127 | 128 | Valid values: `true`, `md5`, `false` 129 | 130 | Whether to display differences when the setting changes. 131 | 132 | Default value: `true` 133 | 134 | ### `ini_subsetting` 135 | 136 | ini_subsettings is used to manage multiple values in a setting in an INI file 137 | 138 | #### Properties 139 | 140 | The following properties are available in the `ini_subsetting` type. 141 | 142 | ##### `ensure` 143 | 144 | Valid values: `present`, `absent` 145 | 146 | Ensurable method handles modeling creation. It creates an ensure property 147 | 148 | Default value: `present` 149 | 150 | ##### `value` 151 | 152 | The value of the subsetting to be defined. 153 | 154 | #### Parameters 155 | 156 | The following parameters are available in the `ini_subsetting` type. 157 | 158 | * [`delete_if_empty`](#-ini_subsetting--delete_if_empty) 159 | * [`insert_type`](#-ini_subsetting--insert_type) 160 | * [`insert_value`](#-ini_subsetting--insert_value) 161 | * [`key_val_separator`](#-ini_subsetting--key_val_separator) 162 | * [`name`](#-ini_subsetting--name) 163 | * [`path`](#-ini_subsetting--path) 164 | * [`provider`](#-ini_subsetting--provider) 165 | * [`quote_char`](#-ini_subsetting--quote_char) 166 | * [`section`](#-ini_subsetting--section) 167 | * [`setting`](#-ini_subsetting--setting) 168 | * [`show_diff`](#-ini_subsetting--show_diff) 169 | * [`subsetting`](#-ini_subsetting--subsetting) 170 | * [`subsetting_key_val_separator`](#-ini_subsetting--subsetting_key_val_separator) 171 | * [`subsetting_separator`](#-ini_subsetting--subsetting_separator) 172 | * [`use_exact_match`](#-ini_subsetting--use_exact_match) 173 | 174 | ##### `delete_if_empty` 175 | 176 | Valid values: `true`, `false` 177 | 178 | Set to true to delete the parent setting when the subsetting is empty instead of writing an empty string 179 | 180 | Default value: `false` 181 | 182 | ##### `insert_type` 183 | 184 | Valid values: `start`, `end`, `before`, `after`, `index` 185 | 186 | Where the new subsetting item should be inserted 187 | 188 | * :start - insert at the beginning of the line. 189 | * :end - insert at the end of the line (default). 190 | * :before - insert before the specified element if possible. 191 | * :after - insert after the specified element if possible. 192 | * :index - insert at the specified index number. 193 | 194 | Default value: `end` 195 | 196 | ##### `insert_value` 197 | 198 | The value for the insert types which require one. 199 | 200 | ##### `key_val_separator` 201 | 202 | The separator string to use between each setting name and value. 203 | 204 | Default value: ` = ` 205 | 206 | ##### `name` 207 | 208 | namevar 209 | 210 | An arbitrary name used as the identity of the resource. 211 | 212 | ##### `path` 213 | 214 | The ini file Puppet will ensure contains the specified setting. 215 | 216 | ##### `provider` 217 | 218 | The specific backend to use for this `ini_subsetting` resource. You will seldom need to specify this --- Puppet will 219 | usually discover the appropriate provider for your platform. 220 | 221 | ##### `quote_char` 222 | 223 | The character used to quote the entire value of the setting. Valid values are '', '\"' and \"'\" 224 | 225 | Default value: `''` 226 | 227 | ##### `section` 228 | 229 | The name of the section in the ini file in which the setting should be defined. 230 | 231 | Default value: `''` 232 | 233 | ##### `setting` 234 | 235 | The name of the setting to be defined. 236 | 237 | ##### `show_diff` 238 | 239 | Valid values: `true`, `md5`, `false` 240 | 241 | Whether to display differences when the setting changes. 242 | 243 | Default value: `true` 244 | 245 | ##### `subsetting` 246 | 247 | The name of the subsetting to be defined. 248 | 249 | ##### `subsetting_key_val_separator` 250 | 251 | The separator string between the subsetting name and its value. Defaults to the empty string. 252 | 253 | Default value: `''` 254 | 255 | ##### `subsetting_separator` 256 | 257 | The separator string between subsettings. Defaults to the empty string. 258 | 259 | Default value: ` ` 260 | 261 | ##### `use_exact_match` 262 | 263 | Valid values: `true`, `false` 264 | 265 | Set to true if your subsettings don\'t have values and you want to use exact matches to determine if the subsetting 266 | exists. 267 | 268 | Default value: `false` 269 | 270 | ## Functions 271 | 272 | ### `create_ini_settings` 273 | 274 | Type: Ruby 4.x API 275 | 276 | DEPRECATED. Use the namespaced function [`inifile::create_ini_settings`](#inifilecreate_ini_settings) instead. 277 | 278 | #### `create_ini_settings(Any *$args)` 279 | 280 | The create_ini_settings function. 281 | 282 | Returns: `Any` 283 | 284 | ##### `*args` 285 | 286 | Data type: `Any` 287 | 288 | 289 | 290 | ### `inifile::create_ini_settings` 291 | 292 | Type: Ruby 4.x API 293 | 294 | This function is used to create a set of ini_setting resources from a hash 295 | 296 | #### `inifile::create_ini_settings(Hash $settings, Optional[Hash] $defaults)` 297 | 298 | The inifile::create_ini_settings function. 299 | 300 | Returns: `Any` 301 | 302 | ##### `settings` 303 | 304 | Data type: `Hash` 305 | 306 | A hash of settings you want to create ini_setting resources from 307 | 308 | ##### `defaults` 309 | 310 | Data type: `Optional[Hash]` 311 | 312 | A hash of defaults you would like to use in the ini_setting resources 313 | 314 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # inifile 2 | 3 | #### Table of Contents 4 | 5 | 1. [Overview](#overview) 6 | 1. [Module Description - What the module does and why it is useful](#module-description) 7 | 1. [Setup - The basics of getting started with inifile module](#setup) 8 | 1. [Usage - Configuration options and additional functionality](#usage) 9 | 1. [Reference - An under-the-hood peek at what the module is doing and how](#reference) 10 | 1. [Limitations - OS compatibility, etc.](#limitations) 11 | 1. [License](#license) 12 | 1. [Development - Guide for contributing to the module](#development) 13 | 14 | 15 | ## Overview 16 | 17 | The inifile module lets Puppet manage settings stored in INI-style configuration files. 18 | 19 | 20 | ## Module Description 21 | 22 | Many applications use INI-style configuration files to store their settings. This module supplies two custom resource types to let you manage those settings through Puppet. 23 | 24 | 25 | ## Setup 26 | 27 | ### Beginning with inifile 28 | 29 | To manage a single setting in an INI file, add the `ini_setting` type to a class: 30 | 31 | ~~~puppet 32 | ini_setting { "sample setting": 33 | ensure => present, 34 | path => '/tmp/foo.ini', 35 | section => 'bar', 36 | setting => 'baz', 37 | value => 'quux', 38 | } 39 | ~~~ 40 | 41 | 42 | ## Usage 43 | 44 | 45 | The inifile module is used to: 46 | 47 | * Support comments starting with either '#' or ';'. 48 | * Support either whitespace or no whitespace around '='. 49 | * Add any missing sections to the INI file. 50 | 51 | It does not manipulate your file any more than it needs to. In most cases, it doesn't affect the original whitespace, comments, or ordering. See the common usages below for examples. 52 | 53 | ### Manage multiple values in a setting 54 | 55 | Use the `ini_subsetting` type: 56 | 57 | ~~~puppet 58 | ini_subsetting {'sample subsetting': 59 | ensure => present, 60 | section => '', 61 | key_val_separator => '=', 62 | path => '/etc/default/pe-puppetdb', 63 | setting => 'JAVA_ARGS', 64 | subsetting => '-Xmx', 65 | value => '512m', 66 | } 67 | ~~~ 68 | 69 | Results in managing this `-Xmx` subsetting: 70 | 71 | ~~~puppet 72 | JAVA_ARGS="-Xmx512m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" 73 | ~~~ 74 | 75 | 76 | ### Use a non-standard section header 77 | 78 | ~~~puppet 79 | ini_setting { 'default minage': 80 | ensure => present, 81 | path => '/etc/security/users', 82 | section => 'default', 83 | setting => 'minage', 84 | value => '1', 85 | section_prefix => '', 86 | section_suffix => ':', 87 | } 88 | ~~~ 89 | 90 | Results in: 91 | 92 | ~~~puppet 93 | default: 94 | minage = 1 95 | ~~~ 96 | 97 | ### Use a non-standard indent character 98 | 99 | To use a non-standard indent character or string for added settings, set the `indent_char` and the `indent_width` parameters. The `indent_width` parameter controls how many `indent_char` appear in the indent. 100 | 101 | 102 | ~~~puppet 103 | ini_setting { 'procedure cache size': 104 | ensure => present, 105 | path => '/var/lib/ase/config/ASE-16_0/SYBASE.cfg', 106 | section => 'SQL Server Administration', 107 | setting => 'procedure cache size', 108 | value => '15000', 109 | indent_char => "\t", 110 | indent_width => 2, 111 | } 112 | ~~~ 113 | 114 | Results in: 115 | 116 | ~~~puppet 117 | [SQL Server Administration] 118 | procedure cache size = 15000 119 | ~~~ 120 | 121 | ### Implement child providers 122 | 123 | You might want to create child providers that inherit the `ini_setting` provider for one of the following reasons: 124 | 125 | * To make a custom resource to manage an application that stores its settings in INI files, without recreating the code to manage the files themselves. 126 | * To [purge all unmanaged settings](https://docs.puppetlabs.com/references/latest/type.html#resources-attribute-purge) from a managed INI file. 127 | 128 | To implement child providers, first specify a custom type. Have it implement a namevar called `name` and a property called `value`: 129 | 130 | ~~~ruby 131 | #my_module/lib/puppet/type/glance_api_config.rb 132 | Puppet::Type.newtype(:glance_api_config) do 133 | ensurable 134 | newparam(:name, :namevar => true) do 135 | desc 'Section/setting name to manage from glance-api.conf' 136 | # namevar should be of the form section/setting 137 | newvalues(/\S+\/\S+/) 138 | end 139 | newproperty(:value) do 140 | desc 'The value of the setting to define' 141 | munge do |v| 142 | v.to_s.strip 143 | end 144 | end 145 | end 146 | ~~~ 147 | 148 | Your type also needs a provider that uses the `ini_setting` provider as its parent: 149 | 150 | ~~~ruby 151 | # my_module/lib/puppet/provider/glance_api_config/ini_setting.rb 152 | Puppet::Type.type(:glance_api_config).provide( 153 | :ini_setting, 154 | # set ini_setting as the parent provider 155 | :parent => Puppet::Type.type(:ini_setting).provider(:ruby) 156 | ) do 157 | # implement section as the first part of the namevar 158 | def section 159 | resource[:name].split('/', 2).first 160 | end 161 | def setting 162 | # implement setting as the second part of the namevar 163 | resource[:name].split('/', 2).last 164 | end 165 | # hard code the file path (this allows purging) 166 | def self.file_path 167 | '/etc/glance/glance-api.conf' 168 | end 169 | end 170 | ~~~ 171 | 172 | Now you can manage the settings in the `/etc/glance/glance-api.conf` file as individual resources: 173 | 174 | ~~~puppet 175 | glance_api_config { 'HEADER/important_config': 176 | value => 'secret_value', 177 | } 178 | ~~~ 179 | 180 | If you've implemented `self.file_path`, you can have Puppet purge the file of the all lines that aren't implemented as Puppet resources: 181 | 182 | ~~~puppet 183 | resources { 'glance_api_config': 184 | purge => true, 185 | } 186 | ~~~ 187 | 188 | ### Manage multiple ini_settings 189 | 190 | To manage multiple `ini_settings`, use the [`inifile::create_ini_settings`](REFERENCE.md#inifilecreate_ini_settings) function. 191 | 192 | ~~~puppet 193 | $defaults = { 'path' => '/tmp/foo.ini' } 194 | $example = { 'section1' => { 'setting1' => 'value1' } } 195 | inifile::create_ini_settings($example, $defaults) 196 | ~~~ 197 | 198 | Results in: 199 | 200 | ~~~puppet 201 | ini_setting { '[section1] setting1': 202 | ensure => present, 203 | section => 'section1', 204 | setting => 'setting1', 205 | value => 'value1', 206 | path => '/tmp/foo.ini', 207 | } 208 | ~~~ 209 | 210 | To include special parameters, use the following code: 211 | 212 | ~~~puppet 213 | $defaults = { 'path' => '/tmp/foo.ini' } 214 | $example = { 215 | 'section1' => { 216 | 'setting1' => 'value1', 217 | 'settings2' => { 218 | 'ensure' => 'absent' 219 | } 220 | } 221 | } 222 | inifile::create_ini_settings($example, $defaults) 223 | ~~~ 224 | 225 | Results in: 226 | 227 | ~~~puppet 228 | ini_setting { '[section1] setting1': 229 | ensure => present, 230 | section => 'section1', 231 | setting => 'setting1', 232 | value => 'value1', 233 | path => '/tmp/foo.ini', 234 | } 235 | ini_setting { '[section1] setting2': 236 | ensure => absent, 237 | section => 'section1', 238 | setting => 'setting2', 239 | path => '/tmp/foo.ini', 240 | } 241 | ~~~ 242 | 243 | #### Manage multiple ini_settings with Hiera 244 | 245 | For the profile `example`: 246 | 247 | ~~~puppet 248 | class profile::example ( 249 | Hash $settings, 250 | ) { 251 | $defaults = { 'path' => '/tmp/foo.ini' } 252 | inifile::create_ini_settings($settings, $defaults) 253 | } 254 | ~~~ 255 | 256 | Provide this in your Hiera data: 257 | 258 | ~~~puppet 259 | profile::example::settings: 260 | section1: 261 | setting1: value1 262 | setting2: value2 263 | setting3: 264 | ensure: absent 265 | ~~~ 266 | 267 | Results in: 268 | 269 | ~~~puppet 270 | ini_setting { '[section1] setting1': 271 | ensure => present, 272 | section => 'section1', 273 | setting => 'setting1', 274 | value => 'value1', 275 | path => '/tmp/foo.ini', 276 | } 277 | ini_setting { '[section1] setting2': 278 | ensure => present, 279 | section => 'section1', 280 | setting => 'setting2', 281 | value => 'value2', 282 | path => '/tmp/foo.ini', 283 | } 284 | ini_setting { '[section1] setting3': 285 | ensure => absent, 286 | section => 'section1', 287 | setting => 'setting3', 288 | path => '/tmp/foo.ini', 289 | } 290 | ~~~ 291 | 292 | 293 | ## Reference 294 | See [REFERENCE.md](https://github.com/puppetlabs/puppetlabs-inifile/blob/main/REFERENCE.md) 295 | 296 | 297 | 298 | ## Limitations 299 | 300 | ### Supported operating systems 301 | 302 | For an extensive list of supported operating systems, see [metadata.json](https://github.com/puppetlabs/puppetlabs-inifile/blob/main/metadata.json) 303 | 304 | ### create_ini_settings 305 | 306 | When using inifile::create_ini_settings it’s worth noting that namespace tags will not be applied to the resource. If you need these namespace tags we advise using the standard ini_setting resource. 307 | 308 | For more information about resource tags, please see [this article](https://puppet.com/docs/puppet/7/lang_tags.html#lang_tags-assigning-tags-to-resources). 309 | 310 | 311 | ## License 312 | 313 | This codebase is licensed under the Apache2.0 licensing, however due to the nature of the codebase the open source dependencies may also use a combination of [AGPL](https://opensource.org/license/agpl-v3/), [BSD-2](https://opensource.org/license/bsd-2-clause/), [BSD-3](https://opensource.org/license/bsd-3-clause/), [GPL2.0](https://opensource.org/license/gpl-2-0/), [LGPL](https://opensource.org/license/lgpl-3-0/), [MIT](https://opensource.org/license/mit/) and [MPL](https://opensource.org/license/mpl-2-0/) Licensing. 314 | 315 | 316 | ## Development 317 | 318 | We are experimenting with a new tool for running acceptance tests. It's name is [puppet_litmus](https://github.com/puppetlabs/puppet_litmus) this replaces beaker as the test runner. To run the acceptance tests follow the instructions [here](https://github.com/puppetlabs/puppet_litmus/wiki/Tutorial:-use-Litmus-to-execute-acceptance-tests-with-a-sample-module-(MoTD)#install-the-necessary-gems-for-the-module). 319 | 320 | Puppet Labs modules on the Puppet Forge are open projects, and community contributions are essential for keeping them great. We can't access the huge number of platforms and myriad of hardware, software, and deployment configurations that Puppet is intended to serve. 321 | 322 | We want to keep it as easy as possible to contribute changes so that our modules work in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. 323 | 324 | For more information, see our [module contribution guide.](https://puppet.com/docs/puppet/latest/contributing.html) 325 | 326 | ### Contributors 327 | 328 | To see who's already involved, see the [list of contributors.](https://github.com/puppetlabs/puppetlabs-inifile/graphs/contributors) 329 | -------------------------------------------------------------------------------- /spec/unit/puppet/util/ini_file_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'stringio' 5 | require 'puppet/util/ini_file' 6 | 7 | describe Puppet::Util::IniFile do 8 | subject(:ini_sub) { described_class.new('/my/ini/file/path') } 9 | 10 | before :each do 11 | allow(File).to receive(:file?).with('/my/ini/file/path').and_return(true) 12 | allow(described_class).to receive(:readlines).once.with('/my/ini/file/path') do 13 | sample_content 14 | end 15 | end 16 | 17 | context 'when parsing a file' do 18 | let(:sample_content) do 19 | template = <<-INIFILE 20 | # This is a comment 21 | [section1] 22 | ; This is also a comment 23 | foo=foovalue 24 | 25 | bar = barvalue 26 | baz = 27 | [section2] 28 | 29 | foo= foovalue2 30 | baz=bazvalue 31 | ; commented = out setting 32 | #another comment 33 | ; yet another comment 34 | zot = multi word value 35 | xyzzy['thing1']['thing2']=xyzzyvalue 36 | l=git log 37 | INIFILE 38 | template.split("\n") 39 | end 40 | 41 | it 'parses the correct number of sections' do 42 | # there is always a "global" section, so our count should be 3. 43 | expect(ini_sub.section_names.length).to eq(3) 44 | end 45 | 46 | it 'parses the correct section_names' do 47 | # there should always be a "global" section named "" at the beginning of the list 48 | expect(ini_sub.section_names).to eq(['', 'section1', 'section2']) 49 | end 50 | 51 | it 'exposes settings for sections #section1' do 52 | expect(ini_sub.get_settings('section1')).to eq('bar' => 'barvalue', 53 | 'baz' => '', 54 | 'foo' => 'foovalue') 55 | end 56 | 57 | it 'exposes settings for sections #section2' do 58 | expect(ini_sub.get_settings('section2')).to eq('baz' => 'bazvalue', 59 | 'foo' => 'foovalue2', 60 | 'l' => 'git log', 61 | "xyzzy['thing1']['thing2']" => 'xyzzyvalue', 62 | 'zot' => 'multi word value') 63 | end 64 | end 65 | 66 | context 'when parsing a file whose first line is a section' do 67 | let(:sample_content) do 68 | template = <<-INIFILE 69 | [section1] 70 | ; This is a comment 71 | foo=foovalue 72 | INIFILE 73 | template.split("\n") 74 | end 75 | 76 | it 'parses the correct number of sections' do 77 | # there is always a "global" section, so our count should be 2. 78 | expect(ini_sub.section_names.length).to eq(2) 79 | end 80 | 81 | it 'parses the correct section_names' do 82 | # there should always be a "global" section named "" at the beginning of the list 83 | expect(ini_sub.section_names).to eq(['', 'section1']) 84 | end 85 | 86 | it 'exposes settings for sections' do 87 | expect(ini_sub.get_value('section1', 'foo')).to eq('foovalue') 88 | end 89 | end 90 | 91 | context "when parsing a file with a 'global' section" do 92 | let(:sample_content) do 93 | template = <<-INIFILE 94 | foo = bar 95 | [section1] 96 | ; This is a comment 97 | foo=foovalue 98 | INIFILE 99 | template.split("\n") 100 | end 101 | 102 | it 'parses the correct number of sections' do 103 | # there is always a "global" section, so our count should be 2. 104 | expect(ini_sub.section_names.length).to eq(2) 105 | end 106 | 107 | it 'parses the correct section_names' do 108 | # there should always be a "global" section named "" at the beginning of the list 109 | expect(ini_sub.section_names).to eq(['', 'section1']) 110 | end 111 | 112 | it 'exposes settings for sections #bar' do 113 | expect(ini_sub.get_value('', 'foo')).to eq('bar') 114 | end 115 | 116 | it 'exposes settings for sections #foovalue' do 117 | expect(ini_sub.get_value('section1', 'foo')).to eq('foovalue') 118 | end 119 | end 120 | 121 | context 'when updating a file with existing empty values' do 122 | let(:sample_content) do 123 | template = <<-INIFILE 124 | [section1] 125 | foo= 126 | #bar= 127 | #xyzzy['thing1']['thing2']='xyzzyvalue' 128 | INIFILE 129 | template.split("\n") 130 | end 131 | 132 | before :each do 133 | expect(ini_sub.get_value('section1', 'far')).to be_nil 134 | expect(ini_sub.get_value('section1', 'bar')).to be_nil 135 | expect(ini_sub.get_value('section1', "xyzzy['thing1']['thing2']")).to be_nil 136 | end 137 | # rubocop:enable RSpec/ExpectInHook 138 | 139 | it 'properlies update uncommented values' do 140 | ini_sub.set_value('section1', 'foo', ' = ', 'foovalue') 141 | expect(ini_sub.get_value('section1', 'foo')).to eq('foovalue') 142 | end 143 | 144 | it 'properlies update uncommented values without separator' do 145 | ini_sub.set_value('section1', 'foo', 'foovalue') 146 | expect(ini_sub.get_value('section1', 'foo')).to eq('foovalue') 147 | end 148 | 149 | it 'properlies update commented value' do 150 | ini_sub.set_value('section1', 'bar', ' = ', 'barvalue') 151 | expect(ini_sub.get_value('section1', 'bar')).to eq('barvalue') 152 | end 153 | 154 | it 'properlies update commented values' do 155 | ini_sub.set_value('section1', "xyzzy['thing1']['thing2']", ' = ', 'xyzzyvalue') 156 | expect(ini_sub.get_value('section1', "xyzzy['thing1']['thing2']")).to eq('xyzzyvalue') 157 | end 158 | 159 | it 'properlies update commented value without separator' do 160 | ini_sub.set_value('section1', 'bar', 'barvalue') 161 | expect(ini_sub.get_value('section1', 'bar')).to eq('barvalue') 162 | end 163 | 164 | it 'properlies update commented values without separator' do 165 | ini_sub.set_value('section1', "xyzzy['thing1']['thing2']", 'xyzzyvalue') 166 | expect(ini_sub.get_value('section1', "xyzzy['thing1']['thing2']")).to eq('xyzzyvalue') 167 | end 168 | 169 | it 'properlies add new empty values' do 170 | ini_sub.set_value('section1', 'baz', ' = ', 'bazvalue') 171 | expect(ini_sub.get_value('section1', 'baz')).to eq('bazvalue') 172 | end 173 | 174 | it 'adds new empty values without separator' do 175 | ini_sub.set_value('section1', 'baz', 'bazvalue') 176 | expect(ini_sub.get_value('section1', 'baz')).to eq('bazvalue') 177 | end 178 | end 179 | 180 | context 'when the file has quotation marks in its section names' do 181 | let(:sample_content) do 182 | template = <<-INIFILE 183 | [branch "main"] 184 | remote = origin 185 | merge = refs/heads/main 186 | 187 | [alias] 188 | to-deploy = log --merges --grep='pull request' --format='%s (%cN)' origin/production..origin/main 189 | [branch "production"] 190 | remote = origin 191 | merge = refs/heads/production 192 | INIFILE 193 | template.split("\n") 194 | end 195 | 196 | it 'parses the sections' do 197 | expect(ini_sub.section_names).to contain_exactly('', 'branch "main"', 'alias', 'branch "production"') 198 | end 199 | end 200 | 201 | context 'when Samba INI file with dollars in section names' do 202 | let(:sample_content) do 203 | template = <<-INIFILE 204 | [global] 205 | workgroup = FELLOWSHIP 206 | ; ... 207 | idmap config * : backend = tdb 208 | 209 | [printers] 210 | comment = All Printers 211 | ; ... 212 | browseable = No 213 | 214 | [print$] 215 | comment = Printer Drivers 216 | path = /var/lib/samba/printers 217 | 218 | [Shares] 219 | path = /home/shares 220 | read only = No 221 | guest ok = Yes 222 | INIFILE 223 | template.split("\n") 224 | end 225 | 226 | it 'parses the correct section_names' do 227 | expect(ini_sub.section_names).to contain_exactly('', 'global', 'printers', 'print$', 'Shares') 228 | end 229 | end 230 | 231 | context 'when section names with forward slashes in them' do 232 | let(:sample_content) do 233 | template = <<-INIFILE 234 | [monitor:///var/log/*.log] 235 | disabled = test_value 236 | INIFILE 237 | template.split("\n") 238 | end 239 | 240 | it 'parses the correct section_names' do 241 | expect(ini_sub.section_names).to contain_exactly('', 'monitor:///var/log/*.log') 242 | end 243 | end 244 | 245 | context 'when KDE Configuration with braces in setting names' do 246 | let(:sample_content) do 247 | template = <<-INIFILE 248 | [khotkeys] 249 | _k_friendly_name=khotkeys 250 | {5465e8c7-d608-4493-a48f-b99d99fdb508}=Print,none,PrintScreen 251 | {d03619b6-9b3c-48cc-9d9c-a2aadb485550}=Search,none,Search 252 | INIFILE 253 | template.split("\n") 254 | end 255 | 256 | it 'exposes settings for sections #print' do 257 | expect(ini_sub.get_value('khotkeys', '{5465e8c7-d608-4493-a48f-b99d99fdb508}')).to eq('Print,none,PrintScreen') 258 | end 259 | 260 | it 'exposes settings for sections #search' do 261 | expect(ini_sub.get_value('khotkeys', '{d03619b6-9b3c-48cc-9d9c-a2aadb485550}')).to eq('Search,none,Search') 262 | end 263 | end 264 | 265 | context 'when Configuration with colons in setting names' do 266 | let(:sample_content) do 267 | template = <<-INIFILE 268 | [Drive names] 269 | A:=5.25" Floppy 270 | B:=3.5" Floppy 271 | C:=Winchester 272 | INIFILE 273 | template.split("\n") 274 | end 275 | 276 | it 'exposes settings for sections #A' do 277 | expect(ini_sub.get_value('Drive names', 'A:')).to eq '5.25" Floppy' 278 | end 279 | 280 | it 'exposes settings for sections #B' do 281 | expect(ini_sub.get_value('Drive names', 'B:')).to eq '3.5" Floppy' 282 | end 283 | 284 | it 'exposes settings for sections #C' do 285 | expect(ini_sub.get_value('Drive names', 'C:')).to eq 'Winchester' 286 | end 287 | end 288 | 289 | context 'when Configuration with spaces in setting names' do 290 | let(:sample_content) do 291 | template = <<-INIFILE 292 | [global] 293 | # log files split per-machine: 294 | log file = /var/log/samba/log.%m 295 | 296 | kerberos method = system keytab 297 | passdb backend = tdbsam 298 | security = ads 299 | INIFILE 300 | template.split("\n") 301 | end 302 | 303 | it 'exposes settings for sections #log' do 304 | expect(ini_sub.get_value('global', 'log file')).to eq '/var/log/samba/log.%m' 305 | end 306 | 307 | it 'exposes settings for sections #kerberos' do 308 | expect(ini_sub.get_value('global', 'kerberos method')).to eq 'system keytab' 309 | end 310 | 311 | it 'exposes settings for sections #passdb' do 312 | expect(ini_sub.get_value('global', 'passdb backend')).to eq 'tdbsam' 313 | end 314 | 315 | it 'exposes settings for sections #security' do 316 | expect(ini_sub.get_value('global', 'security')).to eq 'ads' 317 | end 318 | end 319 | end 320 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /spec/acceptance/ini_setting_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'ini_setting resource' do 6 | basedir = setup_test_directory 7 | 8 | after :all do 9 | run_shell("rm #{basedir}/*.ini", expect_failures: true) 10 | end 11 | 12 | shared_examples 'has_content' do |path, pp, content| 13 | before :all do 14 | run_shell("rm #{path}", expect_failures: true) 15 | end 16 | 17 | it 'applies the manifest twice' do 18 | expect { idempotent_apply(pp) }.not_to raise_error 19 | end 20 | 21 | describe file(path) do 22 | it { is_expected.to be_file } 23 | 24 | describe '#content' do 25 | subject { super().content } 26 | 27 | it { is_expected.to match content } 28 | end 29 | end 30 | end 31 | 32 | shared_examples 'has_error' do |path, pp, error| 33 | before :all do 34 | run_shell("rm #{path}", expect_failures: true) 35 | end 36 | 37 | it 'applies the manifest and gets a failure message' do 38 | expect(apply_manifest(pp, expect_failures: true).stderr).to match(error) 39 | end 40 | 41 | describe file(path) do 42 | it { is_expected.not_to be_file } 43 | end 44 | end 45 | 46 | context 'when ensure parameter => present for global and section' do 47 | pp = <<-MANIFEST 48 | ini_setting { 'ensure => present for section': 49 | ensure => present, 50 | path => "#{basedir}/ini_setting.ini", 51 | section => 'one', 52 | setting => 'two', 53 | value => 'three', 54 | } 55 | ini_setting { 'ensure => present for global': 56 | ensure => present, 57 | path => "#{basedir}/ini_setting.ini", 58 | section => '', 59 | setting => 'four', 60 | value => 'five', 61 | } 62 | MANIFEST 63 | 64 | it_behaves_like 'has_content', "#{basedir}/ini_setting.ini", pp, %r{four = five\R\R\[one\]\Rtwo = three} 65 | end 66 | 67 | context 'when ensure parameter => absent for key/value' do 68 | before :all do 69 | ipp = <<-MANIFEST 70 | file { '#{basedir}/ini_setting.ini': 71 | content => "four = five \n [one] \n two = three", 72 | force => true, 73 | } 74 | MANIFEST 75 | 76 | apply_manifest(ipp) 77 | end 78 | 79 | pp = <<-MANIFEST 80 | ini_setting { 'ensure => absent for key/value': 81 | ensure => absent, 82 | path => "#{basedir}/ini_setting.ini", 83 | section => 'one', 84 | setting => 'two', 85 | value => 'three', 86 | } 87 | MANIFEST 88 | 89 | it 'applies the manifest twice' do 90 | expect { idempotent_apply(pp) }.not_to raise_error 91 | end 92 | 93 | describe file("#{basedir}/ini_setting.ini") do 94 | it { is_expected.to be_file } 95 | 96 | describe '#content' do 97 | subject { super().content } 98 | 99 | it { is_expected.to match %r{four = five} } 100 | it { is_expected.not_to match %r{\[one\]} } 101 | it { is_expected.not_to match %r{two = three} } 102 | end 103 | end 104 | end 105 | 106 | context 'when ensure parameter => absent for global' do 107 | before :all do 108 | ipp = <<-MANIFEST 109 | file { '#{basedir}/ini_setting.ini': 110 | content => "four = five\n [one]\n two = three", 111 | force => true, 112 | } 113 | MANIFEST 114 | 115 | apply_manifest(ipp) 116 | end 117 | 118 | after :all do 119 | run_shell("rm #{basedir}/ini_setting.ini", expect_failures: true) 120 | end 121 | 122 | pp = <<-MANIFEST 123 | ini_setting { 'ensure => absent for global': 124 | ensure => absent, 125 | path => "#{basedir}/ini_setting.ini", 126 | section => '', 127 | setting => 'four', 128 | value => 'five', 129 | } 130 | MANIFEST 131 | 132 | it 'applies the manifest twice' do 133 | expect { idempotent_apply(pp) }.not_to raise_error 134 | end 135 | 136 | describe file("#{basedir}/ini_setting.ini") do 137 | it { is_expected.to be_file } 138 | 139 | describe '#content' do 140 | subject { super().content } 141 | 142 | it { is_expected.not_to match %r{four = five} } 143 | it { is_expected.to match %r{\[one\]} } 144 | it { is_expected.to match %r{two = three} } 145 | end 146 | end 147 | end 148 | 149 | describe 'path parameter' do 150 | context 'when path => foo' do 151 | pp = <<-MANIFEST 152 | ini_setting { 'path => foo': 153 | ensure => present, 154 | section => 'one', 155 | setting => 'two', 156 | value => 'three', 157 | path => 'foo', 158 | } 159 | MANIFEST 160 | 161 | it_behaves_like 'has_error', 'foo', pp, %r{must be fully qualified} 162 | end 163 | end 164 | 165 | context 'when ensure parameter => present and only section' do 166 | pp = <<-MANIFEST 167 | ini_setting { 'ensure => present for section': 168 | ensure => present, 169 | path => "#{basedir}/ini_setting.ini", 170 | section => 'one', 171 | } 172 | MANIFEST 173 | 174 | it_behaves_like 'has_content', "#{basedir}/ini_setting.ini", pp, %r{\[one\]} 175 | end 176 | 177 | describe 'show_diff parameter and logging:' do 178 | setup_puppet_config_file 179 | 180 | [{ value: 'initial_value', matcher: 'created', show_diff: true }, 181 | { value: 'public_value', matcher: %r{initial_value.*public_value}, show_diff: true }, 182 | { value: 'secret_value', matcher: %r{redacted sensitive information.*redacted sensitive information}, show_diff: false }, 183 | { value: 'md5_value', matcher: %r{\{md5\}881671aa2bbc680bc530c4353125052b.*\{md5\}ed0903a7fa5de7886ca1a7a9ad06cf51}, show_diff: :md5 }].each do |i| 184 | pp = <<-MANIFEST 185 | ini_setting { 'test_show_diff': 186 | ensure => present, 187 | section => 'test', 188 | setting => 'something', 189 | value => '#{i[:value]}', 190 | path => "#{basedir}/test_show_diff.ini", 191 | show_diff => #{i[:show_diff]} 192 | } 193 | MANIFEST 194 | 195 | context "when show_diff => #{i[:show_diff]}" do 196 | res = apply_manifest(pp, expect_changes: true) 197 | it 'applies manifest and expects changed value to be logged in proper form' do 198 | expect(res.stdout).to match(i[:matcher]) 199 | end 200 | 201 | it 'applies manifest and expects changed value to be logged in proper form #optional test' do 202 | expect(res.stdout).not_to match(i[:value]) unless i[:show_diff] == true 203 | end 204 | end 205 | end 206 | end 207 | 208 | describe 'values with spaces in the value part at the beginning or at the end' do 209 | pp = <<-MANIFEST 210 | ini_setting { 'path => foo': 211 | ensure => present, 212 | section => 'one', 213 | setting => 'two', 214 | value => ' 123', 215 | path => '#{basedir}/ini_setting.ini', 216 | } 217 | MANIFEST 218 | 219 | it_behaves_like 'has_content', "#{basedir}/ini_setting.ini", pp, %r{\[one\]\Rtwo = 123} 220 | end 221 | 222 | describe 'refreshonly' do 223 | before :each do 224 | ipp = <<-MANIFEST 225 | file { '#{basedir}/ini_setting.ini': 226 | content => "[section1]\n valueinsection1 = 123", 227 | force => true, 228 | } 229 | MANIFEST 230 | 231 | apply_manifest(ipp) 232 | end 233 | 234 | after :each do 235 | run_shell("rm #{basedir}/ini_setting.ini", expect_failures: true) 236 | end 237 | 238 | context 'when event is triggered' do 239 | context 'when update setting value' do 240 | let(:update_value_manifest) do 241 | <<-MANIFEST 242 | notify { foo: 243 | notify => Ini_Setting['updateSetting'], 244 | } 245 | 246 | ini_setting { "updateSetting": 247 | ensure => present, 248 | path => "#{basedir}/ini_setting.ini", 249 | section => 'section1', 250 | setting => 'valueinsection1', 251 | value => "newValue", 252 | refreshonly => true, 253 | } 254 | MANIFEST 255 | end 256 | 257 | before(:each) do 258 | apply_manifest(update_value_manifest, expect_changes: true) 259 | end 260 | 261 | describe file("#{basedir}/ini_setting.ini") do 262 | it { is_expected.to be_file } 263 | 264 | describe '#content' do 265 | subject { super().content } 266 | 267 | it { is_expected.to match %r{valueinsection1 = newValue} } 268 | end 269 | end 270 | end 271 | 272 | context 'when remove setting value' do 273 | let(:remove_setting_manifest) do 274 | <<-MANIFEST 275 | notify { foo: 276 | notify => Ini_Setting['removeSetting'], 277 | } 278 | 279 | ini_setting { "removeSetting": 280 | ensure => absent, 281 | path => "#{basedir}/ini_setting.ini", 282 | section => 'section1', 283 | setting => 'valueinsection1', 284 | refreshonly => true, 285 | } 286 | MANIFEST 287 | end 288 | 289 | before(:each) do 290 | apply_manifest(remove_setting_manifest, expect_changes: true) 291 | end 292 | 293 | describe file("#{basedir}/ini_setting.ini") do 294 | it { is_expected.to be_file } 295 | 296 | describe '#content' do 297 | subject { super().content } 298 | 299 | it { is_expected.to be_empty } 300 | end 301 | end 302 | end 303 | end 304 | 305 | context 'when not receiving an event' do 306 | context 'when does not update setting' do 307 | let(:does_not_update_value_manifest) do 308 | <<-MANIFEST 309 | file { "#{basedir}/ini_setting.ini": 310 | ensure => present, 311 | notify => Ini_Setting['updateSetting'], 312 | } 313 | ini_setting { "updateSetting": 314 | ensure => present, 315 | path => "#{basedir}/ini_setting.ini", 316 | section => 'section1', 317 | setting => 'valueinsection1', 318 | value => "newValue", 319 | refreshonly => true, 320 | } 321 | MANIFEST 322 | end 323 | 324 | before(:each) do 325 | apply_manifest(does_not_update_value_manifest, expect_changes: false) 326 | end 327 | 328 | describe file("#{basedir}/ini_setting.ini") do 329 | it { is_expected.to be_file } 330 | 331 | describe '#content' do 332 | subject { super().content } 333 | 334 | it { is_expected.to match %r{valueinsection1 = 123} } 335 | it { is_expected.to match %r{\[section1\]} } 336 | end 337 | end 338 | end 339 | 340 | context 'when does not remove setting' do 341 | let(:does_not_remove_setting_manifest) do 342 | <<-MANIFEST 343 | file { "#{basedir}/ini_setting.ini": 344 | ensure => present, 345 | notify => Ini_Setting['removeSetting'], 346 | } 347 | 348 | ini_setting { "removeSetting": 349 | ensure => absent, 350 | path => "#{basedir}/ini_setting.ini", 351 | section => 'section1', 352 | setting => 'valueinsection1', 353 | refreshonly => true, 354 | } 355 | MANIFEST 356 | end 357 | 358 | before(:each) do 359 | apply_manifest(does_not_remove_setting_manifest, expect_changes: false) 360 | end 361 | 362 | describe file("#{basedir}/ini_setting.ini") do 363 | it { is_expected.to be_file } 364 | 365 | describe '#content' do 366 | subject { super().content } 367 | 368 | it { is_expected.not_to be_empty } 369 | it { is_expected.to match %r{valueinsection1 = 123} } 370 | it { is_expected.to match %r{\[section1\]} } 371 | end 372 | end 373 | end 374 | end 375 | end 376 | end 377 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | ## 2.3.0 2 | ### Summary 3 | This release uses the PDK convert functionality which in return makes the module PDK compliant. It also includes a feature for `force_new_section_creation` and a roll up of maintenance changes. 4 | 5 | ### Added 6 | - Added `force_new_section_creation` parameter. 7 | - PDK convert and update to use pdk 1.5.0 (MODULES-6326). 8 | 9 | ### Removed 10 | - Support for Scientific Linux 5 11 | - Support for Debian 7 12 | 13 | ## Supported Release [2.2.2] 14 | ### Summary 15 | This is a bug fix release that corrects type autoloading. 16 | 17 | ### Fixed 18 | - Correct type autoload ([FM-6932](https://tickets.puppet.com/browse/FM-6932)). 19 | 20 | ## Supported Release [2.2.1] 21 | ### Summary 22 | This is a bug fix release for a problem with managing existing lines in Puppet > 5.4.0 23 | 24 | ### Fixed 25 | - issue with ini_setting's :refreshonly parameter validation ([MODULES-6687](https://tickets.puppet.com/browse/MODULES-6687)) 26 | 27 | ## Supported Release [2.2.0] 28 | ### Summary 29 | This release uses the PDK convert functionality which in return makes the module PDK compliant. It also includes a roll up of maintenance changes. 30 | 31 | ### Added 32 | - PDK convert inifile ([MODULES-6453](https://tickets.puppet.com/browse/MODULES-6453)). 33 | - Modulesync updates. 34 | 35 | ### Fixed 36 | - Changes to address additional Rubocop failures. 37 | - Addressing puppet-lint doc warnings. 38 | 39 | ### Removed 40 | - `gem update bundler` command in .travis.yml due to ([MODULES-6339](https://tickets.puppet.com/browse/MODULES-6339)). 41 | 42 | ## Supported Release 2.1.1 43 | ### Summary 44 | This release is in order to implement Rubocop within the module and includes a wide array of formatting changes throughout the code and the enabling of rubocop checks to be run against all pull requests against the module. 45 | 46 | ### Changed 47 | - Rubocop checks will now be run against any PRs made towards the module. 48 | - The module has undergone a substantial reformatting in order to comply with the designated standards. 49 | 50 | ## Supported Release 2.1.0 51 | ### Summary 52 | This is a clean release prior to the implementation of rubocop within the module. 53 | 54 | ### Added 55 | - Several Modulesync updates have been made. 56 | - Indent Character can now be set. 57 | - Support for Debian 9 has been added. 58 | 59 | ### Removed 60 | - Support for Ubuntu 1004 and 1204 has been removed. 61 | - Support for SLES 10 SP4 has been removed. 62 | - Support for Debian 6 has been removed. 63 | - Support for Solaris 12 has been removed. 64 | - Support for Windows Server 2003 R2 has been removed. 65 | 66 | ## Supported Release 2.0.0 67 | ### Summary 68 | This is a major release that includes a few bugfixes as well as some general module updates. 69 | 70 | **This release drops Puppet 3 support** 71 | 72 | ### Changed 73 | - Moved lower Puppet version requirement to 4.7.0, MODULES-4830 74 | 75 | ### Fixed 76 | - Fix path validation on windows MODULES-4170 77 | - Fix headings in README 78 | - Fix for mimicking commented settings MODULES-4932 79 | - Fix for Backwards compatible ini_file.set_value MODULES-5172 80 | 81 | ## Supported Release 1.6.0 82 | ### Summary 83 | This release expands functionality around sub-settings and adds the `refreshonly` parameter so the user can specify whether a resource should or should not respond to a refresh event. 84 | 85 | ### Features 86 | - `refreshonly` decide whether or not a value should be updated as part of a refresh 87 | - `insert_type` choose where the sub-setting is placed in the final string 88 | - `subsetting_key_val_separator` specify a key/value separator for sub-settings 89 | 90 | ### Bugfixes 91 | - MODULES-3145 Cast values to strings before passing to provider 92 | 93 | 94 | ## Supported Release 1.5.0 95 | ### Summary 96 | This release adds the long-awaited `show_diff` parameter for diffing the complete file on changes (or can also just show the md5 sums). 97 | 98 | ### Features 99 | - Added `show_diff` parameter to show diffs on changes. 100 | - Remove empty ini sections when the last line in the section is removed. 101 | 102 | ### Bugfixes 103 | - Workaround `create_ini_settings()` duplicate resources puppet bug PUP-4709 104 | 105 | ## Supported Release 1.4.3 106 | ###Summary 107 | 108 | Small release for support of newer PE versions. This increments the version of PE in the metadata.json file. 109 | 110 | ## 2015-09-01 - Supported Release 1.4.2 111 | ### Summary 112 | This release adds some bugfixes. 113 | 114 | ####Bugfixes 115 | - MODULES-2212 Add use_exact_match parameter for subsettings 116 | - MODULES-1908 Munge the setting to ensure we always strip the whitespace 117 | - MODULES-2369 Support a space as a key_val_separator 118 | 119 | ## 2015-07-15 - Supported Release 1.4.1 120 | ### Summary 121 | This release bumps the metadata for PE up. 122 | 123 | ##2015-07-07 - Supported Releases 1.4.0 124 | ###Summary 125 | 126 | This is primarily a release which includes acceptance tests updates, but also includes some minor bug fixes and improvements 127 | 128 | ####Features 129 | - Solaris 12 Support 130 | - Acceptance testing improvements 131 | 132 | ####Bugfixes 133 | - MODULES-1599 Match only on space and tab whitespace after k/v separator 134 | 135 | ##2015-06-09 - Supported Releases 1.3.0 136 | ###Summary 137 | 138 | This is primarily a feature release, but also includes test fixes, documentation updates and synchronization of files with modulesync. 139 | 140 | ####Features 141 | - Synchronized files using modulesync 142 | - Improved documentation 143 | - Allow changing key value separator beyond indentation 144 | - Adding the ability to change regex match for $section in inifile 145 | 146 | ####Bugfixes 147 | - pin beaker-rspec for windows testing 148 | - pin rspec gems for testing 149 | - Adds default values for section 150 | - Fixed names containing spaces 151 | 152 | ##2014-11-11 - Supported Releases 1.2.0 153 | ###Summary 154 | 155 | This is primarily a bugfix release, but also includes documentation updates and synchronization of files with modulesync. 156 | 157 | ####Features 158 | - Synchronized files using modulesync 159 | - Improved documentation with a warning about old, manually installed inifile with PE3.3+ 160 | 161 | ####Bugfixes 162 | - Fix issue where single character settings were not being saved 163 | 164 | ##2014-09-30 - Supported Releases 1.1.4 165 | ###Summary 166 | 167 | This release includes documentation and test updates. 168 | 169 | ##2014-07-15 - Supported Release 1.1.3 170 | ###Summary 171 | 172 | This release merely updates metadata.json so the module can be uninstalled and 173 | upgraded via the puppet module command. 174 | 175 | ##2014-07-10 - Supported Release 1.1.2 176 | ###Summary 177 | 178 | This is a re-packaging release. 179 | 180 | ##2014-07-07 - Release 1.1.1 181 | ###Summary 182 | 183 | This supported bugfix release corrects the inifile section header detection 184 | regex (so you can use more characters in your section titles). 185 | 186 | ####Bugfixes 187 | - Correct section regex to allow anything other than ] 188 | - Correct `exists?` to return a boolean 189 | - Lots of test updates 190 | - Add missing CONTRIBUTING.md 191 | 192 | ##2014-06-04 - Release 1.1.0 193 | ###Summary 194 | 195 | This is a compatibility and feature release. This release adds one new 196 | feature, the ability to control the quote character used. This allows you to 197 | do things like: 198 | 199 | ``` 200 | ini_subsetting { '-Xms': 201 | ensure => present, 202 | path => '/some/config/file', 203 | section => '', 204 | setting => 'JAVA_ARGS', 205 | quote_char => '"', 206 | subsetting => '-Xms' 207 | value => '256m', 208 | } 209 | ``` 210 | 211 | Which builds: 212 | 213 | ``` 214 | JAVA_ARGS="-Xmx256m -Xms256m" 215 | ``` 216 | 217 | ####Features 218 | - Add quote_char parameter to the ini_subsetting resource type 219 | 220 | ####Bugfixes 221 | 222 | ####Known Bugs 223 | * No known bugs 224 | 225 | ##2014-03-04 - Supported Release 1.0.3 226 | ###Summary 227 | 228 | This is a supported release. It has only test changes. 229 | 230 | ####Features 231 | 232 | ####Bugfixes 233 | 234 | ####Known Bugs 235 | * No known bugs 236 | 237 | 238 | ##2014-02-26 - Version 1.0.2 239 | ###Summary 240 | This release adds supported platforms to metadata.json and contains spec fixes 241 | 242 | 243 | ##2014-02-12 - Version 1.0.1 244 | ###Summary 245 | This release is a bugfix for handling whitespace/[]'s better, and adding a 246 | bunch of tests. 247 | 248 | ####Bugfixes 249 | - Handle whitespace in sections 250 | - Handle square brances in values 251 | - Add metadata.json 252 | - Update some travis testing 253 | - Tons of beaker-rspec tests 254 | 255 | 256 | ##2013-07-16 - Version 1.0.0 257 | ####Features 258 | - Handle empty values. 259 | - Handle whitespace in settings names (aka: server role = something) 260 | - Add mechanism for allowing ini_setting subclasses to override the 261 | formation of the namevar during .instances, to allow for ini_setting 262 | derived types that manage flat ini-file-like files and still purge 263 | them. 264 | 265 | --- 266 | ##2013-05-28 - Chris Price - 0.10.3 267 | * Fix bug in subsetting handling for new settings (cbea5dc) 268 | 269 | ##2013-05-22 - Chris Price - 0.10.2 270 | * Better handling of quotes for subsettings (1aa7e60) 271 | 272 | ##2013-05-21 - Chris Price - 0.10.1 273 | * Change constants to class variables to avoid ruby warnings (6b19864) 274 | 275 | ##2013-04-10 - Erik Dalén - 0.10.1 276 | * Style fixes (c4af8c3) 277 | 278 | ##2013-04-02 - Dan Bode - 0.10.1 279 | * Add travisfile and Gemfile (c2052b3) 280 | 281 | ##2013-04-02 - Chris Price - 0.10.1 282 | * Update README.markdown (ad38a08) 283 | 284 | ##2013-02-15 - Karel Brezina - 0.10.0 285 | * Added 'ini_subsetting' custom resource type (4351d8b) 286 | 287 | ##2013-03-11 - Dan Bode - 0.10.0 288 | * guard against nil indentation values (5f71d7f) 289 | 290 | ##2013-01-07 - Dan Bode - 0.10.0 291 | * Add purging support to ini file (2f22483) 292 | 293 | ##2013-02-05 - James Sweeny - 0.10.0 294 | * Fix test to use correct key_val_parameter (b1aff63) 295 | 296 | ##2012-11-06 - Chris Price - 0.10.0 297 | * Added license file w/Apache 2.0 license (5e1d203) 298 | 299 | ##2012-11-02 - Chris Price - 0.9.0 300 | * Version 0.9.0 released 301 | 302 | ##2012-10-26 - Chris Price - 0.9.0 303 | * Add detection for commented versions of settings (a45ab65) 304 | 305 | ##2012-10-20 - Chris Price - 0.9.0 306 | * Refactor to clarify implementation of `save` (f0d443f) 307 | 308 | ##2012-10-20 - Chris Price - 0.9.0 309 | * Add example for `ensure=absent` (e517148) 310 | 311 | ##2012-10-20 - Chris Price - 0.9.0 312 | * Better handling of whitespace lines at ends of sections (845fa70) 313 | 314 | ##2012-10-20 - Chris Price - 0.9.0 315 | * Respect indentation / spacing for existing sections and settings (c2c26de) 316 | 317 | ##2012-10-17 - Chris Price - 0.9.0 318 | * Minor tweaks to handling of removing settings (cda30a6) 319 | 320 | ##2012-10-10 - Dan Bode - 0.9.0 321 | * Add support for removing lines (1106d70) 322 | 323 | ##2012-10-02 - Dan Bode - 0.9.0 324 | * Make value a property (cbc90d3) 325 | 326 | ##2012-10-02 - Dan Bode - 0.9.0 327 | * Make ruby provider a better parent. (1564c47) 328 | 329 | ##2012-09-29 - Reid Vandewiele - 0.9.0 330 | * Allow values with spaces to be parsed and set (3829e20) 331 | 332 | ##2012-09-24 - Chris Price - 0.0.3 333 | * Version 0.0.3 released 334 | 335 | ##2012-09-20 - Chris Price - 0.0.3 336 | * Add validation for key_val_separator (e527908) 337 | 338 | ##2012-09-19 - Chris Price - 0.0.3 339 | * Allow overriding separator string between key/val pairs (8d1fdc5) 340 | 341 | ##2012-08-20 - Chris Price - 0.0.2 342 | * Version 0.0.2 released 343 | 344 | ##2012-08-17 - Chris Price - 0.0.2 345 | * Add support for "global" section at beginning of file (c57dab4) 346 | 347 | [2.3.0]:https://github.com/puppetlabs/puppetlabs-apt/compare/2.2.2...2.3.0 348 | [2.2.2]:https://github.com/puppetlabs/puppetlabs-apt/compare/2.2.1...2.2.2 349 | [2.2.1]:https://github.com/puppetlabs/puppetlabs-apt/compare/2.2.0...2.2.1 350 | [2.2.0]:https://github.com/puppetlabs/puppetlabs-apt/compare/2.1.1...2.2.0 351 | -------------------------------------------------------------------------------- /lib/puppet/util/ini_file.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require File.expand_path('external_iterator', __dir__) 4 | require File.expand_path('ini_file/section', __dir__) 5 | 6 | module Puppet::Util # rubocop:disable Style/ClassAndModuleChildren 7 | # 8 | # ini_file.rb 9 | # 10 | class IniFile 11 | def initialize(path, key_val_separator = ' = ', section_prefix = '[', section_suffix = ']', 12 | indent_char = ' ', indent_width = nil) 13 | 14 | k_v_s = (key_val_separator =~ %r{^\s+$}) ? ' ' : key_val_separator.strip 15 | 16 | @section_prefix = section_prefix 17 | @section_suffix = section_suffix 18 | @indent_char = indent_char 19 | @indent_width = indent_width&.to_i 20 | 21 | @section_regex = section_regex 22 | @setting_regex = %r{^(\s*)([^#;\s]|[^#;\s].*?[^\s#{k_v_s}])(\s*#{k_v_s}[ \t]*)(.*)\s*$} 23 | @commented_setting_regex = %r{^(\s*)[#;]+(\s*)(.*?[^\s#{k_v_s}])(\s*#{k_v_s}[ \t]*)(.*)\s*$} 24 | 25 | @path = path 26 | @key_val_separator = key_val_separator 27 | @section_names = [] 28 | @sections_hash = {} 29 | parse_file 30 | end 31 | 32 | def section_regex 33 | # Only put in prefix/suffix if they exist 34 | r_string = '^\s*' 35 | r_string += Regexp.escape(@section_prefix) 36 | r_string += '(.*)' 37 | r_string += Regexp.escape(@section_suffix) 38 | r_string += '\s*$' 39 | %r{#{r_string}} 40 | end 41 | 42 | attr_reader :section_names 43 | 44 | def get_settings(section_name) 45 | section = @sections_hash[section_name] 46 | section.setting_names.each_with_object({}) do |setting, result| 47 | result[setting] = section.get_value(setting) 48 | end 49 | end 50 | 51 | def section?(section_name) 52 | @sections_hash.key?(section_name) 53 | end 54 | 55 | def get_value(section_name, setting) 56 | @sections_hash[section_name].get_value(setting) if @sections_hash.key?(section_name) 57 | end 58 | 59 | def set_value(*args) 60 | case args.size 61 | when 1 62 | section_name = args[0] 63 | when 3 64 | # Backwards compatible set_value function, See MODULES-5172 65 | (section_name, setting, value) = args 66 | when 4 67 | (section_name, setting, separator, value) = args 68 | end 69 | 70 | complete_setting = { 71 | setting:, 72 | separator:, 73 | value: 74 | } 75 | add_section(Section.new(section_name, nil, nil, nil, nil)) unless @sections_hash.key?(section_name) 76 | 77 | section = @sections_hash[section_name] 78 | 79 | if section.existing_setting?(setting) 80 | update_line(section, setting, value) 81 | section.update_existing_setting(setting, value) 82 | elsif find_commented_setting(section, setting) 83 | # So, this stanza is a bit of a hack. What we're trying 84 | # to do here is this: for settings that don't already 85 | # exist, we want to take a quick peek to see if there 86 | # is a commented-out version of them in the section. 87 | # If so, we'd prefer to add the setting directly after 88 | # the commented line, rather than at the end of the section. 89 | 90 | # If we get here then we found a commented line, so we 91 | # call "insert_inline_setting_line" to update the lines array 92 | insert_inline_setting_line(find_commented_setting(section, setting), section, complete_setting) 93 | 94 | # Then, we need to tell the setting object that we hacked 95 | # in an inline setting 96 | section.insert_inline_setting(setting, value) 97 | 98 | # Finally, we need to update all of the start/end line 99 | # numbers for all of the sections *after* the one that 100 | # was modified. 101 | section_index = @section_names.index(section_name) 102 | increment_section_line_numbers(section_index + 1) 103 | elsif !setting.nil? || !value.nil? 104 | section.set_additional_setting(setting, value) 105 | end 106 | end 107 | 108 | def remove_setting(section_name, setting) 109 | section = @sections_hash[section_name] 110 | return unless section.existing_setting?(setting) 111 | 112 | # If the setting is found, we have some work to do. 113 | # First, we remove the line from our array of lines: 114 | remove_line(section, setting) 115 | 116 | # Then, we need to tell the setting object to remove 117 | # the setting from its state: 118 | section.remove_existing_setting(setting) 119 | 120 | # Finally, we need to update all of the start/end line 121 | # numbers for all of the sections *after* the one that 122 | # was modified. 123 | section_index = @section_names.index(section_name) 124 | decrement_section_line_numbers(section_index + 1) 125 | 126 | return unless section.empty? 127 | 128 | # By convention, it's time to remove this newly emptied out section 129 | lines.delete_at(section.start_line) 130 | decrement_section_line_numbers(section_index + 1) 131 | @section_names.delete_at(section_index) 132 | @sections_hash.delete(section.name) 133 | end 134 | 135 | def save 136 | global_empty = @sections_hash[''].empty? && @sections_hash[''].additional_settings.empty? 137 | File.open(@path, 'w') do |fh| 138 | @section_names.each_index do |index| 139 | name = @section_names[index] 140 | 141 | section = @sections_hash[name] 142 | 143 | # We need a buffer to cache lines that are only whitespace 144 | whitespace_buffer = [] 145 | 146 | if section.new_section? && !section.global? 147 | fh.puts('') if (index == 1 && !global_empty) || index > 1 148 | 149 | fh.puts("#{@section_prefix}#{section.name}#{@section_suffix}") 150 | end 151 | 152 | unless section.new_section? 153 | # write all of the pre-existing lines 154 | (section.start_line..section.end_line).each do |line_num| 155 | line = lines[line_num] 156 | 157 | # We buffer any lines that are only whitespace so that 158 | # if they are at the end of a section, we can insert 159 | # any new settings *before* the final chunk of whitespace 160 | # lines. 161 | if line.match?(%r{^\s*$}) 162 | whitespace_buffer << line 163 | else 164 | # If we get here, we've found a non-whitespace line. 165 | # We'll flush any cached whitespace lines before we 166 | # write it. 167 | flush_buffer_to_file(whitespace_buffer, fh) 168 | fh.puts(line) 169 | end 170 | end 171 | end 172 | 173 | # write new settings, if there are any 174 | section.additional_settings.each_pair do |key, value| 175 | fh.puts("#{@indent_char * (@indent_width || section.indentation || 0)}#{key}#{@key_val_separator}#{value}") 176 | end 177 | 178 | if !whitespace_buffer.empty? 179 | flush_buffer_to_file(whitespace_buffer, fh) 180 | elsif section.new_section? && !section.additional_settings.empty? && (index < @section_names.length - 1) 181 | # We get here if there were no blank lines at the end of the 182 | # section. 183 | # 184 | # If we are adding a new section with a new setting, 185 | # and if there are more sections that come after this one, 186 | # we'll write one blank line just so that there is a little 187 | # whitespace between the sections. 188 | # if (section.end_line.nil? && 189 | fh.puts('') 190 | end 191 | end 192 | end 193 | end 194 | 195 | private 196 | 197 | def add_section(section) 198 | @sections_hash[section.name] = section 199 | @section_names << section.name 200 | end 201 | 202 | def parse_file 203 | line_iter = create_line_iter 204 | 205 | # We always create a "global" section at the beginning of the file, for 206 | # anything that appears before the first named section. 207 | section = read_section('', 0, line_iter) 208 | add_section(section) 209 | line, line_num = line_iter.next 210 | 211 | while line 212 | if (match = @section_regex.match(line)) 213 | section = read_section(match[1], line_num, line_iter) 214 | add_section(section) 215 | end 216 | line, line_num = line_iter.next 217 | end 218 | end 219 | 220 | def read_section(name, start_line, line_iter) 221 | settings = {} 222 | end_line_num = start_line 223 | min_indentation = nil 224 | empty = true 225 | empty_line_count = 0 226 | loop do 227 | line, line_num = line_iter.peek 228 | if line_num.nil? || @section_regex.match(line) 229 | # the global section always exists, even when it's empty; 230 | # when it's empty, we must be sure it's thought of as new, 231 | # which is signalled with a nil ending line 232 | end_line_num = nil if name == '' && empty 233 | return Section.new(name, start_line, end_line_num, settings, min_indentation, empty_line_count) 234 | end 235 | if (match = @setting_regex.match(line)) 236 | settings[match[2]] = match[4] 237 | indentation = match[1].length 238 | min_indentation = [indentation, min_indentation || indentation].min 239 | end 240 | end_line_num = line_num 241 | empty_line_count += 1 if line == "\n" 242 | 243 | empty = false 244 | line_iter.next 245 | end 246 | end 247 | 248 | def update_line(section, setting, value) 249 | (section.start_line..section.end_line).each do |line_num| 250 | next unless (match = @setting_regex.match(lines[line_num])) 251 | 252 | lines[line_num] = "#{match[1]}#{match[2]}#{match[3]}#{value}" if match[2] == setting 253 | end 254 | end 255 | 256 | def remove_line(section, setting) 257 | (section.start_line..section.end_line).each do |line_num| 258 | next unless (match = @setting_regex.match(lines[line_num])) 259 | 260 | lines.delete_at(line_num) if match[2] == setting 261 | end 262 | end 263 | 264 | def create_line_iter 265 | ExternalIterator.new(lines) 266 | end 267 | 268 | def lines 269 | @lines ||= IniFile.readlines(@path) 270 | end 271 | 272 | # This is mostly here because it makes testing easier--we don't have 273 | # to try to stub any methods on File. 274 | def self.readlines(path) # rubocop:disable Lint/IneffectiveAccessModifier : Attempting to change breaks tests 275 | # If this type is ever used with very large files, we should 276 | # write this in a different way, using a temp 277 | # file; for now assuming that this type is only used on 278 | # small-ish config files that can fit into memory without 279 | # too much trouble. 280 | File.file?(path) ? File.readlines(path) : [] 281 | end 282 | 283 | # This utility method scans through the lines for a section looking for 284 | # commented-out versions of a setting. It returns `nil` if it doesn't 285 | # find one. If it does find one, then it returns a hash containing 286 | # two keys: 287 | # 288 | # :line_num - the line number that contains the commented version 289 | # of the setting 290 | # :match - the ruby regular expression match object, which can 291 | # be used to mimic the whitespace from the comment line 292 | def find_commented_setting(section, setting) 293 | return nil if section.new_section? 294 | 295 | (section.start_line..section.end_line).each do |line_num| 296 | next unless (match = @commented_setting_regex.match(lines[line_num])) 297 | return { match:, line_num: } if match[3] == setting 298 | end 299 | nil 300 | end 301 | 302 | # This utility method is for inserting a line into the existing 303 | # lines array. The `result` argument is expected to be in the 304 | # format of the return value of `find_commented_setting`. 305 | def insert_inline_setting_line(result, section, complete_setting) 306 | line_num = result[:line_num] 307 | s = complete_setting 308 | lines.insert(line_num + 1, "#{@indent_char * (@indent_width || section.indentation || 0)}#{s[:setting]}#{s[:separator]}#{s[:value]}") 309 | end 310 | 311 | # Utility method; given a section index (index into the @section_names 312 | # array), decrement the start/end line numbers for that section and all 313 | # all of the other sections that appear *after* the specified section. 314 | def decrement_section_line_numbers(section_index) 315 | @section_names[section_index..(@section_names.length - 1)].each do |name| 316 | section = @sections_hash[name] 317 | section.decrement_line_nums 318 | end 319 | end 320 | 321 | # Utility method; given a section index (index into the @section_names 322 | # array), increment the start/end line numbers for that section and all 323 | # all of the other sections that appear *after* the specified section. 324 | def increment_section_line_numbers(section_index) 325 | @section_names[section_index..(@section_names.length - 1)].each do |name| 326 | section = @sections_hash[name] 327 | section.increment_line_nums 328 | end 329 | end 330 | 331 | def flush_buffer_to_file(buffer, file) 332 | return if buffer.empty? 333 | 334 | buffer.each { |l| file.puts(l) } 335 | buffer.clear 336 | end 337 | end 338 | end 339 | -------------------------------------------------------------------------------- /spec/unit/puppet/provider/ini_subsetting/ruby_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'puppet' 5 | 6 | provider_class = Puppet::Type.type(:ini_subsetting).provider(:ruby) 7 | describe provider_class do 8 | include PuppetlabsSpec::Files 9 | 10 | let(:tmpfile) { tmpfilename('ini_setting_test') } 11 | 12 | def validate_file(expected_content, tmpfile) 13 | expect(File.read(tmpfile)).to eq expected_content 14 | end 15 | 16 | before :each do 17 | File.write(tmpfile, orig_content) 18 | end 19 | 20 | context 'when ensuring that a subsetting is present' do 21 | let(:common_params) do 22 | { 23 | title: 'ini_setting_ensure_present_test', 24 | path: tmpfile, 25 | section: '', 26 | key_val_separator: '=', 27 | setting: 'JAVA_ARGS' 28 | } 29 | end 30 | 31 | let(:orig_content) do 32 | <<-JAVAARGS 33 | JAVA_ARGS="-Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" 34 | JAVAARGS 35 | end 36 | 37 | expected_content_one = <<-JAVAARGS 38 | JAVA_ARGS="-Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof -Xms128m" 39 | JAVAARGS 40 | it 'adds a missing subsetting' do 41 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m')) 42 | provider = described_class.new(resource) 43 | expect(provider.exists?).to be_nil 44 | provider.create 45 | validate_file(expected_content_one, tmpfile) 46 | end 47 | 48 | expected_content_two = <<-JAVAARGS 49 | JAVA_ARGS="-Xms128m -Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" 50 | JAVAARGS 51 | it 'adds a missing subsetting element at the beginning of the line' do 52 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m', insert_type: :start)) 53 | provider = described_class.new(resource) 54 | expect(provider.exists?).to be_nil 55 | provider.create 56 | validate_file(expected_content_two, tmpfile) 57 | end 58 | 59 | expected_content_three = <<-JAVAARGS 60 | JAVA_ARGS="-Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof -Xms128m" 61 | JAVAARGS 62 | it 'adds a missing subsetting element at the end of the line' do 63 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m', insert_type: :end)) 64 | provider = described_class.new(resource) 65 | expect(provider.exists?).to be_nil 66 | provider.create 67 | validate_file(expected_content_three, tmpfile) 68 | end 69 | 70 | expected_content_four = <<-JAVAARGS 71 | JAVA_ARGS="-Xmx192m -Xms128m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" 72 | JAVAARGS 73 | it 'adds a missing subsetting element after the given item' do 74 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m', insert_type: :after, insert_value: '-Xmx')) 75 | provider = described_class.new(resource) 76 | expect(provider.exists?).to be_nil 77 | provider.create 78 | validate_file(expected_content_four, tmpfile) 79 | end 80 | 81 | expected_content_five = <<-JAVAARGS 82 | JAVA_ARGS="-Xms128m -Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" 83 | JAVAARGS 84 | it 'adds a missing subsetting element before the given item' do 85 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m', insert_type: :before, insert_value: '-Xmx')) 86 | provider = described_class.new(resource) 87 | expect(provider.exists?).to be_nil 88 | provider.create 89 | validate_file(expected_content_five, tmpfile) 90 | end 91 | 92 | expected_content_six = <<-JAVAARGS 93 | JAVA_ARGS="-Xmx192m -Xms128m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" 94 | JAVAARGS 95 | it 'adds a missing subsetting element at the given index' do 96 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m', insert_type: :index, insert_value: '1')) 97 | provider = described_class.new(resource) 98 | expect(provider.exists?).to be_nil 99 | provider.create 100 | validate_file(expected_content_six, tmpfile) 101 | end 102 | 103 | expected_content_seven = <<-JAVAARGS 104 | JAVA_ARGS="-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" 105 | JAVAARGS 106 | it 'removes an existing subsetting' do 107 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xmx')) 108 | provider = described_class.new(resource) 109 | expect(provider.exists?).to eq '192m' 110 | provider.destroy 111 | validate_file(expected_content_seven, tmpfile) 112 | end 113 | 114 | expected_content_eight = <<-JAVAARGS 115 | JAVA_ARGS="-Xmx192m" 116 | JAVAARGS 117 | it 'is able to remove several subsettings with the same name' do 118 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-XX')) 119 | provider = described_class.new(resource) 120 | expect(provider.exists?).to eq ':+HeapDumpOnOutOfMemoryError' 121 | provider.destroy 122 | validate_file(expected_content_eight, tmpfile) 123 | end 124 | 125 | expected_content_nine = <<-JAVAARGS 126 | JAVA_ARGS="-Xmx256m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" 127 | JAVAARGS 128 | it 'modifies an existing subsetting' do 129 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xmx', value: '256m')) 130 | provider = described_class.new(resource) 131 | expect(provider.exists?).to eq '192m' 132 | provider.value = '256m' 133 | validate_file(expected_content_nine, tmpfile) 134 | end 135 | 136 | expected_content_ten = <<-JAVAARGS 137 | JAVA_ARGS="-Xmx192m -XXtest -XXtest" 138 | JAVAARGS 139 | it 'is able to modify several subsettings with the same name' do 140 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-XX', value: 'test')) 141 | provider = described_class.new(resource) 142 | expect(provider.exists?).to eq ':+HeapDumpOnOutOfMemoryError' 143 | provider.value = 'test' 144 | validate_file(expected_content_ten, tmpfile) 145 | end 146 | end 147 | 148 | context 'when working with subsettings in files with unquoted settings values' do 149 | let(:common_params) do 150 | { 151 | title: 'ini_setting_ensure_present_test', 152 | path: tmpfile, 153 | section: 'main', 154 | setting: 'reports' 155 | } 156 | end 157 | 158 | let(:orig_content) do 159 | <<-INIFILE 160 | [main] 161 | 162 | reports = http,foo 163 | INIFILE 164 | end 165 | 166 | expected_content_one = <<-INIFILE 167 | [main] 168 | 169 | reports = foo 170 | INIFILE 171 | it 'removes an existing subsetting' do 172 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'http', subsetting_separator: ',')) 173 | provider = described_class.new(resource) 174 | expect(provider.exists?).to eq '' 175 | provider.destroy 176 | validate_file(expected_content_one, tmpfile) 177 | end 178 | 179 | expected_content_two = <<-INIFILE 180 | [main] 181 | 182 | reports = http,foo,puppetdb 183 | INIFILE 184 | it "adds a new subsetting when the 'parent' setting already exists" do 185 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'puppetdb', subsetting_separator: ',')) 186 | provider = described_class.new(resource) 187 | expect(provider.exists?).to be_nil 188 | provider.value = '' 189 | validate_file(expected_content_two, tmpfile) 190 | end 191 | 192 | expected_content_three = <<-INIFILE 193 | [main] 194 | 195 | reports = http,foo 196 | somenewsetting = puppetdb 197 | INIFILE 198 | it "adds a new subsetting when the 'parent' setting does not already exist" do 199 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(setting: 'somenewsetting', subsetting: 'puppetdb', subsetting_separator: ',')) 200 | provider = described_class.new(resource) 201 | expect(provider.exists?).to be_nil 202 | provider.value = '' 203 | validate_file(expected_content_three, tmpfile) 204 | end 205 | end 206 | 207 | context 'when working with subsettings in files with use_exact_match' do 208 | let(:common_params) do 209 | { 210 | title: 'ini_setting_ensure_present_test', 211 | path: tmpfile, 212 | section: 'main', 213 | setting: 'reports', 214 | use_exact_match: true 215 | } 216 | end 217 | 218 | let(:orig_content) do 219 | <<-INIFILE 220 | [main] 221 | 222 | reports = http,foo 223 | INIFILE 224 | end 225 | 226 | expected_content_one = <<-INIFILE 227 | [main] 228 | 229 | reports = http,foo,fo 230 | INIFILE 231 | it "adds a new subsetting when the 'parent' setting already exists" do 232 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'fo', subsetting_separator: ',')) 233 | provider = described_class.new(resource) 234 | provider.value = '' 235 | expect(validate_file(expected_content_one, tmpfile)).to be_truthy 236 | end 237 | 238 | expected_content_two = <<-INIFILE 239 | [main] 240 | 241 | reports = http,foo 242 | INIFILE 243 | it 'does not remove substring subsettings' do 244 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'fo', subsetting_separator: ',')) 245 | provider = described_class.new(resource) 246 | provider.value = '' 247 | provider.destroy 248 | expect(validate_file(expected_content_two, tmpfile)).to be_truthy 249 | end 250 | end 251 | 252 | context 'when working with subsettings in files with subsetting_key_val_separator' do 253 | let(:common_params) do 254 | { 255 | title: 'ini_setting_ensure_present_test', 256 | path: tmpfile, 257 | section: 'main', 258 | setting: 'reports', 259 | subsetting_separator: ',', 260 | subsetting_key_val_separator: ':' 261 | } 262 | end 263 | 264 | let(:orig_content) do 265 | <<-INIFILE 266 | [main] 267 | 268 | reports = a:1,b:2 269 | INIFILE 270 | end 271 | 272 | expected_content_one = <<-INIFILE 273 | [main] 274 | 275 | reports = a:1,b:2,c:3 276 | INIFILE 277 | it "adds a new subsetting when the 'parent' setting already exists" do 278 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'c', value: '3')) 279 | provider = described_class.new(resource) 280 | provider.value = '3' 281 | expect(validate_file(expected_content_one, tmpfile)).to be_truthy 282 | end 283 | 284 | expected_content_two = <<-INIFILE 285 | [main] 286 | 287 | reports = a:1,b:2 288 | somenewsetting = c:3 289 | INIFILE 290 | it "adds a new subsetting when the 'parent' setting does not already exist" do 291 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'c', value: '3', setting: 'somenewsetting')) 292 | provider = described_class.new(resource) 293 | expect(provider.exists?).to be_nil 294 | provider.value = '3' 295 | validate_file(expected_content_two, tmpfile) 296 | end 297 | 298 | expected_content_three = <<-INIFILE 299 | [main] 300 | 301 | reports = a:1 302 | INIFILE 303 | it 'is able to remove the existing subsetting' do 304 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'b')) 305 | provider = described_class.new(resource) 306 | expect(provider.exists?).to eq '2' 307 | provider.destroy 308 | validate_file(expected_content_three, tmpfile) 309 | end 310 | 311 | expected_content_four = <<-INIFILE 312 | [main] 313 | 314 | reports = a:1,b:5 315 | INIFILE 316 | it 'is able to modify the existing subsetting' do 317 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'b', value: '5')) 318 | provider = described_class.new(resource) 319 | expect(provider.exists?).to eq '2' 320 | provider.value = '5' 321 | validate_file(expected_content_four, tmpfile) 322 | end 323 | end 324 | 325 | context 'when delete_if_empty is toggled to true' do 326 | let(:common_params) do 327 | { 328 | title: 'ini_setting_delete_if_empty_test', 329 | path: tmpfile, 330 | section: 'main', 331 | delete_if_empty: true 332 | } 333 | end 334 | 335 | let(:orig_content) do 336 | <<-INIFILE 337 | [main] 338 | reports = http 339 | something = else 340 | INIFILE 341 | end 342 | 343 | expected_content_one = <<-INIFILE 344 | [main] 345 | something = else 346 | INIFILE 347 | 348 | expected_content_two = '' 349 | 350 | it 'removes the subsetting when the it is empty' do 351 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(setting: 'reports', subsetting: 'http', subsetting_separator: ',')) 352 | provider = described_class.new(resource) 353 | provider.destroy 354 | expect(validate_file(expected_content_one, tmpfile)).to be_truthy 355 | resource = Puppet::Type::Ini_subsetting.new(common_params.merge(setting: 'something', subsetting: 'else', subsetting_separator: ',')) 356 | provider = described_class.new(resource) 357 | provider.destroy 358 | expect(validate_file(expected_content_two, tmpfile)).to be_truthy 359 | end 360 | end 361 | end 362 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | require: 3 | - rubocop-performance 4 | - rubocop-rspec 5 | AllCops: 6 | NewCops: enable 7 | DisplayCopNames: true 8 | TargetRubyVersion: 3.1 9 | Include: 10 | - "**/*.rb" 11 | Exclude: 12 | - bin/* 13 | - ".vendor/**/*" 14 | - "**/Gemfile" 15 | - "**/Rakefile" 16 | - pkg/**/* 17 | - spec/fixtures/**/* 18 | - vendor/**/* 19 | - "**/Puppetfile" 20 | - "**/Vagrantfile" 21 | - "**/Guardfile" 22 | inherit_from: ".rubocop_todo.yml" 23 | Layout/LineLength: 24 | Description: People have wide screens, use them. 25 | Max: 200 26 | RSpec/BeforeAfterAll: 27 | Description: Beware of using after(:all) as it may cause state to leak between tests. 28 | A necessary evil in acceptance testing. 29 | Exclude: 30 | - spec/acceptance/**/*.rb 31 | RSpec/HookArgument: 32 | Description: Prefer explicit :each argument, matching existing module's style 33 | EnforcedStyle: each 34 | RSpec/DescribeSymbol: 35 | Exclude: 36 | - spec/unit/facter/**/*.rb 37 | Style/BlockDelimiters: 38 | Description: Prefer braces for chaining. Mostly an aesthetical choice. Better to 39 | be consistent then. 40 | EnforcedStyle: braces_for_chaining 41 | Style/ClassAndModuleChildren: 42 | Description: Compact style reduces the required amount of indentation. 43 | EnforcedStyle: compact 44 | Style/EmptyElse: 45 | Description: Enforce against empty else clauses, but allow `nil` for clarity. 46 | EnforcedStyle: empty 47 | Style/FormatString: 48 | Description: Following the main puppet project's style, prefer the % format format. 49 | EnforcedStyle: percent 50 | Style/FormatStringToken: 51 | Description: Following the main puppet project's style, prefer the simpler template 52 | tokens over annotated ones. 53 | EnforcedStyle: template 54 | Style/Lambda: 55 | Description: Prefer the keyword for easier discoverability. 56 | EnforcedStyle: literal 57 | Style/RegexpLiteral: 58 | Description: Community preference. See https://github.com/voxpupuli/modulesync_config/issues/168 59 | EnforcedStyle: percent_r 60 | Style/TernaryParentheses: 61 | Description: Checks for use of parentheses around ternary conditions. Enforce parentheses 62 | on complex expressions for better readability, but seriously consider breaking 63 | it up. 64 | EnforcedStyle: require_parentheses_when_complex 65 | Style/TrailingCommaInArguments: 66 | Description: Prefer always trailing comma on multiline argument lists. This makes 67 | diffs, and re-ordering nicer. 68 | EnforcedStyleForMultiline: comma 69 | Style/TrailingCommaInArrayLiteral: 70 | Description: Prefer always trailing comma on multiline literals. This makes diffs, 71 | and re-ordering nicer. 72 | EnforcedStyleForMultiline: comma 73 | Style/SymbolArray: 74 | Description: Using percent style obscures symbolic intent of array's contents. 75 | EnforcedStyle: brackets 76 | RSpec/MessageSpies: 77 | EnforcedStyle: receive 78 | Style/Documentation: 79 | Exclude: 80 | - lib/puppet/parser/functions/**/* 81 | - spec/**/* 82 | Style/WordArray: 83 | EnforcedStyle: brackets 84 | Performance/AncestorsInclude: 85 | Enabled: true 86 | Performance/BigDecimalWithNumericArgument: 87 | Enabled: true 88 | Performance/BlockGivenWithExplicitBlock: 89 | Enabled: true 90 | Performance/CaseWhenSplat: 91 | Enabled: true 92 | Performance/ConstantRegexp: 93 | Enabled: true 94 | Performance/MethodObjectAsBlock: 95 | Enabled: true 96 | Performance/RedundantSortBlock: 97 | Enabled: true 98 | Performance/RedundantStringChars: 99 | Enabled: true 100 | Performance/ReverseFirst: 101 | Enabled: true 102 | Performance/SortReverse: 103 | Enabled: true 104 | Performance/Squeeze: 105 | Enabled: true 106 | Performance/StringInclude: 107 | Enabled: true 108 | Performance/Sum: 109 | Enabled: true 110 | Style/CollectionMethods: 111 | Enabled: true 112 | Style/MethodCalledOnDoEndBlock: 113 | Enabled: true 114 | Style/StringMethods: 115 | Enabled: true 116 | Bundler/GemFilename: 117 | Enabled: false 118 | Bundler/InsecureProtocolSource: 119 | Enabled: false 120 | Capybara/CurrentPathExpectation: 121 | Enabled: false 122 | Capybara/VisibilityMatcher: 123 | Enabled: false 124 | Gemspec/DuplicatedAssignment: 125 | Enabled: false 126 | Gemspec/OrderedDependencies: 127 | Enabled: false 128 | Gemspec/RequiredRubyVersion: 129 | Enabled: false 130 | Gemspec/RubyVersionGlobalsUsage: 131 | Enabled: false 132 | Layout/ArgumentAlignment: 133 | Enabled: false 134 | Layout/BeginEndAlignment: 135 | Enabled: false 136 | Layout/ClosingHeredocIndentation: 137 | Enabled: false 138 | Layout/EmptyComment: 139 | Enabled: false 140 | Layout/EmptyLineAfterGuardClause: 141 | Enabled: false 142 | Layout/EmptyLinesAroundArguments: 143 | Enabled: false 144 | Layout/EmptyLinesAroundAttributeAccessor: 145 | Enabled: false 146 | Layout/EndOfLine: 147 | Enabled: false 148 | Layout/FirstArgumentIndentation: 149 | Enabled: false 150 | Layout/HashAlignment: 151 | Enabled: false 152 | Layout/HeredocIndentation: 153 | Enabled: false 154 | Layout/LeadingEmptyLines: 155 | Enabled: false 156 | Layout/SpaceAroundMethodCallOperator: 157 | Enabled: false 158 | Layout/SpaceInsideArrayLiteralBrackets: 159 | Enabled: false 160 | Layout/SpaceInsideReferenceBrackets: 161 | Enabled: false 162 | Lint/BigDecimalNew: 163 | Enabled: false 164 | Lint/BooleanSymbol: 165 | Enabled: false 166 | Lint/ConstantDefinitionInBlock: 167 | Enabled: false 168 | Lint/DeprecatedOpenSSLConstant: 169 | Enabled: false 170 | Lint/DisjunctiveAssignmentInConstructor: 171 | Enabled: false 172 | Lint/DuplicateElsifCondition: 173 | Enabled: false 174 | Lint/DuplicateRequire: 175 | Enabled: false 176 | Lint/DuplicateRescueException: 177 | Enabled: false 178 | Lint/EmptyConditionalBody: 179 | Enabled: false 180 | Lint/EmptyFile: 181 | Enabled: false 182 | Lint/ErbNewArguments: 183 | Enabled: false 184 | Lint/FloatComparison: 185 | Enabled: false 186 | Lint/HashCompareByIdentity: 187 | Enabled: false 188 | Lint/IdentityComparison: 189 | Enabled: false 190 | Lint/InterpolationCheck: 191 | Enabled: false 192 | Lint/MissingCopEnableDirective: 193 | Enabled: false 194 | Lint/MixedRegexpCaptureTypes: 195 | Enabled: false 196 | Lint/NestedPercentLiteral: 197 | Enabled: false 198 | Lint/NonDeterministicRequireOrder: 199 | Enabled: false 200 | Lint/OrderedMagicComments: 201 | Enabled: false 202 | Lint/OutOfRangeRegexpRef: 203 | Enabled: false 204 | Lint/RaiseException: 205 | Enabled: false 206 | Lint/RedundantCopEnableDirective: 207 | Enabled: false 208 | Lint/RedundantRequireStatement: 209 | Enabled: false 210 | Lint/RedundantSafeNavigation: 211 | Enabled: false 212 | Lint/RedundantWithIndex: 213 | Enabled: false 214 | Lint/RedundantWithObject: 215 | Enabled: false 216 | Lint/RegexpAsCondition: 217 | Enabled: false 218 | Lint/ReturnInVoidContext: 219 | Enabled: false 220 | Lint/SafeNavigationConsistency: 221 | Enabled: false 222 | Lint/SafeNavigationWithEmpty: 223 | Enabled: false 224 | Lint/SelfAssignment: 225 | Enabled: false 226 | Lint/SendWithMixinArgument: 227 | Enabled: false 228 | Lint/ShadowedArgument: 229 | Enabled: false 230 | Lint/StructNewOverride: 231 | Enabled: false 232 | Lint/ToJSON: 233 | Enabled: false 234 | Lint/TopLevelReturnWithArgument: 235 | Enabled: false 236 | Lint/TrailingCommaInAttributeDeclaration: 237 | Enabled: false 238 | Lint/UnreachableLoop: 239 | Enabled: false 240 | Lint/UriEscapeUnescape: 241 | Enabled: false 242 | Lint/UriRegexp: 243 | Enabled: false 244 | Lint/UselessMethodDefinition: 245 | Enabled: false 246 | Lint/UselessTimes: 247 | Enabled: false 248 | Metrics/AbcSize: 249 | Enabled: false 250 | Metrics/BlockLength: 251 | Enabled: false 252 | Metrics/BlockNesting: 253 | Enabled: false 254 | Metrics/ClassLength: 255 | Enabled: false 256 | Metrics/CyclomaticComplexity: 257 | Enabled: false 258 | Metrics/MethodLength: 259 | Enabled: false 260 | Metrics/ModuleLength: 261 | Enabled: false 262 | Metrics/ParameterLists: 263 | Enabled: false 264 | Metrics/PerceivedComplexity: 265 | Enabled: false 266 | Migration/DepartmentName: 267 | Enabled: false 268 | Naming/AccessorMethodName: 269 | Enabled: false 270 | Naming/BlockParameterName: 271 | Enabled: false 272 | Naming/HeredocDelimiterCase: 273 | Enabled: false 274 | Naming/HeredocDelimiterNaming: 275 | Enabled: false 276 | Naming/MemoizedInstanceVariableName: 277 | Enabled: false 278 | Naming/MethodParameterName: 279 | Enabled: false 280 | Naming/RescuedExceptionsVariableName: 281 | Enabled: false 282 | Naming/VariableNumber: 283 | Enabled: false 284 | Performance/BindCall: 285 | Enabled: false 286 | Performance/DeletePrefix: 287 | Enabled: false 288 | Performance/DeleteSuffix: 289 | Enabled: false 290 | Performance/InefficientHashSearch: 291 | Enabled: false 292 | Performance/UnfreezeString: 293 | Enabled: false 294 | Performance/UriDefaultParser: 295 | Enabled: false 296 | RSpec/Be: 297 | Enabled: false 298 | RSpec/Capybara/FeatureMethods: 299 | Enabled: false 300 | RSpec/ContainExactly: 301 | Enabled: false 302 | RSpec/ContextMethod: 303 | Enabled: false 304 | RSpec/ContextWording: 305 | Enabled: false 306 | RSpec/DescribeClass: 307 | Enabled: false 308 | RSpec/EmptyHook: 309 | Enabled: false 310 | RSpec/EmptyLineAfterExample: 311 | Enabled: false 312 | RSpec/EmptyLineAfterExampleGroup: 313 | Enabled: false 314 | RSpec/EmptyLineAfterHook: 315 | Enabled: false 316 | RSpec/ExampleLength: 317 | Enabled: false 318 | RSpec/ExampleWithoutDescription: 319 | Enabled: false 320 | RSpec/ExpectChange: 321 | Enabled: false 322 | RSpec/ExpectInHook: 323 | Enabled: false 324 | RSpec/FactoryBot/AttributeDefinedStatically: 325 | Enabled: false 326 | RSpec/FactoryBot/CreateList: 327 | Enabled: false 328 | RSpec/FactoryBot/FactoryClassName: 329 | Enabled: false 330 | RSpec/HooksBeforeExamples: 331 | Enabled: false 332 | RSpec/ImplicitBlockExpectation: 333 | Enabled: false 334 | RSpec/ImplicitSubject: 335 | Enabled: false 336 | RSpec/LeakyConstantDeclaration: 337 | Enabled: false 338 | RSpec/LetBeforeExamples: 339 | Enabled: false 340 | RSpec/MatchArray: 341 | Enabled: false 342 | RSpec/MissingExampleGroupArgument: 343 | Enabled: false 344 | RSpec/MultipleExpectations: 345 | Enabled: false 346 | RSpec/MultipleMemoizedHelpers: 347 | Enabled: false 348 | RSpec/MultipleSubjects: 349 | Enabled: false 350 | RSpec/NestedGroups: 351 | Enabled: false 352 | RSpec/PredicateMatcher: 353 | Enabled: false 354 | RSpec/ReceiveCounts: 355 | Enabled: false 356 | RSpec/ReceiveNever: 357 | Enabled: false 358 | RSpec/RepeatedExampleGroupBody: 359 | Enabled: false 360 | RSpec/RepeatedExampleGroupDescription: 361 | Enabled: false 362 | RSpec/RepeatedIncludeExample: 363 | Enabled: false 364 | RSpec/ReturnFromStub: 365 | Enabled: false 366 | RSpec/SharedExamples: 367 | Enabled: false 368 | RSpec/StubbedMock: 369 | Enabled: false 370 | RSpec/UnspecifiedException: 371 | Enabled: false 372 | RSpec/VariableDefinition: 373 | Enabled: false 374 | RSpec/VoidExpect: 375 | Enabled: false 376 | RSpec/Yield: 377 | Enabled: false 378 | Security/Open: 379 | Enabled: false 380 | Style/AccessModifierDeclarations: 381 | Enabled: false 382 | Style/AccessorGrouping: 383 | Enabled: false 384 | Style/BisectedAttrAccessor: 385 | Enabled: false 386 | Style/CaseLikeIf: 387 | Enabled: false 388 | Style/ClassEqualityComparison: 389 | Enabled: false 390 | Style/ColonMethodDefinition: 391 | Enabled: false 392 | Style/CombinableLoops: 393 | Enabled: false 394 | Style/CommentedKeyword: 395 | Enabled: false 396 | Style/Dir: 397 | Enabled: false 398 | Style/DoubleCopDisableDirective: 399 | Enabled: false 400 | Style/EmptyBlockParameter: 401 | Enabled: false 402 | Style/EmptyLambdaParameter: 403 | Enabled: false 404 | Style/Encoding: 405 | Enabled: false 406 | Style/EvalWithLocation: 407 | Enabled: false 408 | Style/ExpandPathArguments: 409 | Enabled: false 410 | Style/ExplicitBlockArgument: 411 | Enabled: false 412 | Style/ExponentialNotation: 413 | Enabled: false 414 | Style/FloatDivision: 415 | Enabled: false 416 | Style/FrozenStringLiteralComment: 417 | Enabled: false 418 | Style/GlobalStdStream: 419 | Enabled: false 420 | Style/HashAsLastArrayItem: 421 | Enabled: false 422 | Style/HashLikeCase: 423 | Enabled: false 424 | Style/HashTransformKeys: 425 | Enabled: false 426 | Style/HashTransformValues: 427 | Enabled: false 428 | Style/IfUnlessModifier: 429 | Enabled: false 430 | Style/KeywordParametersOrder: 431 | Enabled: false 432 | Style/MinMax: 433 | Enabled: false 434 | Style/MixinUsage: 435 | Enabled: false 436 | Style/MultilineWhenThen: 437 | Enabled: false 438 | Style/NegatedUnless: 439 | Enabled: false 440 | Style/NumericPredicate: 441 | Enabled: false 442 | Style/OptionalBooleanParameter: 443 | Enabled: false 444 | Style/OrAssignment: 445 | Enabled: false 446 | Style/RandomWithOffset: 447 | Enabled: false 448 | Style/RedundantAssignment: 449 | Enabled: false 450 | Style/RedundantCondition: 451 | Enabled: false 452 | Style/RedundantConditional: 453 | Enabled: false 454 | Style/RedundantFetchBlock: 455 | Enabled: false 456 | Style/RedundantFileExtensionInRequire: 457 | Enabled: false 458 | Style/RedundantRegexpCharacterClass: 459 | Enabled: false 460 | Style/RedundantRegexpEscape: 461 | Enabled: false 462 | Style/RedundantSelfAssignment: 463 | Enabled: false 464 | Style/RedundantSort: 465 | Enabled: false 466 | Style/RescueStandardError: 467 | Enabled: false 468 | Style/SingleArgumentDig: 469 | Enabled: false 470 | Style/SlicingWithRange: 471 | Enabled: false 472 | Style/SoleNestedConditional: 473 | Enabled: false 474 | Style/StderrPuts: 475 | Enabled: false 476 | Style/StringConcatenation: 477 | Enabled: false 478 | Style/Strip: 479 | Enabled: false 480 | Style/SymbolProc: 481 | Enabled: false 482 | Style/TrailingBodyOnClass: 483 | Enabled: false 484 | Style/TrailingBodyOnMethodDefinition: 485 | Enabled: false 486 | Style/TrailingBodyOnModule: 487 | Enabled: false 488 | Style/TrailingCommaInHashLiteral: 489 | Enabled: false 490 | Style/TrailingMethodEndStatement: 491 | Enabled: false 492 | Style/UnpackFirst: 493 | Enabled: false 494 | Capybara/MatchStyle: 495 | Enabled: false 496 | Capybara/NegationMatcher: 497 | Enabled: false 498 | Capybara/SpecificActions: 499 | Enabled: false 500 | Capybara/SpecificFinders: 501 | Enabled: false 502 | Capybara/SpecificMatcher: 503 | Enabled: false 504 | Gemspec/DeprecatedAttributeAssignment: 505 | Enabled: false 506 | Gemspec/DevelopmentDependencies: 507 | Enabled: false 508 | Gemspec/RequireMFA: 509 | Enabled: false 510 | Layout/LineContinuationLeadingSpace: 511 | Enabled: false 512 | Layout/LineContinuationSpacing: 513 | Enabled: false 514 | Layout/LineEndStringConcatenationIndentation: 515 | Enabled: false 516 | Layout/SpaceBeforeBrackets: 517 | Enabled: false 518 | Lint/AmbiguousAssignment: 519 | Enabled: false 520 | Lint/AmbiguousOperatorPrecedence: 521 | Enabled: false 522 | Lint/AmbiguousRange: 523 | Enabled: false 524 | Lint/ConstantOverwrittenInRescue: 525 | Enabled: false 526 | Lint/DeprecatedConstants: 527 | Enabled: false 528 | Lint/DuplicateBranch: 529 | Enabled: false 530 | Lint/DuplicateMagicComment: 531 | Enabled: false 532 | Lint/DuplicateMatchPattern: 533 | Enabled: false 534 | Lint/DuplicateRegexpCharacterClassElement: 535 | Enabled: false 536 | Lint/EmptyBlock: 537 | Enabled: false 538 | Lint/EmptyClass: 539 | Enabled: false 540 | Lint/EmptyInPattern: 541 | Enabled: false 542 | Lint/IncompatibleIoSelectWithFiberScheduler: 543 | Enabled: false 544 | Lint/LambdaWithoutLiteralBlock: 545 | Enabled: false 546 | Lint/NoReturnInBeginEndBlocks: 547 | Enabled: false 548 | Lint/NonAtomicFileOperation: 549 | Enabled: false 550 | Lint/NumberedParameterAssignment: 551 | Enabled: false 552 | Lint/OrAssignmentToConstant: 553 | Enabled: false 554 | Lint/RedundantDirGlobSort: 555 | Enabled: false 556 | Lint/RefinementImportMethods: 557 | Enabled: false 558 | Lint/RequireRangeParentheses: 559 | Enabled: false 560 | Lint/RequireRelativeSelfPath: 561 | Enabled: false 562 | Lint/SymbolConversion: 563 | Enabled: false 564 | Lint/ToEnumArguments: 565 | Enabled: false 566 | Lint/TripleQuotes: 567 | Enabled: false 568 | Lint/UnexpectedBlockArity: 569 | Enabled: false 570 | Lint/UnmodifiedReduceAccumulator: 571 | Enabled: false 572 | Lint/UselessRescue: 573 | Enabled: false 574 | Lint/UselessRuby2Keywords: 575 | Enabled: false 576 | Metrics/CollectionLiteralLength: 577 | Enabled: false 578 | Naming/BlockForwarding: 579 | Enabled: false 580 | Performance/CollectionLiteralInLoop: 581 | Enabled: false 582 | Performance/ConcurrentMonotonicTime: 583 | Enabled: false 584 | Performance/MapCompact: 585 | Enabled: false 586 | Performance/RedundantEqualityComparisonBlock: 587 | Enabled: false 588 | Performance/RedundantSplitRegexpArgument: 589 | Enabled: false 590 | Performance/StringIdentifierArgument: 591 | Enabled: false 592 | RSpec/BeEq: 593 | Enabled: false 594 | RSpec/BeNil: 595 | Enabled: false 596 | RSpec/ChangeByZero: 597 | Enabled: false 598 | RSpec/ClassCheck: 599 | Enabled: false 600 | RSpec/DuplicatedMetadata: 601 | Enabled: false 602 | RSpec/ExcessiveDocstringSpacing: 603 | Enabled: false 604 | RSpec/FactoryBot/ConsistentParenthesesStyle: 605 | Enabled: false 606 | RSpec/FactoryBot/FactoryNameStyle: 607 | Enabled: false 608 | RSpec/FactoryBot/SyntaxMethods: 609 | Enabled: false 610 | RSpec/IdenticalEqualityAssertion: 611 | Enabled: false 612 | RSpec/NoExpectationExample: 613 | Enabled: false 614 | RSpec/PendingWithoutReason: 615 | Enabled: false 616 | RSpec/Rails/AvoidSetupHook: 617 | Enabled: false 618 | RSpec/Rails/HaveHttpStatus: 619 | Enabled: false 620 | RSpec/Rails/InferredSpecType: 621 | Enabled: false 622 | RSpec/Rails/MinitestAssertions: 623 | Enabled: false 624 | RSpec/Rails/TravelAround: 625 | Enabled: false 626 | RSpec/RedundantAround: 627 | Enabled: false 628 | RSpec/SkipBlockInsideExample: 629 | Enabled: false 630 | RSpec/SortMetadata: 631 | Enabled: false 632 | RSpec/SubjectDeclaration: 633 | Enabled: false 634 | RSpec/VerifiedDoubleReference: 635 | Enabled: false 636 | Security/CompoundHash: 637 | Enabled: false 638 | Security/IoMethods: 639 | Enabled: false 640 | Style/ArgumentsForwarding: 641 | Enabled: false 642 | Style/ArrayIntersect: 643 | Enabled: false 644 | Style/CollectionCompact: 645 | Enabled: false 646 | Style/ComparableClamp: 647 | Enabled: false 648 | Style/ConcatArrayLiterals: 649 | Enabled: false 650 | Style/DataInheritance: 651 | Enabled: false 652 | Style/DirEmpty: 653 | Enabled: false 654 | Style/DocumentDynamicEvalDefinition: 655 | Enabled: false 656 | Style/EmptyHeredoc: 657 | Enabled: false 658 | Style/EndlessMethod: 659 | Enabled: false 660 | Style/EnvHome: 661 | Enabled: false 662 | Style/FetchEnvVar: 663 | Enabled: false 664 | Style/FileEmpty: 665 | Enabled: false 666 | Style/FileRead: 667 | Enabled: false 668 | Style/FileWrite: 669 | Enabled: false 670 | Style/HashConversion: 671 | Enabled: false 672 | Style/HashExcept: 673 | Enabled: false 674 | Style/IfWithBooleanLiteralBranches: 675 | Enabled: false 676 | Style/InPatternThen: 677 | Enabled: false 678 | Style/MagicCommentFormat: 679 | Enabled: false 680 | Style/MapCompactWithConditionalBlock: 681 | Enabled: false 682 | Style/MapToHash: 683 | Enabled: false 684 | Style/MapToSet: 685 | Enabled: false 686 | Style/MinMaxComparison: 687 | Enabled: false 688 | Style/MultilineInPatternThen: 689 | Enabled: false 690 | Style/NegatedIfElseCondition: 691 | Enabled: false 692 | Style/NestedFileDirname: 693 | Enabled: false 694 | Style/NilLambda: 695 | Enabled: false 696 | Style/NumberedParameters: 697 | Enabled: false 698 | Style/NumberedParametersLimit: 699 | Enabled: false 700 | Style/ObjectThen: 701 | Enabled: false 702 | Style/OpenStructUse: 703 | Enabled: false 704 | Style/OperatorMethodCall: 705 | Enabled: false 706 | Style/QuotedSymbols: 707 | Enabled: false 708 | Style/RedundantArgument: 709 | Enabled: false 710 | Style/RedundantConstantBase: 711 | Enabled: false 712 | Style/RedundantDoubleSplatHashBraces: 713 | Enabled: false 714 | Style/RedundantEach: 715 | Enabled: false 716 | Style/RedundantHeredocDelimiterQuotes: 717 | Enabled: false 718 | Style/RedundantInitialize: 719 | Enabled: false 720 | Style/RedundantLineContinuation: 721 | Enabled: false 722 | Style/RedundantSelfAssignmentBranch: 723 | Enabled: false 724 | Style/RedundantStringEscape: 725 | Enabled: false 726 | Style/SelectByRegexp: 727 | Enabled: false 728 | Style/StringChars: 729 | Enabled: false 730 | Style/SwapValues: 731 | Enabled: false 732 | --------------------------------------------------------------------------------