| 2 |
rodolico |
1 |
# perlConfigFileUtility
|
|
|
2 |
|
|
|
3 |
A Perl utility for managing configuration files with template merging, interactive editing, and format conversion capabilities.
|
|
|
4 |
|
|
|
5 |
## Overview
|
|
|
6 |
|
|
|
7 |
`perlConfigFileUtility` is designed to simplify configuration file management by:
|
|
|
8 |
- Merging template structures with existing configuration files
|
|
|
9 |
- Providing an interactive hierarchical editor for modifying configurations
|
|
|
10 |
- Converting between YAML and JSON formats
|
|
|
11 |
- Automatically backing up config files when saving changes
|
|
|
12 |
|
|
|
13 |
## Background
|
|
|
14 |
|
|
|
15 |
This utility solves two primary problems:
|
|
|
16 |
|
|
|
17 |
1. **User-Friendly Editing**: Enables users unfamiliar with JSON/YAML syntax to safely edit configuration files through an interactive interface.
|
|
|
18 |
|
|
|
19 |
2. **Version Compatibility**: Handles configuration file evolution across software versions. When new key/value pairs are added to the application, the utility merges them with existing config files, ensuring older configurations remain compatible with newer software.
|
|
|
20 |
|
|
|
21 |
### Implementation Approach
|
|
|
22 |
|
|
|
23 |
- Default configuration is stored as a Perl data structure in a separate file (loaded via `do` statement in project script)
|
|
|
24 |
- This template can be shared across multiple applications
|
|
|
25 |
- The utility loads existing YAML/JSON configs, merges missing values from the template, and presents an interactive editor
|
|
|
26 |
- Format conversion (YAML ↔ JSON) was added as a natural extension of the merge functionality
|
|
|
27 |
|
|
|
28 |
## Features
|
|
|
29 |
|
|
|
30 |
- **Template Merging**: Combine Perl data structure templates with existing YAML/JSON config files
|
|
|
31 |
- **Interactive Editor**: Navigate and edit nested hashes and arrays with a menu-driven interface
|
|
|
32 |
- **Dynamic Modifications**: Add new keys to hashes and elements to arrays on the fly
|
| 6 |
rodolico |
33 |
- **Rename Keys**: Rename hash keys while preserving their values in interactive edit mode
|
| 2 |
rodolico |
34 |
- **Delete Operations**: Remove keys from hashes and elements from arrays, including all nested children
|
| 6 |
rodolico |
35 |
- **Config Comparison**: Load and compare multiple config files to identify differences
|
| 2 |
rodolico |
36 |
- **Format Conversion**: Seamlessly convert between YAML and JSON configuration formats
|
|
|
37 |
- **Safe Updates**: Automatic backup creation before saving changes
|
|
|
38 |
|
|
|
39 |
## Usage
|
|
|
40 |
|
|
|
41 |
```bash
|
|
|
42 |
# Basic usage
|
| 6 |
rodolico |
43 |
perlConfigFileUtility [-t <template_file>] [-c <config_file>] [-o <output_file>] [-e] [-C]
|
| 2 |
rodolico |
44 |
|
|
|
45 |
# Positional arguments (backward compatibility)
|
|
|
46 |
perlConfigFileUtility <template_file> [config_file]
|
| 6 |
rodolico |
47 |
|
|
|
48 |
# Compare multiple config files
|
|
|
49 |
perlConfigFileUtility -C -c <file1> -c <file2> [-c <file3> ...]
|
| 2 |
rodolico |
50 |
```
|
|
|
51 |
|
| 3 |
rodolico |
52 |
## Example of template file
|
|
|
53 |
|
|
|
54 |
The template file is a Perl hash reference that defines the complete configuration structure with default values and inline documentation. Below is a simplified excerpt from the `sneakernet` project's datastructure file (see `../zfs_utils/sneakernet/sneakernet.datastructure` for the full example).
|
|
|
55 |
|
|
|
56 |
```perl
|
|
|
57 |
# Default configuration structure for sneakernet
|
|
|
58 |
# This file is loaded by the sneakernet script to provide default configuration values
|
|
|
59 |
# Variables from $programDefinition hashref (scriptDirectory, scriptFullPath) will be interpolated at runtime
|
|
|
60 |
|
|
|
61 |
{
|
|
|
62 |
'dryrun' => 0, # if set to 1, will not perform any changes, just log what would be done
|
|
|
63 |
'verbosity' => 1, # verbosity level for logging
|
|
|
64 |
'debug' => 0, # set to number greater than 0 for debugging program
|
|
|
65 |
'status_file' => "<scriptDirectory>/sneakernet_target.status", # file created on source server to track last copied dataset
|
|
|
66 |
'log_file' => "<scriptFullPath>.log",
|
|
|
67 |
'displayLogsOnTTY' => '', # if set to path of a tty, log messages will be sent there also. Example: /dev/ttyv1
|
|
|
68 |
'displayLogsOnConsole' => 1, # If set to non-zero, will send log messages to screen in addition to log file
|
|
|
69 |
'source' => { # information about source server
|
|
|
70 |
'hostname' => '', # used to see if we are on source
|
|
|
71 |
'poolname' => 'pool', # name of the ZFS default parent pool to export
|
|
|
72 |
'cleanUpScriptsDir' => '<scriptDirectory>/cleanupScripts', # location on disk where scripts to be sent to target are located
|
|
|
73 |
'report' => { # if set, will generate a report via email or by storing on a drive
|
|
|
74 |
'email' => '', # if set, will email the report to this address
|
|
|
75 |
'subject' => '', # subject of the report email (will be auto-generated if empty)
|
|
|
76 |
'targetDrive' => { # if set, will store the report on this drive
|
|
|
77 |
'label' => '', # the GPT or msdos label of the report drive, REQUIRED
|
|
|
78 |
'fstype' => '', # filesystem type of the report drive, default is msdos
|
|
|
79 |
'check_interval' => 15, # how often to check for the disk (seconds), message displayed every interval
|
|
|
80 |
'wait_timeout' => 300, # time to wait for the disk to appear in seconds
|
|
|
81 |
'mount_point' => '', # where to mount the report drive, default is /mnt/label
|
|
|
82 |
}
|
|
|
83 |
}
|
|
|
84 |
},
|
|
|
85 |
# ... additional sections: 'target', 'transport', 'datasets' (see full file)
|
|
|
86 |
}
|
|
|
87 |
```
|
|
|
88 |
|
|
|
89 |
### How it works
|
|
|
90 |
|
|
|
91 |
The template file serves dual purposes:
|
|
|
92 |
|
|
|
93 |
1. **Default Values**: The Perl script loads it at runtime using `$config = do 'scriptname.datastructure'`, which returns the hash reference with all defaults.
|
|
|
94 |
|
|
|
95 |
2. **Configuration Schema**: The inline comments document each setting's purpose, forming a self-documenting configuration schema that can be exported to YAML format (e.g., `scriptname.conf.yaml`).
|
|
|
96 |
|
|
|
97 |
When the datastructure changes, this tool can non-destructively merge the updated template with existing configuration files, preserving user customizations while adding new keys and documentation. The interactive edit mode (`-e`) provides safe, menu-driven configuration management.
|
|
|
98 |
|
| 2 |
rodolico |
99 |
## Options
|
|
|
100 |
|
|
|
101 |
| Option | Description |
|
|
|
102 |
|--------|-------------|
|
|
|
103 |
| `-t, --template <file>` | Template file (Perl hashref) |
|
| 6 |
rodolico |
104 |
| `-c, --config <file>` | Config file (YAML or JSON) - can specify multiple for comparison |
|
| 2 |
rodolico |
105 |
| `-o, --output <file>` | Output file (default: STDOUT) |
|
|
|
106 |
| `-e, --edit` | Interactive edit mode |
|
| 6 |
rodolico |
107 |
| `-C, --compare` | Compare multiple config files and show differences |
|
| 2 |
rodolico |
108 |
| `-v, --version` | Show version information |
|
|
|
109 |
| `-h, --help` | Show help message |
|
|
|
110 |
|
| 4 |
rodolico |
111 |
## Examples
|
|
|
112 |
|
|
|
113 |
### Merge Template with Existing Config
|
|
|
114 |
```bash
|
|
|
115 |
perlConfigFileUtility -t template.pl -c config.yaml -o output.yaml
|
|
|
116 |
```
|
|
|
117 |
|
|
|
118 |
### Interactive Editing
|
|
|
119 |
```bash
|
|
|
120 |
perlConfigFileUtility -c config.yaml -e
|
|
|
121 |
```
|
| 6 |
rodolico |
122 |
Navigate through nested structures, modify values, add new keys/elements, rename keys, or delete existing ones.
|
| 4 |
rodolico |
123 |
|
| 6 |
rodolico |
124 |
### Compare Multiple Config Files
|
|
|
125 |
```bash
|
|
|
126 |
# Compare two config files
|
|
|
127 |
perlConfigFileUtility -C -c production.yaml -c staging.yaml
|
|
|
128 |
|
|
|
129 |
# Compare three or more files
|
|
|
130 |
perlConfigFileUtility -C -c prod.yaml -c staging.yaml -c dev.yaml
|
|
|
131 |
|
|
|
132 |
# Save comparison report to file
|
|
|
133 |
perlConfigFileUtility -C -c config1.yaml -c config2.yaml -o comparison.txt
|
|
|
134 |
```
|
|
|
135 |
Shows all differences including missing keys and differing values.
|
|
|
136 |
|
| 4 |
rodolico |
137 |
### Format Conversion
|
|
|
138 |
```bash
|
|
|
139 |
# YAML to JSON
|
|
|
140 |
perlConfigFileUtility -c config.yaml -o config.json
|
|
|
141 |
|
|
|
142 |
# JSON to YAML
|
|
|
143 |
perlConfigFileUtility -c config.json -o config.yaml
|
|
|
144 |
```
|
|
|
145 |
|
|
|
146 |
### Update Config with New Template Keys
|
|
|
147 |
```bash
|
|
|
148 |
# Merge new template keys into existing config, preserving user values
|
|
|
149 |
perlConfigFileUtility -t myapp.datastructure -c myapp.conf.yaml -o myapp.conf.yaml
|
|
|
150 |
```
|
|
|
151 |
|
|
|
152 |
### Start Fresh from Template
|
|
|
153 |
```bash
|
|
|
154 |
# Export template as YAML config
|
|
|
155 |
perlConfigFileUtility -t myapp.datastructure -o myapp.conf.yaml
|
|
|
156 |
```
|
|
|
157 |
|
|
|
158 |
## Interactive Editor Commands
|
|
|
159 |
|
|
|
160 |
When in edit mode (`-e`), the following commands are available:
|
|
|
161 |
|
|
|
162 |
- **Number (1-N)**: Select and edit the numbered item
|
|
|
163 |
- **0 or Enter**: Go back to previous level
|
|
|
164 |
- **a**: Add new key (in hash) or element (in array)
|
| 6 |
rodolico |
165 |
- **r**: Rename a key (in hash only)
|
| 4 |
rodolico |
166 |
- **d**: Delete a key (from hash) or element (from array)
|
|
|
167 |
- **q**: Quit and optionally save changes
|
|
|
168 |
|
|
|
169 |
When editing:
|
|
|
170 |
- **Scalars**: Enter new value or press Enter to keep current
|
|
|
171 |
- **Hashes**: Navigate into nested structure
|
|
|
172 |
- **Arrays**: Navigate into nested structure or edit elements
|
|
|
173 |
|
| 2 |
rodolico |
174 |
## Requirements
|
|
|
175 |
|
| 4 |
rodolico |
176 |
### Perl Modules
|
| 2 |
rodolico |
177 |
|
| 4 |
rodolico |
178 |
**Required:**
|
|
|
179 |
- Perl 5.x or higher
|
|
|
180 |
- `File::Slurp` - File reading utilities
|
|
|
181 |
- `Data::Dumper` - Data structure serialization
|
|
|
182 |
- `Getopt::Long` - Command-line option parsing
|
|
|
183 |
|
|
|
184 |
**At least one YAML library:**
|
|
|
185 |
- `YAML::XS` (recommended, fastest)
|
|
|
186 |
- `YAML::Tiny`
|
|
|
187 |
- `YAML`
|
|
|
188 |
|
|
|
189 |
**At least one JSON library:**
|
|
|
190 |
- `JSON::XS` (recommended, fastest)
|
|
|
191 |
- `JSON::PP`
|
|
|
192 |
- `JSON`
|
|
|
193 |
|
|
|
194 |
### Installation
|
|
|
195 |
|
|
|
196 |
**Using CPAN:**
|
|
|
197 |
```bash
|
|
|
198 |
cpan File::Slurp YAML::XS JSON::XS
|
|
|
199 |
```
|
|
|
200 |
|
|
|
201 |
**Using cpanm:**
|
|
|
202 |
```bash
|
|
|
203 |
cpanm File::Slurp YAML::XS JSON::XS
|
|
|
204 |
```
|
|
|
205 |
|
|
|
206 |
**Debian/Ubuntu:**
|
|
|
207 |
```bash
|
|
|
208 |
sudo apt-get install libfile-slurp-perl libyaml-perl libjson-xs-perl
|
|
|
209 |
```
|
|
|
210 |
|
|
|
211 |
**RedHat/CentOS:**
|
|
|
212 |
```bash
|
|
|
213 |
sudo yum install perl-File-Slurp perl-YAML perl-JSON-XS
|
|
|
214 |
```
|
|
|
215 |
|
|
|
216 |
**FreeBSD:**
|
|
|
217 |
```bash
|
|
|
218 |
sudo pkg install p5-File-Slurp p5-YAML p5-JSON-XS
|
|
|
219 |
```
|
|
|
220 |
|
|
|
221 |
### Source Code
|
|
|
222 |
|
|
|
223 |
This script is part of the Subversion repository and may be downloaded, checked out, or exported from:
|
|
|
224 |
|
|
|
225 |
```bash
|
|
|
226 |
# Checkout the entire repository
|
|
|
227 |
svn checkout http://svn.dailydata.net/svn/perlutils/trunk perlutils
|
|
|
228 |
|
|
|
229 |
# Export without version control metadata
|
|
|
230 |
svn export http://svn.dailydata.net/svn/perlutils/trunk perlutils
|
|
|
231 |
|
|
|
232 |
# Download just this script
|
|
|
233 |
svn export http://svn.dailydata.net/svn/perlutils/trunk/perlConfigFileUtility
|
|
|
234 |
```
|
|
|
235 |
|
|
|
236 |
## Use Cases
|
|
|
237 |
|
|
|
238 |
### 1. User-Friendly Configuration Editing
|
|
|
239 |
|
|
|
240 |
Enable non-technical users to modify complex configuration files without risking syntax errors:
|
|
|
241 |
```bash
|
|
|
242 |
perlConfigFileUtility -c /etc/myapp/config.yaml -e
|
|
|
243 |
```
|
|
|
244 |
|
|
|
245 |
### 2. Configuration Version Management
|
|
|
246 |
|
|
|
247 |
When software is updated with new configuration options, merge them into existing configs:
|
|
|
248 |
```bash
|
|
|
249 |
# v2.0 adds new keys to template
|
|
|
250 |
perlConfigFileUtility -t myapp-v2.datastructure -c myapp-v1.conf.yaml -o myapp-v2.conf.yaml
|
|
|
251 |
```
|
|
|
252 |
User customizations are preserved while new defaults are added.
|
|
|
253 |
|
|
|
254 |
### 3. Configuration Format Migration
|
|
|
255 |
|
|
|
256 |
Convert existing configs between formats:
|
|
|
257 |
```bash
|
|
|
258 |
# Organization switches from JSON to YAML
|
|
|
259 |
for file in configs/*.json; do
|
|
|
260 |
perlConfigFileUtility -c "$file" -o "${file%.json}.yaml"
|
|
|
261 |
done
|
|
|
262 |
```
|
|
|
263 |
|
|
|
264 |
### 4. Configuration Deployment
|
|
|
265 |
|
|
|
266 |
Generate initial configuration files from templates:
|
|
|
267 |
```bash
|
|
|
268 |
perlConfigFileUtility -t defaults.pl -o /etc/newapp/config.yaml
|
|
|
269 |
```
|
|
|
270 |
|
| 6 |
rodolico |
271 |
### 5. Configuration Comparison and Auditing
|
|
|
272 |
|
|
|
273 |
Compare config files across environments to identify discrepancies:
|
|
|
274 |
```bash
|
|
|
275 |
# Compare production vs staging configurations
|
|
|
276 |
perlConfigFileUtility -C -c /etc/app/prod.yaml -c /etc/app/staging.yaml
|
|
|
277 |
|
|
|
278 |
# Audit multiple server configs
|
|
|
279 |
perlConfigFileUtility -C -c server1.yaml -c server2.yaml -c server3.yaml -o audit-report.txt
|
|
|
280 |
```
|
|
|
281 |
Identifies missing keys, type mismatches, and value differences for troubleshooting and compliance.
|
|
|
282 |
|
| 4 |
rodolico |
283 |
## Technical Details
|
|
|
284 |
|
|
|
285 |
### Template Files
|
|
|
286 |
|
|
|
287 |
Template files are Perl data structures (hashrefs) that define the configuration schema:
|
|
|
288 |
|
|
|
289 |
```perl
|
|
|
290 |
{
|
|
|
291 |
'setting1' => 'default_value',
|
|
|
292 |
'section' => {
|
|
|
293 |
'nested_setting' => 123,
|
|
|
294 |
'array_setting' => ['item1', 'item2']
|
|
|
295 |
}
|
|
|
296 |
}
|
|
|
297 |
```
|
|
|
298 |
|
|
|
299 |
Inline comments in templates serve as documentation and are preserved when exported to YAML.
|
|
|
300 |
|
|
|
301 |
### Merge Behavior
|
|
|
302 |
|
|
|
303 |
- **Missing keys**: Added from template
|
|
|
304 |
- **Existing keys**: Preserved from config (user values not overwritten)
|
|
|
305 |
- **Nested hashes**: Recursively merged
|
|
|
306 |
- **Arrays**: Extended if template is longer; existing elements preserved
|
|
|
307 |
- **Type mismatches**: Config value takes precedence
|
|
|
308 |
|
|
|
309 |
### Backup Strategy
|
|
|
310 |
|
|
|
311 |
When saving changes, the original file is renamed with `.bak` extension:
|
|
|
312 |
```
|
|
|
313 |
config.yaml → config.yaml.bak (old version)
|
|
|
314 |
config.yaml → (new version)
|
|
|
315 |
```
|
|
|
316 |
|
|
|
317 |
## Troubleshooting
|
|
|
318 |
|
|
|
319 |
**No YAML/JSON library error:**
|
|
|
320 |
Install at least one YAML and one JSON library using your system package manager or CPAN.
|
|
|
321 |
|
|
|
322 |
**Template must be a hashref error:**
|
|
|
323 |
Ensure template file contains a single hashref structure (starts with `{` and ends with `}`).
|
|
|
324 |
|
|
|
325 |
**Config must be a hashref error:**
|
|
|
326 |
The utility only works with hash-based configurations, not arrays or scalars at the root level.
|
|
|
327 |
|
|
|
328 |
**Permission denied errors:**
|
|
|
329 |
Ensure you have write permissions for the output directory when saving files.
|
|
|
330 |
|
| 2 |
rodolico |
331 |
## License
|
|
|
332 |
|
|
|
333 |
Simplified BSD License (FreeBSD License)
|
|
|
334 |
Copyright (c) 2026, Daily Data Inc.
|
|
|
335 |
|
|
|
336 |
## Author
|
|
|
337 |
|
|
|
338 |
R. W. Rodolico <rodo@dailydata.net>
|
|
|
339 |
|
|
|
340 |
## Version
|
|
|
341 |
|
| 6 |
rodolico |
342 |
1.2.0 (January 2026)
|
| 2 |
rodolico |
343 |
|
|
|
344 |
### Version History
|
|
|
345 |
|
| 6 |
rodolico |
346 |
- **1.2.0** (2026-01-18):
|
|
|
347 |
- Added config file comparison mode to identify differences across multiple files
|
|
|
348 |
- Added ability to rename hash keys in interactive edit mode
|
|
|
349 |
- Refactored long functions for improved code readability and maintainability
|
| 2 |
rodolico |
350 |
- **1.1.0** (2026-01-15): Added ability to delete keys from hashes and elements from arrays
|
|
|
351 |
- **1.0** (2026-01-13): Initial release with merge, edit, and format conversion capabilities
|