terradactyl 1.3.0 → 1.4.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 +4 -4
- data/.github/workflows/build-status.yml +1 -1
- data/.github/workflows/validate-pullrequest.yml +1 -1
- data/CHANGELOG.md +11 -0
- data/README.md +23 -7
- data/examples/multi-stack-subdirectories/preprod-stacks/stack1/example.tf +1 -0
- data/examples/multi-stack-subdirectories/preprod-stacks/stack1/versions.tf +8 -0
- data/examples/multi-stack-subdirectories/preprod-stacks/stack2/example.tf +1 -0
- data/examples/multi-stack-subdirectories/preprod-stacks/stack2/versions.tf +8 -0
- data/examples/multi-stack-subdirectories/preprod-stacks/terradactyl.yaml +7 -0
- data/examples/multi-stack-subdirectories/terradactyl.yaml +4 -0
- data/examples/multi-stack-subdirectories/test-stacks/stack1/example.tf +1 -0
- data/examples/multi-stack-subdirectories/test-stacks/stack1/versions.tf +8 -0
- data/examples/multi-stack-subdirectories/test-stacks/stack2/example.tf +1 -0
- data/examples/multi-stack-subdirectories/test-stacks/stack2/versions.tf +8 -0
- data/examples/multi-stack-subdirectories/test-stacks/terradactyl.yaml +7 -0
- data/lib/terradactyl/cli.rb +133 -106
- data/lib/terradactyl/config.rb +26 -7
- data/lib/terradactyl/filters.rb +4 -8
- data/lib/terradactyl/stack.rb +12 -4
- data/lib/terradactyl/stacks.rb +5 -3
- data/lib/terradactyl/version.rb +1 -1
- metadata +13 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 572d97af0265aeffbfc6144ffa01767365af13a5c927c2faeb075ab1a9c40abb
|
4
|
+
data.tar.gz: 9551f11afa4b79a621457e7a3b14891667deb90012164590141a958b345ef730
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5a915631b4d0abaab3a850192e9e2201eccbe78d8abb87906458077f0565d14d86ad4b7af234e73ae0ec409a9f89051013c2d37879084ce11dac3abf585f0aba
|
7
|
+
data.tar.gz: fae2314dcc7db1189d60216d1ff3590570cc18714cf023442de44e3746e255d32f4c3d6f69cfc1b74302136c1588d9b9eace300842da141f7544296d01271b9a
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 1.4.0 (2022-09-21)
|
4
|
+
|
5
|
+
NEW FEATURES:
|
6
|
+
|
7
|
+
* add support for multiple stack subdirectories
|
8
|
+
* add optional BASE_FOLDER input for most Terradactyl commands
|
9
|
+
|
10
|
+
BUG FIXES:
|
11
|
+
|
12
|
+
* fix StacksApplyFilterPrePlanned filtering behaviour
|
13
|
+
|
3
14
|
## 1.3.0 (2022-09-21)
|
4
15
|
|
5
16
|
NEW FEATURES:
|
data/README.md
CHANGED
@@ -19,7 +19,7 @@ Terradactyl simplifies managing large heterogeneous Terraform monorepos by intro
|
|
19
19
|
|
20
20
|
Requires Ruby 2.5 or greater.
|
21
21
|
|
22
|
-
NOTE: Terraform sub-command operations are only supported between stable versions `>= 0.11.x` to `~>
|
22
|
+
NOTE: Terraform sub-command operations are only supported between stable versions `>= 0.11.x` to `~> 1.3.x`.
|
23
23
|
|
24
24
|
## Installation
|
25
25
|
|
@@ -43,8 +43,8 @@ And then execute:
|
|
43
43
|
|
44
44
|
Terradactyl repos rely on two simple organizational conventions:
|
45
45
|
|
46
|
-
* a single project-level `
|
47
|
-
*
|
46
|
+
* a single project-level `terradactyl.yaml`
|
47
|
+
* subdirectories for your stacks
|
48
48
|
|
49
49
|
```sh
|
50
50
|
.
|
@@ -140,6 +140,20 @@ NOTE: `*.tfstate*` files are not cleaned up by default for obvious reasons, so c
|
|
140
140
|
|
141
141
|
See the [Configuration](#configuration) section for more info on how to control which files get removed during a `clean <stack>` or `clean-all` operation.
|
142
142
|
|
143
|
+
#### quickplan a single stack in a non-default subdirectory
|
144
|
+
|
145
|
+
If you have a setup with multiple stack subdirectories, you have one stack subdirectory that's referenced by default in the root-level terradactyl.yaml.
|
146
|
+
|
147
|
+
$ cd examples/multi-stack-subdirectories
|
148
|
+
|
149
|
+
Notice that `preprod-stacks` is the base_folder defined in the root terradactyl.yaml. By default, Terradactyl commands will run in this subdirectory.
|
150
|
+
|
151
|
+
All subdirectories define their own terradactyl.yaml with customized `base_folder` and `misc.base_folder_name` settings. To run commands on stacks in non-default subdirectories from root, you must pass in the optional BASE_FOLDER input to your Terradactyl command.
|
152
|
+
|
153
|
+
$ terradactyl quickplan stack1 # this command will run quickplan on preprod-stacks/stack1
|
154
|
+
$ terradactyl quickplan stack1 test-stacks # this command will run quickplan on test-stacks/stack1
|
155
|
+
|
156
|
+
|
143
157
|
## Operation
|
144
158
|
|
145
159
|
NOTE: `terradactyl` (symlinked as `td`) ONLY operates in the root of your monorepo. In order to execute any subcommands, your working directory must contain your project-level configuration file, otherwise you will receive this:
|
@@ -176,7 +190,7 @@ Terradactyl add some unique utility commands that permit you to more readily man
|
|
176
190
|
Installs supporting components, namely Terraform itself...
|
177
191
|
|
178
192
|
# Install the latest terraform binary
|
179
|
-
|
193
|
+
terradactyl install terraform
|
180
194
|
|
181
195
|
# Install pessimistic version
|
182
196
|
terradactyl install terraform --version="~> 0.13.0"
|
@@ -201,14 +215,14 @@ Terradactyl provides a few useful meta-commands that can help you avoid repetiti
|
|
201
215
|
|
202
216
|
Clean, initialize and plan a single stack in one operation.
|
203
217
|
|
204
|
-
|
218
|
+
terradactyl quickplan <stack>
|
205
219
|
|
206
220
|
#### smartapply/smartrefresh
|
207
221
|
|
208
222
|
Apply or Refresh _ANY_ stack containing a plan file.
|
209
223
|
|
210
|
-
|
211
|
-
|
224
|
+
terradactyl smartapply <stack>
|
225
|
+
terradactyl smartrefresh <stack>
|
212
226
|
|
213
227
|
### Getting Help
|
214
228
|
|
@@ -225,6 +239,7 @@ For help on any individual sub-command do:
|
|
225
239
|
As previously mentioned, configuration is hierarchical. This means you may specify:
|
226
240
|
|
227
241
|
* one project-level configuration for ALL stacks
|
242
|
+
* an overriding subdirectory configuration for all stacks within the subdirectory
|
228
243
|
* an overriding stack-level configuration for each independent stack
|
229
244
|
|
230
245
|
See [examples](examples) for different setups.
|
@@ -265,6 +280,7 @@ terradactyl: <Object, Terradactyl config>
|
|
265
280
|
environment: <Object, shell environment variables>
|
266
281
|
TF_PLUGIN_CACHE_DIR: <String, path to common Terraform plugin directory, default=$HOME/.terraform.d/plugins>
|
267
282
|
misc: <Object, misc Terradactyl settings>
|
283
|
+
base_folder_name: <String, name of the stack folder. Required for multiple stack subdirectories, default=nil>
|
268
284
|
utf8: <Bool, use utf8 in stdout, default=true>
|
269
285
|
disable_color: <Bool, disable color in stdout, default=false>
|
270
286
|
cleanup: <Object, Terradactyl cleanup settings>
|
@@ -0,0 +1 @@
|
|
1
|
+
resource "null_resource" "baz" {}
|
@@ -0,0 +1 @@
|
|
1
|
+
resource "null_resource" "baz" {}
|
@@ -0,0 +1 @@
|
|
1
|
+
resource "null_resource" "bar" {}
|
@@ -0,0 +1 @@
|
|
1
|
+
resource "null_resource" "foo" {}
|
data/lib/terradactyl/cli.rb
CHANGED
@@ -68,9 +68,15 @@ module Terradactyl
|
|
68
68
|
Terradactyl::Terraform::VersionManager.latest
|
69
69
|
end
|
70
70
|
|
71
|
-
|
72
|
-
|
73
|
-
|
71
|
+
# get name of base folder for printing logs
|
72
|
+
def base_folder_name(base_folder)
|
73
|
+
base_folder || config.misc.base_folder_name || config.base_folder
|
74
|
+
end
|
75
|
+
|
76
|
+
# rubocop:disable Metrics/AbcSize
|
77
|
+
def upgrade_stack(name, base_override = nil)
|
78
|
+
@stack ||= Stack.new(name, base_override)
|
79
|
+
print_warning "Upgrading in #{config.base_folder}: #{@stack.name}"
|
74
80
|
if @stack.upgrade.zero?
|
75
81
|
print_ok "Upgraded: #{@stack.name}"
|
76
82
|
else
|
@@ -95,10 +101,10 @@ module Terradactyl
|
|
95
101
|
puts config.to_h.to_yaml
|
96
102
|
end
|
97
103
|
|
98
|
-
desc 'stacks', 'List the stacks'
|
99
|
-
def stacks
|
104
|
+
desc 'stacks [BASE_FOLDER]', 'List the stacks, with optional base folder override'
|
105
|
+
def stacks(base_override = nil)
|
100
106
|
print_ok 'Stacks:'
|
101
|
-
Stacks.load.each do |name|
|
107
|
+
Stacks.load(base_override: base_override).each do |name|
|
102
108
|
print_dot name.to_s
|
103
109
|
end
|
104
110
|
end
|
@@ -114,49 +120,59 @@ module Terradactyl
|
|
114
120
|
# * Some are useful only in pipelines. These are hidden.
|
115
121
|
#################################################################
|
116
122
|
|
117
|
-
desc 'planpr
|
118
|
-
|
119
|
-
|
120
|
-
|
123
|
+
desc 'planpr [BASE_FOLDER]',
|
124
|
+
'Plan stacks against origin/HEAD (used for PRs), with optional base folder override',
|
125
|
+
hide: true
|
126
|
+
def planpr(base_override = nil)
|
127
|
+
print_header "SmartPlanning PR in #{base_folder_name(base_override)} ..."
|
128
|
+
stacks = Stacks.load(
|
129
|
+
filter: StacksPlanFilterGitDiffOriginBranch.new,
|
130
|
+
base_override: base_override
|
131
|
+
)
|
121
132
|
validate_planpr(stacks).each do |name|
|
122
|
-
clean(name)
|
123
|
-
init(name)
|
124
|
-
plan(name)
|
133
|
+
clean(name, base_override)
|
134
|
+
init(name, base_override)
|
135
|
+
plan(name, base_override)
|
125
136
|
@stack = nil
|
126
137
|
end
|
127
138
|
end
|
128
139
|
|
129
|
-
desc 'smartplan',
|
130
|
-
|
131
|
-
|
132
|
-
|
140
|
+
desc 'smartplan [BASE_FOLDER]',
|
141
|
+
'Plan any stacks that differ from Git HEAD, with optional base folder override'
|
142
|
+
def smartplan(base_override = nil)
|
143
|
+
print_header "SmartPlanning Stacks in #{base_folder_name(base_override)} ..."
|
144
|
+
stacks = Stacks.load(filter: StacksPlanFilterGitDiffHead.new, base_override: base_override)
|
133
145
|
validate_smartplan(stacks).each do |name|
|
134
|
-
clean(name)
|
135
|
-
init(name)
|
136
|
-
plan(name)
|
146
|
+
clean(name, base_override)
|
147
|
+
init(name, base_override)
|
148
|
+
plan(name, base_override)
|
137
149
|
@stack = nil
|
138
150
|
end
|
139
151
|
end
|
140
152
|
|
141
|
-
desc 'smartapply',
|
142
|
-
|
143
|
-
|
144
|
-
|
153
|
+
desc 'smartapply',
|
154
|
+
'Apply any stacks that contain plan files, with optional base folder override',
|
155
|
+
hide: true
|
156
|
+
def smartapply(base_override = nil)
|
157
|
+
print_header "SmartApplying Stacks in #{base_folder_name(base_override)} ..."
|
158
|
+
stacks = Stacks.load(filter: StacksApplyFilterPrePlanned.new, base_override: base_override)
|
145
159
|
print_warning 'No stacks contain plan files ...' unless stacks.any?
|
146
160
|
stacks.each do |name|
|
147
|
-
apply(name)
|
161
|
+
apply(name, base_override)
|
148
162
|
@stack = nil
|
149
163
|
end
|
150
164
|
print_message "Total Stacks Modified: #{stacks.size}"
|
151
165
|
end
|
152
166
|
|
153
|
-
desc 'smartrefresh
|
154
|
-
|
155
|
-
|
156
|
-
|
167
|
+
desc 'smartrefresh [BASE_FOLDER]',
|
168
|
+
'Refresh any stacks that contain plan files, with optional base folder override',
|
169
|
+
hide: true
|
170
|
+
def smartrefresh(base_override = nil)
|
171
|
+
print_header "SmartRefreshing Stacks in #{base_folder_name(base_override)} ..."
|
172
|
+
stacks = Stacks.load(filter: StacksApplyFilterPrePlanned.new, base_override: base_override)
|
157
173
|
print_warning 'No stacks contain plan files ...' unless stacks.any?
|
158
174
|
stacks.each do |name|
|
159
|
-
refresh(name)
|
175
|
+
refresh(name, base_override)
|
160
176
|
@stack = nil
|
161
177
|
end
|
162
178
|
print_message "Total Stacks Refreshed: #{stacks.size}"
|
@@ -168,56 +184,57 @@ module Terradactyl
|
|
168
184
|
# the `quickplan` task is an exception to this rule.
|
169
185
|
#################################################################
|
170
186
|
|
171
|
-
desc 'upgrade NAME
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
187
|
+
desc 'upgrade NAME [BASE_FOLDER]',
|
188
|
+
'Cleans, inits, upgrades and fmts a given stack, by name & optional base folder override'
|
189
|
+
def upgrade(name, base_override = nil)
|
190
|
+
clean(name, base_override)
|
191
|
+
init(name, base_override, backend: false)
|
192
|
+
upgrade_stack(name, base_override)
|
193
|
+
fmt(name, base_override)
|
177
194
|
end
|
178
195
|
|
179
|
-
desc 'quickplan NAME
|
180
|
-
|
196
|
+
desc 'quickplan NAME [BASE_FOLDER]',
|
197
|
+
'Clean, init and plan a stack, by name & optional base folder override'
|
198
|
+
def quickplan(name, base_override = nil)
|
181
199
|
print_header "Quick planning #{name} ..."
|
182
|
-
clean(name)
|
183
|
-
init(name)
|
184
|
-
plan(name)
|
200
|
+
clean(name, base_override)
|
201
|
+
init(name, base_override)
|
202
|
+
plan(name, base_override)
|
185
203
|
end
|
186
204
|
|
187
|
-
desc 'clean-all', 'Clean all stacks'
|
188
|
-
def clean_all
|
189
|
-
print_header
|
190
|
-
Stacks.load.each do |name|
|
191
|
-
clean(name)
|
205
|
+
desc 'clean-all [BASE_FOLDER]', 'Clean all stacks, by optional base folder override'
|
206
|
+
def clean_all(base_override = nil)
|
207
|
+
print_header "Cleaning ALL Stacks in #{base_folder_name(base_override)} ..."
|
208
|
+
Stacks.load(base_override: base_override).each do |name|
|
209
|
+
clean(name, base_override)
|
192
210
|
@stack = nil
|
193
211
|
end
|
194
212
|
end
|
195
213
|
|
196
|
-
desc 'plan-all', 'Plan all stacks'
|
197
|
-
def plan_all
|
198
|
-
print_header
|
199
|
-
Stacks.load.each do |name|
|
214
|
+
desc 'plan-all [BASE_FOLDER]', 'Plan all stacks, by optional base folder override'
|
215
|
+
def plan_all(base_override = nil)
|
216
|
+
print_header "Planning ALL Stacks in #{base_folder_name(base_override)} ..."
|
217
|
+
Stacks.load(base_override: base_override).each do |name|
|
200
218
|
catch(:error) do
|
201
|
-
clean(name)
|
202
|
-
init(name)
|
203
|
-
plan(name)
|
219
|
+
clean(name, base_override)
|
220
|
+
init(name, base_override)
|
221
|
+
plan(name, base_override)
|
204
222
|
end
|
205
223
|
@stack = nil
|
206
224
|
end
|
207
225
|
end
|
208
226
|
|
209
|
-
desc 'audit-all', 'Audit all stacks'
|
227
|
+
desc 'audit-all [BASE_FOLDER]', 'Audit all stacks, by optional base folder override'
|
210
228
|
options report: :optional
|
211
229
|
method_option :report, type: :boolean
|
212
|
-
|
213
|
-
def audit_all
|
230
|
+
def audit_all(base_override = nil)
|
214
231
|
report = { start: Time.now.to_json }
|
215
|
-
print_header
|
216
|
-
Stacks.load.each do |name|
|
232
|
+
print_header "Auditing ALL Stacks in #{base_folder_name(base_override)} ..."
|
233
|
+
Stacks.load(base_override: base_override).each do |name|
|
217
234
|
catch(:error) do
|
218
|
-
clean(name)
|
219
|
-
init(name)
|
220
|
-
audit(name)
|
235
|
+
clean(name, base_override)
|
236
|
+
init(name, base_override)
|
237
|
+
audit(name, base_override)
|
221
238
|
end
|
222
239
|
@stack = nil
|
223
240
|
end
|
@@ -229,14 +246,14 @@ module Terradactyl
|
|
229
246
|
end
|
230
247
|
# rubocop:enable Metrics/AbcSize
|
231
248
|
|
232
|
-
desc 'validate-all', 'Validate all stacks'
|
233
|
-
def validate_all
|
234
|
-
print_header
|
235
|
-
Stacks.load.each do |name|
|
249
|
+
desc 'validate-all [BASE_FOLDER]', 'Validate all stacks, by optional base folder override'
|
250
|
+
def validate_all(base_override = nil)
|
251
|
+
print_header "Validating ALL Stacks in #{base_folder_name(base_override)} ..."
|
252
|
+
Stacks.load(base_override: base_override).each do |name|
|
236
253
|
catch(:error) do
|
237
|
-
clean(name)
|
238
|
-
init(name)
|
239
|
-
validate(name)
|
254
|
+
clean(name, base_override)
|
255
|
+
init(name, base_override)
|
256
|
+
validate(name, base_override)
|
240
257
|
end
|
241
258
|
@stack = nil
|
242
259
|
end
|
@@ -247,9 +264,10 @@ module Terradactyl
|
|
247
264
|
# * These tasks are used regularly against stacks, by name.
|
248
265
|
#################################################################
|
249
266
|
|
250
|
-
desc 'lint NAME
|
251
|
-
|
252
|
-
|
267
|
+
desc 'lint NAME [BASE_FOLDER]',
|
268
|
+
'Lint an individual stack, by name & optional base folder override'
|
269
|
+
def lint(name, base_override = nil)
|
270
|
+
@stack ||= Stack.new(name, base_override)
|
253
271
|
print_ok "Linting: #{@stack.name}"
|
254
272
|
if @stack.lint.zero?
|
255
273
|
print_ok "Formatting OK: #{@stack.name}"
|
@@ -259,9 +277,10 @@ module Terradactyl
|
|
259
277
|
end
|
260
278
|
end
|
261
279
|
|
262
|
-
desc 'fmt NAME
|
263
|
-
|
264
|
-
|
280
|
+
desc 'fmt NAME [BASE_FOLDER]',
|
281
|
+
'Format an individual stack, by name & optional base folder override'
|
282
|
+
def fmt(name, base_override = nil)
|
283
|
+
@stack ||= Stack.new(name, base_override)
|
265
284
|
print_warning "Formatting: #{@stack.name}"
|
266
285
|
if @stack.fmt.zero?
|
267
286
|
print_ok "Formatted: #{@stack.name}"
|
@@ -271,12 +290,14 @@ module Terradactyl
|
|
271
290
|
end
|
272
291
|
end
|
273
292
|
|
274
|
-
desc 'init NAME
|
275
|
-
|
276
|
-
|
293
|
+
desc 'init NAME [BASE_FOLDER]',
|
294
|
+
'Init an individual stack, by name & optional base folder override'
|
295
|
+
# rubocop:disable Metrics/AbcSize
|
296
|
+
def init(name, base_override = nil, backend: true)
|
297
|
+
@stack ||= Stack.new(name, base_override)
|
277
298
|
@stack.config.terraform.init.backend = backend
|
278
299
|
|
279
|
-
print_ok "Initializing: #{@stack.name}"
|
300
|
+
print_ok "Initializing in #{base_folder_name(base_override)}: #{@stack.name}"
|
280
301
|
if @stack.init.zero?
|
281
302
|
print_ok "Initialized: #{@stack.name}"
|
282
303
|
else
|
@@ -286,11 +307,11 @@ module Terradactyl
|
|
286
307
|
end
|
287
308
|
end
|
288
309
|
|
289
|
-
desc 'plan NAME
|
290
|
-
|
291
|
-
def plan(name)
|
292
|
-
@stack ||= Stack.new(name)
|
293
|
-
print_ok "Planning: #{@stack.name}"
|
310
|
+
desc 'plan NAME [BASE_FOLDER]',
|
311
|
+
'Plan an individual stack, by name & optional base folder override'
|
312
|
+
def plan(name, base_override = nil)
|
313
|
+
@stack ||= Stack.new(name, base_override)
|
314
|
+
print_ok "Planning in #{base_folder_name(base_override)}: #{@stack.name}"
|
294
315
|
case @stack.plan
|
295
316
|
when 0
|
296
317
|
print_ok "No changes: #{@stack.name}"
|
@@ -309,19 +330,21 @@ module Terradactyl
|
|
309
330
|
end
|
310
331
|
# rubocop:enable Metrics/AbcSize
|
311
332
|
|
312
|
-
desc 'audit NAME
|
313
|
-
|
314
|
-
|
333
|
+
desc 'audit NAME [BASE_FOLDER]',
|
334
|
+
'Audit an individual stack, by name & optional base folder override'
|
335
|
+
def audit(name, base_override = nil)
|
336
|
+
plan(name, base_override)
|
315
337
|
if (@stack = Stacks.dirty?(name))
|
316
338
|
Stacks.error!(@stack)
|
317
339
|
print_crit "Dirty stack: #{@stack.name}"
|
318
340
|
end
|
319
341
|
end
|
320
342
|
|
321
|
-
desc 'validate NAME
|
322
|
-
|
323
|
-
|
324
|
-
|
343
|
+
desc 'validate NAME [BASE_FOLDER]',
|
344
|
+
'Validate an individual stack, by name & optional base folder override'
|
345
|
+
def validate(name, base_override = nil)
|
346
|
+
@stack ||= Stack.new(name, base_override)
|
347
|
+
print_ok "Validating in #{base_folder_name(base_override)}: #{@stack.name}"
|
325
348
|
if @stack.validate.zero?
|
326
349
|
print_ok "Validated: #{@stack.name}"
|
327
350
|
else
|
@@ -331,18 +354,20 @@ module Terradactyl
|
|
331
354
|
end
|
332
355
|
end
|
333
356
|
|
334
|
-
desc 'clean NAME
|
335
|
-
|
336
|
-
|
337
|
-
|
357
|
+
desc 'clean NAME [BASE_FOLDER]',
|
358
|
+
'Clean an individual stack, by name & optional base folder override'
|
359
|
+
def clean(name, base_override = nil)
|
360
|
+
@stack ||= Stack.new(name, base_override)
|
361
|
+
print_warning "Cleaning in #{config.base_folder}: #{@stack.name}"
|
338
362
|
@stack.clean
|
339
363
|
print_ok "Cleaned: #{@stack.name}"
|
340
364
|
end
|
341
365
|
|
342
|
-
desc 'apply NAME
|
343
|
-
|
344
|
-
|
345
|
-
|
366
|
+
desc 'apply NAME [BASE_FOLDER]',
|
367
|
+
'Apply an individual stack, by name & optional base folder override'
|
368
|
+
def apply(name, base_override = nil)
|
369
|
+
@stack ||= Stack.new(name, base_override)
|
370
|
+
print_warning "Applying in #{base_folder_name(base_override)}: #{@stack.name}"
|
346
371
|
if @stack.apply.zero?
|
347
372
|
print_ok "Applied: #{@stack.name}"
|
348
373
|
else
|
@@ -351,10 +376,11 @@ module Terradactyl
|
|
351
376
|
end
|
352
377
|
end
|
353
378
|
|
354
|
-
desc 'refresh NAME
|
355
|
-
|
356
|
-
|
357
|
-
|
379
|
+
desc 'refresh NAME [BASE_FOLDER]',
|
380
|
+
'Refresh state on an individual stack, by name & optional base folder override'
|
381
|
+
def refresh(name, base_override = nil)
|
382
|
+
@stack ||= Stack.new(name, base_override)
|
383
|
+
print_crit "Refreshing in #{base_folder_name(base_override)}: #{@stack.name}"
|
358
384
|
if @stack.refresh.zero?
|
359
385
|
print_warning "Refreshed: #{@stack.name}"
|
360
386
|
else
|
@@ -363,10 +389,11 @@ module Terradactyl
|
|
363
389
|
end
|
364
390
|
end
|
365
391
|
|
366
|
-
desc 'destroy NAME
|
367
|
-
|
368
|
-
|
369
|
-
|
392
|
+
desc 'destroy NAME [BASE_FOLDER]',
|
393
|
+
'Destroy an individual stack, by name & optional base folder override'
|
394
|
+
def destroy(name, base_override = nil)
|
395
|
+
@stack ||= Stack.new(name, base_override)
|
396
|
+
print_crit "Destroying in #{base_folder_name(base_override)}: #{@stack.name}"
|
370
397
|
if @stack.destroy.zero?
|
371
398
|
print_warning "Destroyed: #{@stack.name}"
|
372
399
|
else
|
data/lib/terradactyl/config.rb
CHANGED
@@ -61,15 +61,16 @@ module Terradactyl
|
|
61
61
|
|
62
62
|
private
|
63
63
|
|
64
|
-
def load_config
|
64
|
+
def load_config(defaults_override: nil, overlay_override: nil)
|
65
65
|
@config = [
|
66
|
-
@defaults,
|
67
|
-
@overlay
|
66
|
+
defaults_override || @defaults,
|
67
|
+
overlay_override || @overlay
|
68
68
|
].inject({}) do |memo, obj|
|
69
69
|
memo.deep_merge!(obj, overwrite_arrays: true)
|
70
70
|
Marshal.load(Marshal.dump(memo))
|
71
71
|
end
|
72
72
|
@terradactyl = structify(@config).terradactyl
|
73
|
+
|
73
74
|
configure_colorization
|
74
75
|
@terradactyl
|
75
76
|
end
|
@@ -120,8 +121,10 @@ module Terradactyl
|
|
120
121
|
|
121
122
|
private_class_method :new
|
122
123
|
|
123
|
-
def load_overlay(
|
124
|
-
|
124
|
+
def load_overlay(overload)
|
125
|
+
config_file_path = overload ? "./#{overload}/#{config_file}" : config_file
|
126
|
+
|
127
|
+
YAML.load_file(config_file_path)
|
125
128
|
rescue Errno::ENOENT => e
|
126
129
|
abort "FATAL: Could not load project file: `#{config_file}`, #{e.message}"
|
127
130
|
end
|
@@ -129,6 +132,21 @@ module Terradactyl
|
|
129
132
|
def config_file
|
130
133
|
@config_file = CONFIG_PROJECT_FILE
|
131
134
|
end
|
135
|
+
|
136
|
+
def merge_overlay(overlay_path)
|
137
|
+
config_file_path = overlay_path ? "./#{overlay_path}/#{config_file}" : config_file
|
138
|
+
|
139
|
+
config_to_merge = YAML.load_file(config_file_path)
|
140
|
+
|
141
|
+
# set base_folder name if it's '.'
|
142
|
+
if config_to_merge['terradactyl']['base_folder'] == '.'
|
143
|
+
config_to_merge['terradactyl']['base_folder'] = overlay_path
|
144
|
+
end
|
145
|
+
|
146
|
+
load_config(overlay_override: config_to_merge)
|
147
|
+
rescue Errno::ENOENT => e
|
148
|
+
abort "FATAL: Could not load project file: `#{config_file}`, #{e.message}"
|
149
|
+
end
|
132
150
|
end
|
133
151
|
|
134
152
|
class ConfigStack < ConfigApplication
|
@@ -140,14 +158,15 @@ module Terradactyl
|
|
140
158
|
|
141
159
|
attr_reader :stack_name, :stack_path, :base_folder
|
142
160
|
|
143
|
-
def initialize(stack_name)
|
161
|
+
def initialize(stack_name, base_override = nil)
|
144
162
|
@stack_name = stack_name
|
145
163
|
@project_config = ConfigProject.instance
|
146
|
-
@base_folder = @project_config.base_folder
|
164
|
+
@base_folder = base_override || @project_config.base_folder
|
147
165
|
@stack_path = "#{@base_folder}/#{@stack_name}"
|
148
166
|
@config_file = "#{@stack_path}/#{ConfigProject::CONFIG_PROJECT_FILE}"
|
149
167
|
@defaults = load_defaults(@project_config.to_h)
|
150
168
|
@overlay = load_overlay(@config_file)
|
169
|
+
|
151
170
|
load_config
|
152
171
|
end
|
153
172
|
|
data/lib/terradactyl/filters.rb
CHANGED
@@ -16,15 +16,11 @@ module Terradactyl
|
|
16
16
|
`git ls-files .`
|
17
17
|
end
|
18
18
|
|
19
|
-
def base_dir
|
20
|
-
config.base_folder
|
21
|
-
end
|
22
|
-
|
23
19
|
def stack_name(path)
|
24
20
|
path.split('/')[1]
|
25
21
|
end
|
26
22
|
|
27
|
-
def sift(stacks)
|
23
|
+
def sift(stacks, _base_dir)
|
28
24
|
stacks
|
29
25
|
end
|
30
26
|
end
|
@@ -42,7 +38,7 @@ module Terradactyl
|
|
42
38
|
`git --no-pager diff --name-only HEAD .`
|
43
39
|
end
|
44
40
|
|
45
|
-
def sift(stacks)
|
41
|
+
def sift(stacks, base_dir)
|
46
42
|
modified = git_cmd.split.each_with_object([]) do |path, memo|
|
47
43
|
memo << stack_name(path) if path =~ /#{base_dir}/
|
48
44
|
end
|
@@ -90,8 +86,8 @@ module Terradactyl
|
|
90
86
|
end
|
91
87
|
|
92
88
|
class StacksApplyFilterPrePlanned < StacksApplyFilterDefault
|
93
|
-
def sift(stacks)
|
94
|
-
targets = Dir.glob(
|
89
|
+
def sift(stacks, base_dir)
|
90
|
+
targets = Dir.glob("#{base_dir}/**/*.tfout").each_with_object([]) do |path, memo|
|
95
91
|
memo << path.split('/')[1]
|
96
92
|
end
|
97
93
|
stacks & targets
|
data/lib/terradactyl/stack.rb
CHANGED
@@ -10,10 +10,18 @@ module Terradactyl
|
|
10
10
|
new(stack_name)
|
11
11
|
end
|
12
12
|
|
13
|
-
def initialize(stack_name)
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
def initialize(stack_name, base_override = nil)
|
14
|
+
if base_override
|
15
|
+
# merge the terradactyl.yaml inside the provided base folder
|
16
|
+
config.merge_overlay(base_override)
|
17
|
+
else
|
18
|
+
config.reload
|
19
|
+
end
|
20
|
+
|
21
|
+
@stack_name = validate_stack_name(stack_name)
|
22
|
+
@base_override = base_override
|
23
|
+
@stack_config = ConfigStack.new(@stack_name, @base_override)
|
24
|
+
@tf_version = tf_version
|
17
25
|
Commands.extend_by_revision(@tf_version, self)
|
18
26
|
print_message "Terraform version: #{@tf_version}"
|
19
27
|
inject_env_vars
|
data/lib/terradactyl/stacks.rb
CHANGED
@@ -50,10 +50,12 @@ module Terradactyl
|
|
50
50
|
|
51
51
|
attr_reader :filter
|
52
52
|
|
53
|
-
def initialize(filter: StacksPlanFilterDefault.new)
|
53
|
+
def initialize(filter: StacksPlanFilterDefault.new, base_override: nil)
|
54
|
+
base_folder = base_override || config.base_folder
|
55
|
+
|
54
56
|
@filter = filter
|
55
|
-
@base_dir = "#{Rake.original_dir}/#{
|
56
|
-
@stacks = @filter.sift(stacks_all)
|
57
|
+
@base_dir = "#{Rake.original_dir}/#{base_folder}"
|
58
|
+
@stacks = @filter.sift(stacks_all, base_folder)
|
57
59
|
end
|
58
60
|
|
59
61
|
def list
|
data/lib/terradactyl/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: terradactyl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Warsing
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2022-09-
|
12
|
+
date: 2022-09-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: thor
|
@@ -185,6 +185,17 @@ files:
|
|
185
185
|
- LICENSE.txt
|
186
186
|
- README.md
|
187
187
|
- Rakefile
|
188
|
+
- examples/multi-stack-subdirectories/preprod-stacks/stack1/example.tf
|
189
|
+
- examples/multi-stack-subdirectories/preprod-stacks/stack1/versions.tf
|
190
|
+
- examples/multi-stack-subdirectories/preprod-stacks/stack2/example.tf
|
191
|
+
- examples/multi-stack-subdirectories/preprod-stacks/stack2/versions.tf
|
192
|
+
- examples/multi-stack-subdirectories/preprod-stacks/terradactyl.yaml
|
193
|
+
- examples/multi-stack-subdirectories/terradactyl.yaml
|
194
|
+
- examples/multi-stack-subdirectories/test-stacks/stack1/example.tf
|
195
|
+
- examples/multi-stack-subdirectories/test-stacks/stack1/versions.tf
|
196
|
+
- examples/multi-stack-subdirectories/test-stacks/stack2/example.tf
|
197
|
+
- examples/multi-stack-subdirectories/test-stacks/stack2/versions.tf
|
198
|
+
- examples/multi-stack-subdirectories/test-stacks/terradactyl.yaml
|
188
199
|
- examples/multi-tf-version/stacks/tfv11/example.tf
|
189
200
|
- examples/multi-tf-version/stacks/tfv11/terradactyl.yaml
|
190
201
|
- examples/multi-tf-version/stacks/tfv12/example.tf
|