config_o_mat 0.5.7 → 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8350a909966c6de2c8f3ed1de8dbf003c4b26eef0c929c759661dd9088e75d83
4
- data.tar.gz: 9339ccb2ec996a3669d8feba956624931c9cb649c7b8f91c9fb048355fd9d814
3
+ metadata.gz: f230957ea0207b297e312345111dda19349194b5fd4731a4a90eb5ef185dd599
4
+ data.tar.gz: 1dba9c32e8a41d4bdb4f0257c286209a6759ec191d7520959ff1748e40329b8e
5
5
  SHA512:
6
- metadata.gz: 63b01beb38ad4fbbd5e2431bf428eb8e27e0c0a76983f93ca8e655bdec317e167e8a79539368db986cd4ee8a02bb00f06a10c48dc557234ac6a83b1c1c097e60
7
- data.tar.gz: 6377e0004345fa5c55d84e4892f654fbce97d3d734b42da5bdab4848888d432fc572aa0c6cc4e6360d5504f4778797c3ef93d91aae86b0d016e6d13188f5f595
6
+ metadata.gz: ce2b20c6942f83a13d1c672f2b6beb4fe3b45f7a08bff3f3dfbce20c6a1b9f8878cf883739a1b41e0b0e84ade645bafdb30f88e001fd9252348ab3775bedfdc5
7
+ data.tar.gz: f12fee67d7c16366c059816b94ed244ea98a0b018265c2f26dfe680f7ecb8fc57758e0c9d22f8cac7f2b1852ceeb7ead3d68ae27aac3d6734689dcbfc98ea075
@@ -0,0 +1,30 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: ['**']
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ ruby-version: ['2.7', '3.1', '3.2', '3.3', '3.4']
16
+
17
+ env:
18
+ AWS_REGION: us-east-1
19
+
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - name: Set up Ruby ${{ matrix.ruby-version }}
24
+ uses: ruby/setup-ruby@v1
25
+ with:
26
+ ruby-version: ${{ matrix.ruby-version }}
27
+ bundler-cache: true
28
+
29
+ - name: Run tests
30
+ run: bundle exec rake spec
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.7.4
1
+ 3.3.9
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 0.5.8
2
+
3
+ BUG FIXES:
4
+
5
+ * Fixes crash when using S3 fallback under Ruby 3.3+
6
+
1
7
  ## 0.5.7
2
8
 
3
9
  BUG FIXES:
