simple_infrastructure 0.1.0
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 +7 -0
- data/CHANGELOG.md +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +301 -0
- data/bin/simple_infrastructure +6 -0
- data/lib/generators/simple_infrastructure/change_generator.rb +16 -0
- data/lib/generators/simple_infrastructure/templates/change.rb.tt +21 -0
- data/lib/simple_infrastructure/change.rb +187 -0
- data/lib/simple_infrastructure/cli.rb +306 -0
- data/lib/simple_infrastructure/configuration.rb +36 -0
- data/lib/simple_infrastructure/dry_run_connection.rb +34 -0
- data/lib/simple_infrastructure/dsl.rb +53 -0
- data/lib/simple_infrastructure/file_operations.rb +48 -0
- data/lib/simple_infrastructure/inventory.rb +44 -0
- data/lib/simple_infrastructure/railtie.rb +22 -0
- data/lib/simple_infrastructure/runner.rb +162 -0
- data/lib/simple_infrastructure/server.rb +39 -0
- data/lib/simple_infrastructure/ssh_connection.rb +91 -0
- data/lib/simple_infrastructure/toml_operations.rb +154 -0
- data/lib/simple_infrastructure/version.rb +5 -0
- data/lib/simple_infrastructure/yaml_operations.rb +79 -0
- data/lib/simple_infrastructure.rb +57 -0
- data/lib/tasks/simple_infrastructure.rake +79 -0
- metadata +96 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: bb8f4aec9834f672b6d225487853b1e935d642b54fc1e6c68f2cf9de94261174
|
|
4
|
+
data.tar.gz: 0a954b0c4d971c07e47840a99aacbbdcc9022a02d2338eb40c722a475b99866c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 494eaf07fe0d6eefb7c6e2f1d8a2750b3ac7c22ef936dcaab9c91722534e9163f0333efac7654c5bb017afd792df19131f372322d0a19002a6789bc591512051
|
|
7
|
+
data.tar.gz: 23df4ef7dc6bc28d64a271d7ff83dd20965cd4ee6bfec7ad458360fd0f1fbe999fdae677a012fd3e903232280171b9cd46ace2ffd916d191944bcbcad9b16f23
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0] - 2025-03-03
|
|
4
|
+
|
|
5
|
+
- Initial extraction from FounderCatalyst Rails app
|
|
6
|
+
- Migration-like DSL for server provisioning via SSH
|
|
7
|
+
- Change types: `run`, `file`, `yaml`, `toml`, `upload`
|
|
8
|
+
- Server inventory with YAML/ERB support
|
|
9
|
+
- CLI with status, dry-run, and change generation
|
|
10
|
+
- Rails integration via Railtie (auto-configures project_root and logger)
|
|
11
|
+
- Rake tasks under `infrastructure:` namespace
|
|
12
|
+
- Rails generator for new change files
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 FounderCatalyst
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# Simple Infrastructure
|
|
2
|
+
|
|
3
|
+
A migration-like DSL for server provisioning via SSH. Provides a change-based approach to server configuration, similar to how Rails database migrations work but for infrastructure.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Idempotency**: Changes can be run multiple times safely
|
|
8
|
+
- **Auditability**: All changes are tracked in version control
|
|
9
|
+
- **Consistency**: All servers in an environment receive the same configuration
|
|
10
|
+
- **Incremental updates**: Only pending changes are applied
|
|
11
|
+
- **Change tracking**: State stored on each server, automatically re-applied on rebuild
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Add to your Gemfile:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem "simple_infrastructure"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then run:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
bundle install
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
For Rails apps, the gem auto-configures via Railtie (sets `project_root` to `Rails.root` and `logger` to `Rails.logger`).
|
|
28
|
+
|
|
29
|
+
For standalone use:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
require "simple_infrastructure"
|
|
33
|
+
|
|
34
|
+
SimpleInfrastructure.configure do |config|
|
|
35
|
+
config.project_root = "/path/to/project"
|
|
36
|
+
end
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Directory Structure
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
config/infrastructure/
|
|
43
|
+
├── inventory.yml # Server inventory
|
|
44
|
+
└── changes/ # Change files
|
|
45
|
+
├── 20250121000100_install_essentials.rb
|
|
46
|
+
├── 20250121000200_configure_storage.rb
|
|
47
|
+
└── ...
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Server Inventory
|
|
51
|
+
|
|
52
|
+
Servers are defined in `config/infrastructure/inventory.yml`, grouped by environment:
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
defaults:
|
|
56
|
+
user: civo
|
|
57
|
+
|
|
58
|
+
servers:
|
|
59
|
+
production:
|
|
60
|
+
- hostname: web1.production.example.com
|
|
61
|
+
- hostname: web2.production.example.com
|
|
62
|
+
- hostname: db.production.example.com
|
|
63
|
+
|
|
64
|
+
staging:
|
|
65
|
+
- hostname: app1.staging.example.com
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The `defaults` section provides default values for all servers. Each server entry can override these defaults.
|
|
69
|
+
|
|
70
|
+
## CLI Usage
|
|
71
|
+
|
|
72
|
+
### Check Status
|
|
73
|
+
|
|
74
|
+
View change status for all servers:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
bin/infrastructure status
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Output shows which servers have pending changes:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
Production -------------------------------------------------------------
|
|
84
|
+
✗ web1.production.example.com (6 pending)
|
|
85
|
+
✔︎ web2.production.example.com (up to date)
|
|
86
|
+
|
|
87
|
+
Staging ----------------------------------------------------------------
|
|
88
|
+
✗ app1.staging.example.com (2 pending)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Use `-v` or `--verbose` to see the list of pending changes:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
bin/infrastructure status -v
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Dry Run
|
|
98
|
+
|
|
99
|
+
Preview what changes would be made without executing them:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
bin/infrastructure --dry-run staging
|
|
103
|
+
bin/infrastructure --dry-run production
|
|
104
|
+
bin/infrastructure --dry-run web1.production.example.com
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Run Changes
|
|
108
|
+
|
|
109
|
+
Apply pending changes:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# All servers in an environment
|
|
113
|
+
bin/infrastructure staging
|
|
114
|
+
bin/infrastructure production
|
|
115
|
+
|
|
116
|
+
# Specific server
|
|
117
|
+
bin/infrastructure web1.production.example.com
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Generate New Change
|
|
121
|
+
|
|
122
|
+
Create a new change file:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
bin/infrastructure new setup_redis
|
|
126
|
+
|
|
127
|
+
# Or using the Rails generator:
|
|
128
|
+
rails generate simple_infrastructure:change setup_redis
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
This creates a timestamped file like `config/infrastructure/changes/20250121143000_setup_redis.rb`.
|
|
132
|
+
|
|
133
|
+
## Writing Changes
|
|
134
|
+
|
|
135
|
+
### Basic Structure
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
# Target specific servers (omit target line for all servers)
|
|
139
|
+
target env: :production # All production servers
|
|
140
|
+
target env: :staging # All staging servers
|
|
141
|
+
target hostname: "db.production.example.com" # Specific server
|
|
142
|
+
|
|
143
|
+
# Run shell commands
|
|
144
|
+
run "apt update", sudo: true
|
|
145
|
+
run "systemctl restart nginx", sudo: true
|
|
146
|
+
|
|
147
|
+
# Manage file contents
|
|
148
|
+
file "/etc/ssh/sshd_config", sudo: true do
|
|
149
|
+
contains "PermitRootLogin no" # Ensure line exists
|
|
150
|
+
remove "PermitRootLogin yes" # Remove line if present
|
|
151
|
+
on_change { run "systemctl restart sshd", sudo: true }
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Manage YAML files
|
|
155
|
+
yaml "/etc/config.yml", sudo: true do
|
|
156
|
+
set "server.port", 8080
|
|
157
|
+
remove "deprecated.setting"
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Manage TOML files
|
|
161
|
+
toml "/etc/config.toml", sudo: true do
|
|
162
|
+
set "database.host", "localhost"
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Upload local files
|
|
166
|
+
upload "config/backup/script.sh", "/root/bin/script.sh", sudo: true, mode: "700"
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Targeting Servers
|
|
170
|
+
|
|
171
|
+
By default, changes apply to all servers. Use `target` to restrict to specific servers:
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
# All servers in production
|
|
175
|
+
target env: :production
|
|
176
|
+
|
|
177
|
+
# Specific hostname
|
|
178
|
+
target hostname: "db.production.example.com"
|
|
179
|
+
|
|
180
|
+
# Hostname pattern (regex)
|
|
181
|
+
target hostname: /^web\d+\.production\./
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### DSL Reference
|
|
185
|
+
|
|
186
|
+
#### `run(command, sudo: false)`
|
|
187
|
+
|
|
188
|
+
Execute a shell command on the remote server.
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
run "apt update", sudo: true
|
|
192
|
+
run "docker compose up -d"
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
#### `file(path, sudo: false, &block)`
|
|
196
|
+
|
|
197
|
+
Manage plain text file contents.
|
|
198
|
+
|
|
199
|
+
```ruby
|
|
200
|
+
file "/etc/fstab", sudo: true do
|
|
201
|
+
contains "/swapfile none swap sw 0 0" # Add if missing
|
|
202
|
+
remove "/old/swap none swap sw 0 0" # Remove if present
|
|
203
|
+
on_change { run "mount -a", sudo: true } # Run only if file changed
|
|
204
|
+
end
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### `yaml(path, sudo: false, &block)`
|
|
208
|
+
|
|
209
|
+
Manage YAML configuration files.
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
yaml "/etc/app/config.yml", sudo: true do
|
|
213
|
+
set "database.host", "localhost"
|
|
214
|
+
set "database.port", 3306
|
|
215
|
+
remove "deprecated_key"
|
|
216
|
+
end
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
#### `toml(path, sudo: false, &block)`
|
|
220
|
+
|
|
221
|
+
Manage TOML configuration files.
|
|
222
|
+
|
|
223
|
+
```ruby
|
|
224
|
+
toml "/etc/app/config.toml", sudo: true do
|
|
225
|
+
set "server.bind", "0.0.0.0"
|
|
226
|
+
set "server.port", 8080
|
|
227
|
+
end
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
#### `upload(local_path, remote_path, sudo: false, mode: nil)`
|
|
231
|
+
|
|
232
|
+
Upload a local file to the remote server.
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
upload "bin/backup", "/root/bin/backup", sudo: true, mode: "700"
|
|
236
|
+
upload "config/nginx.conf", "/etc/nginx/nginx.conf", sudo: true
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### The `on_change` Callback
|
|
240
|
+
|
|
241
|
+
The `file` DSL supports an `on_change` callback that only executes when the file was actually modified:
|
|
242
|
+
|
|
243
|
+
```ruby
|
|
244
|
+
file "/etc/ssh/sshd_config", sudo: true do
|
|
245
|
+
remove "PermitRootLogin yes"
|
|
246
|
+
contains "PermitRootLogin no"
|
|
247
|
+
on_change { run "systemctl restart sshd", sudo: true }
|
|
248
|
+
end
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
This is useful for restarting services only when their configuration changes, avoiding unnecessary service restarts.
|
|
252
|
+
|
|
253
|
+
## Rake Tasks
|
|
254
|
+
|
|
255
|
+
Alternative to the CLI:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
# Show status
|
|
259
|
+
rake infrastructure:status
|
|
260
|
+
|
|
261
|
+
# Run changes
|
|
262
|
+
rake infrastructure:run[production]
|
|
263
|
+
rake infrastructure:run[staging]
|
|
264
|
+
|
|
265
|
+
# Dry run
|
|
266
|
+
rake infrastructure:dry_run[production]
|
|
267
|
+
|
|
268
|
+
# Generate change
|
|
269
|
+
rake infrastructure:generate[setup_redis]
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Change Tracking
|
|
273
|
+
|
|
274
|
+
The system tracks which changes have been applied by storing log files on each server in `~/.infrastructure/`:
|
|
275
|
+
|
|
276
|
+
```
|
|
277
|
+
~/.infrastructure/
|
|
278
|
+
├── 20250121000100_install_essentials.log
|
|
279
|
+
├── 20250121000200_configure_storage.log
|
|
280
|
+
├── 20250121000300_configure_swap.log
|
|
281
|
+
└── ...
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Each log file contains the timestamp when the change was applied and any output from the commands.
|
|
285
|
+
|
|
286
|
+
This approach means:
|
|
287
|
+
- State lives on the server itself, not in your local repository
|
|
288
|
+
- If a server is deleted and recreated, changes will be re-applied automatically
|
|
289
|
+
- Log files provide debugging information if something goes wrong
|
|
290
|
+
|
|
291
|
+
## Best Practices
|
|
292
|
+
|
|
293
|
+
1. **Make changes idempotent**: Use `contains` instead of blindly appending, check if files exist before creating them
|
|
294
|
+
2. **Use `on_change` for service restarts**: Avoid unnecessary restarts by only restarting when config actually changes
|
|
295
|
+
3. **Test with dry-run first**: Always preview changes before applying to production
|
|
296
|
+
4. **Target narrowly when appropriate**: Use specific hostnames for server-specific configuration (like database backups)
|
|
297
|
+
5. **Keep changes small and focused**: One concern per change makes troubleshooting easier
|
|
298
|
+
|
|
299
|
+
## License
|
|
300
|
+
|
|
301
|
+
MIT License. See [LICENSE.txt](LICENSE.txt).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module SimpleInfrastructure
|
|
6
|
+
module Generators
|
|
7
|
+
class ChangeGenerator < Rails::Generators::NamedBase
|
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
|
9
|
+
|
|
10
|
+
def create_change_file
|
|
11
|
+
timestamp = Time.now.strftime("%Y%m%d%H%M%S")
|
|
12
|
+
template "change.rb.tt", "config/infrastructure/changes/#{timestamp}_#{file_name}.rb"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
target env: :production
|
|
2
|
+
|
|
3
|
+
# run "command", sudo: true
|
|
4
|
+
|
|
5
|
+
# file "/path/to/file", sudo: true do
|
|
6
|
+
# contains "line that must exist"
|
|
7
|
+
# remove "line that must not exist"
|
|
8
|
+
# on_change { run "systemctl restart service", sudo: true }
|
|
9
|
+
# end
|
|
10
|
+
|
|
11
|
+
# yaml "/path/to/config.yml", sudo: true do
|
|
12
|
+
# set "key.path", "value"
|
|
13
|
+
# remove "old.key"
|
|
14
|
+
# end
|
|
15
|
+
|
|
16
|
+
# toml "/path/to/config.toml", sudo: true do
|
|
17
|
+
# set "key.path", "value"
|
|
18
|
+
# remove "old.key"
|
|
19
|
+
# end
|
|
20
|
+
|
|
21
|
+
# upload "config/backup/script.sh", "/root/bin/script.sh", sudo: true, mode: "700"
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleInfrastructure
|
|
4
|
+
class Change
|
|
5
|
+
attr_reader :name, :target_criteria, :steps
|
|
6
|
+
|
|
7
|
+
def initialize(name)
|
|
8
|
+
@name = name
|
|
9
|
+
@target_criteria = {}
|
|
10
|
+
@steps = []
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def target(criteria)
|
|
14
|
+
@target_criteria = criteria
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def targets?(server)
|
|
18
|
+
server.matches?(target_criteria)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def add_step(step)
|
|
22
|
+
@steps << step
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def <=>(other)
|
|
26
|
+
name <=> other.name
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.load_all
|
|
30
|
+
changes_dir = SimpleInfrastructure.configuration.changes_dir
|
|
31
|
+
return [] unless Dir.exist?(changes_dir)
|
|
32
|
+
|
|
33
|
+
Dir.glob(File.join(changes_dir, "*.rb")).map do |file|
|
|
34
|
+
Dsl.load_file(file)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Step types
|
|
40
|
+
class RunStep
|
|
41
|
+
attr_reader :command, :sudo
|
|
42
|
+
|
|
43
|
+
def initialize(command, sudo: false)
|
|
44
|
+
@command = command
|
|
45
|
+
@sudo = sudo
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def execute(connection, dry_run: false)
|
|
49
|
+
full_command = sudo ? "sudo #{command}" : command
|
|
50
|
+
SimpleInfrastructure.logger.debug " RUN: #{full_command}"
|
|
51
|
+
return true if dry_run
|
|
52
|
+
|
|
53
|
+
result = connection.exec(full_command)
|
|
54
|
+
unless result[:success]
|
|
55
|
+
raise "Command failed with exit code #{result[:exit_code]}: #{result[:stderr]}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
true
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class FileStep
|
|
63
|
+
attr_reader :path, :operations, :sudo, :on_change_callback
|
|
64
|
+
|
|
65
|
+
def initialize(path, sudo: false)
|
|
66
|
+
@path = path
|
|
67
|
+
@operations = []
|
|
68
|
+
@sudo = sudo
|
|
69
|
+
@on_change_callback = nil
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def contains(line)
|
|
73
|
+
@operations << [:contains, line]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def remove(line)
|
|
77
|
+
@operations << [:remove, line]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def on_change(&block)
|
|
81
|
+
@on_change_callback = block
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def execute(connection, dry_run: false)
|
|
85
|
+
SimpleInfrastructure.logger.debug " FILE: #{path}#{' (sudo)' if sudo}"
|
|
86
|
+
modified = FileOperations.new(connection, path, sudo: sudo, dry_run: dry_run).apply(@operations)
|
|
87
|
+
|
|
88
|
+
if modified && @on_change_callback && !dry_run
|
|
89
|
+
SimpleInfrastructure.logger.debug " ON_CHANGE: Executing callback..."
|
|
90
|
+
callback_context = OnChangeContext.new(connection)
|
|
91
|
+
callback_context.instance_eval(&@on_change_callback)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
true
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
class OnChangeContext
|
|
99
|
+
def initialize(connection)
|
|
100
|
+
@connection = connection
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def run(command, sudo: false)
|
|
104
|
+
full_command = sudo ? "sudo #{command}" : command
|
|
105
|
+
SimpleInfrastructure.logger.debug " RUN: #{full_command}"
|
|
106
|
+
result = @connection.exec(full_command)
|
|
107
|
+
unless result[:success]
|
|
108
|
+
raise "Command failed with exit code #{result[:exit_code]}: #{result[:stderr]}"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
true
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
class YamlStep
|
|
116
|
+
attr_reader :path, :operations, :sudo
|
|
117
|
+
|
|
118
|
+
def initialize(path, sudo: false)
|
|
119
|
+
@path = path
|
|
120
|
+
@operations = []
|
|
121
|
+
@sudo = sudo
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def set(key, value)
|
|
125
|
+
@operations << [:set, key, value]
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def remove(key)
|
|
129
|
+
@operations << [:remove, key]
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def execute(connection, dry_run: false)
|
|
133
|
+
SimpleInfrastructure.logger.debug " YAML: #{path}#{' (sudo)' if sudo}"
|
|
134
|
+
YamlOperations.new(connection, path, sudo: sudo, dry_run: dry_run).apply(@operations)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
class TomlStep
|
|
139
|
+
attr_reader :path, :operations, :sudo
|
|
140
|
+
|
|
141
|
+
def initialize(path, sudo: false)
|
|
142
|
+
@path = path
|
|
143
|
+
@operations = []
|
|
144
|
+
@sudo = sudo
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def set(key, value)
|
|
148
|
+
@operations << [:set, key, value]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def remove(key)
|
|
152
|
+
@operations << [:remove, key]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def execute(connection, dry_run: false)
|
|
156
|
+
SimpleInfrastructure.logger.debug " TOML: #{path}#{' (sudo)' if sudo}"
|
|
157
|
+
TomlOperations.new(connection, path, sudo: sudo, dry_run: dry_run).apply(@operations)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
class UploadStep
|
|
162
|
+
attr_reader :local_path, :remote_path, :sudo, :mode
|
|
163
|
+
|
|
164
|
+
def initialize(local_path, remote_path, sudo: false, mode: nil)
|
|
165
|
+
@local_path = local_path
|
|
166
|
+
@remote_path = remote_path
|
|
167
|
+
@sudo = sudo
|
|
168
|
+
@mode = mode
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def execute(connection, dry_run: false)
|
|
172
|
+
SimpleInfrastructure.logger.debug " UPLOAD: #{local_path} -> #{remote_path}#{' (sudo)' if sudo}"
|
|
173
|
+
return true if dry_run
|
|
174
|
+
|
|
175
|
+
content = File.read(File.join(SimpleInfrastructure.project_root, local_path))
|
|
176
|
+
connection.write_file(remote_path, content, sudo: sudo)
|
|
177
|
+
|
|
178
|
+
if mode
|
|
179
|
+
chmod_cmd = "chmod #{mode} #{remote_path}"
|
|
180
|
+
chmod_cmd = "sudo #{chmod_cmd}" if sudo
|
|
181
|
+
connection.exec(chmod_cmd)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
true
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|