terradactyl 1.2.1 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/build-status.yml +1 -1
- data/.github/workflows/validate-pullrequest.yml +1 -1
- data/CHANGELOG.md +17 -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/commands.rb +16 -0
- 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
- data/terradactyl.gemspec +1 -1
- metadata +15 -4
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,22 @@
|
|
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
|
+
|
14
|
+
## 1.3.0 (2022-09-21)
|
15
|
+
|
16
|
+
NEW FEATURES:
|
17
|
+
|
18
|
+
* add support for Terraform version '~> 1.3.0`
|
19
|
+
|
3
20
|
## 1.2.1 (2022-06-13)
|
4
21
|
|
5
22
|
BUG FIX:
|
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/commands.rb
CHANGED
@@ -434,6 +434,22 @@ module Terradactyl
|
|
434
434
|
Terraform::Rev1_02::PlanFileParser
|
435
435
|
end
|
436
436
|
end
|
437
|
+
|
438
|
+
module Rev1_03
|
439
|
+
class << self
|
440
|
+
def upgradeable?
|
441
|
+
false
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
include Terraform::Commands
|
446
|
+
|
447
|
+
private
|
448
|
+
|
449
|
+
def parser
|
450
|
+
Terraform::Rev1_03::PlanFileParser
|
451
|
+
end
|
452
|
+
end
|
437
453
|
end
|
438
454
|
# rubocop:enable Metrics/ModuleLength
|
439
455
|
end
|
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
data/terradactyl.gemspec
CHANGED
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
|
|
33
33
|
spec.add_dependency 'deep_merge', '~> 1.2'
|
34
34
|
spec.add_dependency 'bundler', '>= 1.16'
|
35
35
|
spec.add_dependency 'rake', '>= 10.0'
|
36
|
-
spec.add_dependency 'terradactyl-terraform', '>= 1.
|
36
|
+
spec.add_dependency 'terradactyl-terraform', '>= 1.3.0'
|
37
37
|
|
38
38
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
39
39
|
spec.add_development_dependency 'pry', '~> 0.12'
|
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-
|
12
|
+
date: 2022-09-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: thor
|
@@ -101,14 +101,14 @@ dependencies:
|
|
101
101
|
requirements:
|
102
102
|
- - ">="
|
103
103
|
- !ruby/object:Gem::Version
|
104
|
-
version: 1.
|
104
|
+
version: 1.3.0
|
105
105
|
type: :runtime
|
106
106
|
prerelease: false
|
107
107
|
version_requirements: !ruby/object:Gem::Requirement
|
108
108
|
requirements:
|
109
109
|
- - ">="
|
110
110
|
- !ruby/object:Gem::Version
|
111
|
-
version: 1.
|
111
|
+
version: 1.3.0
|
112
112
|
- !ruby/object:Gem::Dependency
|
113
113
|
name: rspec
|
114
114
|
requirement: !ruby/object:Gem::Requirement
|
@@ -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
|