data/CLAUDE.md ADDED
@@ -0,0 +1,215 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Overview
6
+
7
+ ConfigOMat (config_o_mat) is a Ruby gem that manages runtime configuration of systemd services by:
8
+ 1. Reading configuration data from AWS AppConfig and AWS Secrets Manager
9
+ 2. Rendering ERB templates with that data
10
+ 3. Restarting systemd services to apply configuration updates
11
+
12
+ The system polls AWS AppConfig on a configurable interval and applies configuration changes atomically.
13
+
14
+ ## Working with Claude Code
15
+
16
+ ### Git Workflow
17
+
18
+ **Branch Naming**: Use prefixes to categorize work:
19
+ - `feat/` - New features
20
+ - `maint/` - Maintenance tasks (CI, dependencies, tooling)
21
+ - `fix/` - Bug fixes
22
+
23
+ Branch names should be short and succinct (e.g., `maint/ci-setup`, `feat/metrics`, `fix/retry-logic`).
24
+
25
+ **Commit Messages**: When Claude creates commits, include the complete verbatim user prompts that led to the changes. This helps other users understand how to effectively work with Claude by seeing real interaction examples.
26
+
27
+ ### Text Artifacts
28
+
29
+ All text artifacts (planning documents, commit messages, branch names, documentation updates, etc.) should be:
30
+ - **Short and concise** - Get to the point quickly
31
+ - **Non-duplicative** - Don't restate what's visible in nearby context (e.g., commit messages shouldn't repeat the diff)
32
+ - **Focused on "why" over "what"** - The code/diff shows what changed; explain the reasoning
33
+
34
+ ## Development Commands
35
+
36
+ ### Running Tests
37
+ ```bash
38
+ bundle exec rake spec # Run all tests
39
+ bundle exec rspec spec/path/to/spec.rb # Run a single test file
40
+ bundle exec rspec spec/path/to/spec.rb:42 # Run test at specific line
41
+ ```
42
+
43
+ ### Building and Installing
44
+ ```bash
45
+ bundle install # Install dependencies
46
+ bundle exec rake build # Build the gem
47
+ bundle exec rake install # Install the gem locally
48
+ bundle exec rake release # Release new version (bumps version, tags, pushes)
49
+ ```
50
+
51
+ ### Documentation
52
+ ```bash
53
+ bundle exec rake doc # Generate YARD documentation
54
+ ```
55
+
56
+ ### Code Coverage
57
+ Tests use SimpleCov with branch coverage enabled. Coverage reports are generated automatically when running specs.
58
+
59
+ ## Architecture
60
+
61
+ ### State Machine Pattern (LifecycleVM)
62
+
63
+ The codebase uses the `lifecycle_vm` gem to implement behavior as finite state machines. Each major component is a VM (Virtual Machine) that defines states, operations, and transitions:
64
+
65
+ - **Configurator::VM** (`lib/config_o_mat/configurator.rb`) - Main configuration management lifecycle
66
+ - **MetaConfigurator::VM** (`lib/config_o_mat/meta_configurator.rb`) - Generates systemd unit files from meta-config
67
+ - **FlipFlopper::VM** (`lib/config_o_mat/flip_flopper.rb`) - Manages flip-flop service restarts (instantiated units @1 and @2)
68
+ - **SecretsLoader::VM** (`lib/config_o_mat/secrets_loader.rb`) - Loads secrets from AWS Secrets Manager
69
+
70
+ Each VM has:
71
+ - **Memory class** - Holds all state for the VM (immutable state pattern)
72
+ - **Op (Operations)** - Actions that execute and modify state (in `op/` subdirectories)
73
+ - **Cond (Conditions)** - Decision points that determine state transitions (in `cond/` subdirectories)
74
+
75
+ ### Core Workflow (Configurator::VM)
76
+
77
+ 1. **Parse CLI** → Parse command-line arguments
78
+ 2. **Load Meta Config** → Read and merge all `.conf` files from config directory
79
+ 3. **Compile Templates** → Load and compile ERB templates
80
+ 4. **Connect to AWS** → Initialize AWS SDK clients (AppConfig, Secrets Manager, S3)
81
+ 5. **Refresh Profiles** → Poll AWS AppConfig for configuration updates
82
+ 6. **Apply Profiles** → Process updated profiles and load any referenced secrets
83
+ 7. **Generate Templates** → Render ERB templates with profile data
84
+ 8. **Reload Services** → Restart affected systemd services
85
+ 9. **Running** → Sleep until next refresh interval, then loop back to refresh
86
+
87
+ ### Configuration Types (lib/config_o_mat/shared/types.rb)
88
+
89
+ The system defines several key configuration objects:
90
+ - **Profile** - AWS AppConfig profile definition (application, environment, profile name)
91
+ - **Template** - ERB template file mapping (src → dst)
92
+ - **Service** - Systemd service with restart mode (`restart`, `flip_flop`, `restart_all`, `none`)
93
+ - **Secret** - AWS Secrets Manager secret definition with parsing instructions
94
+ - **LoadedAppconfigProfile** - Loaded profile data with parsed content (JSON/YAML/text)
95
+ - **LoadedSecret** - Loaded secret data from AWS Secrets Manager
96
+ - **FacterProfile** - System facts from the Facter gem
97
+
98
+ ### Restart Modes
99
+
100
+ Services can use different restart strategies:
101
+ - **restart** - Standard systemd try-reload-or-restart
102
+ - **flip_flop** - Zero-downtime restart using instantiated units (@1 and @2)
103
+ - **restart_all** - Restart all instances of an instantiated unit template
104
+ - **none** - Only update files, don't restart
105
+
106
+ ### Error Handling and Retry Logic
107
+
108
+ The Configurator VM includes retry logic:
109
+ - On first run, any operation failure causes the VM to fail immediately
110
+ - After first run, failures trigger retry with exponential backoff
111
+ - Configurable via `retry_count` and `retry_wait` in config files
112
+ - Failed profile refreshes can roll back to previous working version
113
+
114
+ ### Secrets Integration
115
+
116
+ Profiles can reference secrets via the `aws:secrets` key in their configuration. The SecretsLoader VM:
117
+ 1. Identifies secrets to load from staged profiles
118
+ 2. Checks an in-memory cache to avoid unnecessary AWS calls
119
+ 3. Loads secrets from AWS Secrets Manager
120
+ 4. Parses secrets according to content_type (JSON/YAML/text)
121
+ 5. Makes secrets available to ERB templates
122
+
123
+ ### Testing Pattern
124
+
125
+ Tests use RSpec with:
126
+ - `spec_helper.rb` configures SimpleCov, shared contexts, and Facter mocking
127
+ - Tests are organized by component: `configurator/`, `flip_flopper/`, `secrets_loader/`, `meta_configurator/`, `shared/`
128
+ - Each Op and Cond has corresponding spec files
129
+ - Shared context `'with a logger'` provides test logger for specs tagged with `logger: true`
130
+ - Facter is mocked by default with fixture data from `spec/fixtures/facter/default`
131
+
132
+ ### Operations (Op) Pattern
133
+
134
+ When adding new operations:
135
+ - Inherit from `LifecycleVM::OpBase`
136
+ - Declare `reads` for memory fields to read
137
+ - Declare `writes` for memory fields to modify
138
+ - Implement `call` method
139
+ - Use `error(field, message)` to record errors
140
+ - Return early if `errors?` is true
141
+
142
+ ### Conditions (Cond) Pattern
143
+
144
+ When adding new conditions:
145
+ - Inherit from `LifecycleVM::CondBase`
146
+ - Declare `reads` for memory fields to read
147
+ - Implement `call` to return the decision value
148
+ - Must return a value that matches a key in the VM's state transition map
149
+
150
+ ## Configuration File Format
151
+
152
+ Meta-configuration files (`.conf` in YAML):
153
+ ```yaml
154
+ log_level: debug|info|notice|warn|error
155
+ log_type: stdout|file
156
+ log_file: path/to/file.log
157
+ refresh_interval: 5 # seconds
158
+ retry_count: 3
159
+ retry_wait: 2 # seconds
160
+ region: us-east-1
161
+ gc_compact: 0 # number of ticks between GC compacts (0 = disabled)
162
+ gc_stat: 0 # number of ticks between GC stat logs (0 = disabled)
163
+ fallback_s3_bucket: bucket-name # Required if any profile uses s3_fallback
164
+
165
+ profiles:
166
+ profile_name:
167
+ application: app-name
168
+ environment: env-name
169
+ profile: profile-name
170
+ s3_fallback: s3-key-prefix # Optional S3 fallback location
171
+
172
+ templates:
173
+ template_name:
174
+ src: source/path.erb
175
+ dst: destination/path
176
+
177
+ services:
178
+ service_name:
179
+ systemd_unit: unit-name
180
+ restart_mode: restart|flip_flop|restart_all|none
181
+ templates:
182
+ - template_name
183
+
184
+ facter: profile_name # Enable Facter profile with given name
185
+ ```
186
+
187
+ Multiple `.conf` files are deep-merged in lexical order.
188
+
189
+ ## File Organization
190
+
191
+ - `bin/` - Executable entry points
192
+ - `lib/config_o_mat/` - Main source code
193
+ - `configurator/` - Main configuration management VM
194
+ - `meta_configurator/` - Systemd config generation VM
195
+ - `flip_flopper/` - Zero-downtime restart VM
196
+ - `secrets_loader/` - AWS Secrets Manager integration VM
197
+ - `shared/` - Shared operations, conditions, and types
198
+ - `spec/` - RSpec tests mirroring lib structure
199
+ - `design/` - Original design documentation
200
+
201
+ ## Dependencies
202
+
203
+ Key external dependencies:
204
+ - `lifecycle_vm` - State machine framework
205
+ - `aws-sdk-appconfig` - AWS AppConfig client
206
+ - `aws-sdk-secretsmanager` - AWS Secrets Manager client
207
+ - `aws-sdk-s3` - S3 client for fallback configurations
208
+ - `ruby-dbus` - systemd communication via D-Bus
209
+ - `sd_notify` - systemd watchdog notifications
210
+ - `facter` - System fact collection
211
+ - `logsformyfamily` - Structured JSON logging
212
+
213
+ ## Ruby Version
214
+
215
+ Requires Ruby >= 2.7.0 (see `.ruby-version` and gemspec for current version).
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- config_o_mat (0.5.7)
4
+ config_o_mat (0.5.8)
5
5
  aws-sdk-appconfig (~> 1.18)
6
6
  aws-sdk-s3 (~> 1.114)
7
7
  aws-sdk-secretsmanager (~> 1.57)
@@ -15,32 +15,34 @@ GEM
15
15
  remote: https://rubygems.org/
16
16
  specs:
17
17
  aws-eventstream (1.4.0)
18
- aws-partitions (1.1134.0)
19
- aws-sdk-appconfig (1.68.0)
20
- aws-sdk-core (~> 3, >= 3.227.0)
18
+ aws-partitions (1.1174.0)
19
+ aws-sdk-appconfig (1.71.0)
20
+ aws-sdk-core (~> 3, >= 3.231.0)
21
21
  aws-sigv4 (~> 1.5)
22
- aws-sdk-core (3.227.0)
22
+ aws-sdk-core (3.233.0)
23
23
  aws-eventstream (~> 1, >= 1.3.0)
24
24
  aws-partitions (~> 1, >= 1.992.0)
25
25
  aws-sigv4 (~> 1.9)
26
26
  base64
27
+ bigdecimal
27
28
  jmespath (~> 1, >= 1.6.1)
28
29
  logger
29
- aws-sdk-kms (1.107.0)
30
- aws-sdk-core (~> 3, >= 3.227.0)
30
+ aws-sdk-kms (1.114.0)
31
+ aws-sdk-core (~> 3, >= 3.231.0)
31
32
  aws-sigv4 (~> 1.5)
32
- aws-sdk-s3 (1.194.0)
33
- aws-sdk-core (~> 3, >= 3.227.0)
33
+ aws-sdk-s3 (1.200.0)
34
+ aws-sdk-core (~> 3, >= 3.231.0)
34
35
  aws-sdk-kms (~> 1)
35
36
  aws-sigv4 (~> 1.5)
36
- aws-sdk-secretsmanager (1.117.0)
37
- aws-sdk-core (~> 3, >= 3.227.0)
37
+ aws-sdk-secretsmanager (1.120.0)
38
+ aws-sdk-core (~> 3, >= 3.231.0)
38
39
  aws-sigv4 (~> 1.5)
39
40
  aws-sigv4 (1.12.1)
40
41
  aws-eventstream (~> 1, >= 1.0.2)
41
42
  base64 (0.3.0)
42
- diff-lcs (1.5.0)
43
- docile (1.4.0)
43
+ bigdecimal (3.3.1)
44
+ diff-lcs (1.6.2)
45
+ docile (1.4.1)
44
46
  facter (4.10.0)
45
47
  hocon (~> 1.3)
46
48
  thor (>= 1.0.1, < 1.3)
@@ -49,20 +51,21 @@ GEM
49
51
  lifecycle_vm (0.1.3)
50
52
  logger (1.7.0)
51
53
  logsformyfamily (0.3.0)
52
- rexml (3.4.1)
53
- rspec (3.12.0)
54
- rspec-core (~> 3.12.0)
55
- rspec-expectations (~> 3.12.0)
56
- rspec-mocks (~> 3.12.0)
57
- rspec-core (3.12.1)
58
- rspec-support (~> 3.12.0)
59
- rspec-expectations (3.12.2)
54
+ rake (13.3.0)
55
+ rexml (3.4.4)
56
+ rspec (3.13.2)
57
+ rspec-core (~> 3.13.0)
58
+ rspec-expectations (~> 3.13.0)
59
+ rspec-mocks (~> 3.13.0)
60
+ rspec-core (3.13.6)
61
+ rspec-support (~> 3.13.0)
62
+ rspec-expectations (3.13.5)
60
63
  diff-lcs (>= 1.2.0, < 2.0)
61
- rspec-support (~> 3.12.0)
62
- rspec-mocks (3.12.3)
64
+ rspec-support (~> 3.13.0)
65
+ rspec-mocks (3.13.6)
63
66
  diff-lcs (>= 1.2.0, < 2.0)
64
- rspec-support (~> 3.12.0)
65
- rspec-support (3.12.0)
67
+ rspec-support (~> 3.13.0)
68
+ rspec-support (3.13.6)
66
69
  ruby-dbus (0.19.0)
67
70
  rexml
68
71
  sd_notify (0.1.1)
@@ -70,7 +73,7 @@ GEM
70
73
  docile (~> 1.1)
71
74
  simplecov-html (~> 0.11)
72
75
  simplecov_json_formatter (~> 0.1)
73
- simplecov-html (0.12.3)
76
+ simplecov-html (0.13.2)
74
77
  simplecov_json_formatter (0.1.4)
75
78
  thor (1.2.2)
76
79
 
@@ -79,8 +82,9 @@ PLATFORMS
79
82
 
80
83
  DEPENDENCIES
81
84
  config_o_mat!
85
+ rake (~> 13.0)
82
86
  rspec (~> 3.10)
83
87
  simplecov (~> 0.22.0)
84
88
 
85
89
  BUNDLED WITH
86
- 2.1.4
90
+ 2.4.22
data/config_o_mat.gemspec CHANGED
@@ -47,6 +47,7 @@ Gem::Specification.new do |spec|
47
47
  spec.add_dependency('sd_notify', '~> 0.1.1')
48
48
  spec.add_dependency('facter', ['~> 4.2', '>= 4.2.8'])
49
49
 
50
+ spec.add_development_dependency('rake', '~> 13.0')
50
51
  spec.add_development_dependency('simplecov', '~> 0.22.0')
51
52
  spec.add_development_dependency('rspec', '~> 3.10')
52
53
  end
@@ -59,7 +59,7 @@ module ConfigOMat
59
59
  def request_from_s3(profile_name, definition, ignore_errors)
60
60
  begin
61
61
  s3_response = s3_client.get_object(bucket: fallback_s3_bucket, key: definition.s3_fallback)
62
- OpenStruct.new(
62
+ ConfigOMat::S3FallbackResponse.new(
63
63
  content: s3_response.body,
64
64
  content_type: s3_response.content_type,
65
65
  configuration_version: s3_response.version_id || s3_response.etag
@@ -57,7 +57,7 @@ module ConfigOMat
57
57
  def request_from_s3(profile_name, definition)
58
58
  begin
59
59
  s3_response = s3_client.get_object(bucket: fallback_s3_bucket, key: definition.s3_fallback)
60
- OpenStruct.new(
60
+ ConfigOMat::S3FallbackResponse.new(
61
61
  content: s3_response.body,
62
62
  content_type: s3_response.content_type,
63
63
  configuration_version: s3_response.version_id || s3_response.etag
@@ -456,6 +456,35 @@ module ConfigOMat
456
456
  end
457
457
  end
458
458
 
459
+ class S3FallbackResponse < ConfigItem
460
+ attr_reader :content, :content_type, :configuration_version
461
+
462
+ def initialize(content:, content_type:, configuration_version:)
463
+ @content = content
464
+ @content_type = content_type
465
+ @configuration_version = configuration_version
466
+ end
467
+
468
+ def validate
469
+ error :content, PRESENCE_ERROR_MSG if @content.nil?
470
+ error :content_type, PRESENCE_ERROR_MSG if @content_type.nil? || @content_type.empty?
471
+ error :configuration_version, PRESENCE_ERROR_MSG if @configuration_version.nil? || @configuration_version.empty?
472
+ end
473
+
474
+ def hash
475
+ @content.hash ^ @content_type.hash ^ @configuration_version.hash
476
+ end
477
+
478
+ def eql?(other)
479
+ return false if !super(other)
480
+ if other.content != content ||
481
+ other.content_type != content_type ||
482
+ other.configuration_version != configuration_version
483
+ return false
484
+ end
485
+ true
486
+ end
487
+ end
459
488
 
460
489
  class LoadedProfile < ConfigItem
461
490
  extend Forwardable
@@ -15,5 +15,5 @@
15
15
  # limitations under the License.
16
16
 
17
17
  module ConfigOMat
18
- VERSION = "0.5.7"
18
+ VERSION = "0.5.8"
19
19
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: config_o_mat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.7
4
+ version: 0.5.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Scarborough
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-07-24 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: aws-sdk-appconfig
@@ -128,6 +127,20 @@ dependencies:
128
127
  - - ">="
129
128
  - !ruby/object:Gem::Version
130
129
  version: 4.2.8
130
+ - !ruby/object:Gem::Dependency
131
+ name: rake
132
+ requirement: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: '13.0'
137
+ type: :development
138
+ prerelease: false
139
+ version_requirements: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: '13.0'
131
144
  - !ruby/object:Gem::Dependency
132
145
  name: simplecov
133
146
  requirement: !ruby/object:Gem::Requirement
@@ -156,7 +169,6 @@ dependencies:
156
169
  - - "~>"
157
170
  - !ruby/object:Gem::Version
158
171
  version: '3.10'
159
- description:
160
172
  email:
161
173
  - alex@teak.io
162
174
  executables:
@@ -165,11 +177,13 @@ executables:
165
177
  extensions: []
166
178
  extra_rdoc_files: []
167
179
  files:
180
+ - ".github/workflows/test.yml"
168
181
  - ".gitignore"
169
182
  - ".rspec"
170
183
  - ".ruby-gemset"
171
184
  - ".ruby-version"
172
185
  - CHANGELOG.md
186
+ - CLAUDE.md
173
187
  - Gemfile
174
188
  - Gemfile.lock
175
189
  - LICENSE
@@ -248,7 +262,6 @@ licenses: []
248
262
  metadata:
249
263
  homepage_uri: https://github.com/GoCarrot/configurator
250
264
  source_code_uri: https://github.com/GoCarrot/configurator
251
- post_install_message:
252
265
  rdoc_options: []
253
266
  require_paths:
254
267
  - lib
@@ -263,8 +276,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
263
276
  - !ruby/object:Gem::Version
264
277
  version: '0'
265
278
  requirements: []
266
- rubygems_version: 3.1.6
267
- signing_key:
279
+ rubygems_version: 3.7.2
268
280
  specification_version: 4
269
281
  summary: ConfigOMat applies AWS AppConfig to Systemd services.
270
282
  test_files: []