belt 0.3.23 → 0.3.25
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/lib/belt/cli/dns_command.rb +24 -0
- data/lib/belt/cli/setup_command.rb +72 -17
- data/lib/belt/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5f40dfd0e0c761eeecce4896ee9c8f55ac9a97e98dd734d3c12e713a110d65a1
|
|
4
|
+
data.tar.gz: 88ef3eea4944d9edac97929f83e892fb4e204fcab15c7cdbace48356e4cb0cab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c84e237feafd376b1064d624d0d6bf89bd2409363eb660e2f3c03c1d35a5023b98fcee81b1d826dc8ebcb7e5a0a3fe18c1635006ec771be7d514c5d6e051a546
|
|
7
|
+
data.tar.gz: 9d342d91da2029541c4f238e36f055bcbf4118c6d8087c00be5defa5b6e0d9b66e8c5f24b73e69e52a983acc7363f020ed6b6b538499e2bbe4c19e903da9ac72
|
data/lib/belt/cli/dns_command.rb
CHANGED
|
@@ -5,6 +5,7 @@ require 'erb'
|
|
|
5
5
|
require 'json'
|
|
6
6
|
require_relative 'app_detection'
|
|
7
7
|
require_relative 'environment_config'
|
|
8
|
+
require_relative 'setup_command'
|
|
8
9
|
|
|
9
10
|
module Belt
|
|
10
11
|
module CLI
|
|
@@ -100,6 +101,9 @@ module Belt
|
|
|
100
101
|
# Resolve state bucket using the specified profile (or current credentials)
|
|
101
102
|
@state_bucket = resolve_state_bucket_for_profile(@aws_profile)
|
|
102
103
|
|
|
104
|
+
# Ensure the state bucket exists (convention over configuration)
|
|
105
|
+
ensure_state_bucket_exists(@state_bucket)
|
|
106
|
+
|
|
103
107
|
puts 'Creating dns infrastructure...' unless @quiet
|
|
104
108
|
if @aws_profile && !@quiet
|
|
105
109
|
puts " Using AWS profile: #{@aws_profile}"
|
|
@@ -346,6 +350,26 @@ module Belt
|
|
|
346
350
|
|
|
347
351
|
"belt-terraform-state-#{data['Account']}"
|
|
348
352
|
end
|
|
353
|
+
|
|
354
|
+
# Ensure the state bucket exists, creating it if necessary.
|
|
355
|
+
# Uses belt setup state --aws-profile for convention over configuration.
|
|
356
|
+
def ensure_state_bucket_exists(bucket)
|
|
357
|
+
return if bucket == 'belt-terraform-state' # Placeholder means no credentials
|
|
358
|
+
return if bucket_exists?(bucket)
|
|
359
|
+
|
|
360
|
+
puts "State bucket #{bucket} not found. Creating it..." unless @quiet
|
|
361
|
+
args = []
|
|
362
|
+
args += ['--aws-profile', @aws_profile] if @aws_profile
|
|
363
|
+
SetupCommand.new(args, quiet: @quiet, aws_profile: @aws_profile).run_state_setup
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def bucket_exists?(bucket)
|
|
367
|
+
require 'open3'
|
|
368
|
+
cmd = ['aws', 's3api', 'head-bucket', '--bucket', bucket]
|
|
369
|
+
cmd += ['--profile', @aws_profile] if @aws_profile
|
|
370
|
+
_, status = Open3.capture2e(*cmd)
|
|
371
|
+
status.success?
|
|
372
|
+
end
|
|
349
373
|
end
|
|
350
374
|
end
|
|
351
375
|
end
|
|
@@ -29,26 +29,48 @@ module Belt
|
|
|
29
29
|
when 'frontend'
|
|
30
30
|
Belt::CLI::FrontendSetupCommand.run(args)
|
|
31
31
|
else
|
|
32
|
-
|
|
33
|
-
puts "\nSubcommands:"
|
|
34
|
-
puts ' state Set up S3 bucket for Terraform state'
|
|
35
|
-
puts ' tables Generate DynamoDB table definitions from contracts.rb'
|
|
36
|
-
puts ' frontend Generate S3 + CloudFront infrastructure for frontend hosting'
|
|
32
|
+
print_usage
|
|
37
33
|
exit 1
|
|
38
34
|
end
|
|
39
35
|
end
|
|
40
36
|
|
|
41
|
-
def
|
|
37
|
+
def self.print_usage
|
|
38
|
+
puts <<~USAGE
|
|
39
|
+
Usage: belt setup <state|tables|frontend> [options]
|
|
40
|
+
|
|
41
|
+
Subcommands:
|
|
42
|
+
state Set up S3 bucket for Terraform state
|
|
43
|
+
tables Generate DynamoDB table definitions from contracts.rb
|
|
44
|
+
frontend Generate S3 + CloudFront infrastructure for frontend hosting
|
|
45
|
+
|
|
46
|
+
Options for state:
|
|
47
|
+
--aws-profile NAME Use a specific AWS profile (for multi-account setups)
|
|
48
|
+
--bucket NAME Use a custom bucket name instead of belt-terraform-state-<account>
|
|
49
|
+
--select Interactively select from existing buckets
|
|
50
|
+
|
|
51
|
+
Examples:
|
|
52
|
+
belt setup state # Create/verify bucket using current credentials
|
|
53
|
+
belt setup state --aws-profile fpshared # Create bucket in a specific AWS account
|
|
54
|
+
belt setup state --select # Choose from existing buckets
|
|
55
|
+
belt setup state --bucket my-bucket # Use a custom bucket name
|
|
56
|
+
|
|
57
|
+
The state bucket stores Terraform state for all Belt applications in an AWS account.
|
|
58
|
+
By default, Belt uses `belt-terraform-state-<account_id>` as the bucket name.
|
|
59
|
+
USAGE
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def initialize(args = [], quiet: false, aws_profile: nil)
|
|
42
63
|
@app_name = detect_app_name
|
|
43
64
|
@env_name = nil
|
|
44
65
|
@custom_bucket = nil
|
|
45
66
|
@select_mode = false
|
|
46
67
|
@quiet = quiet
|
|
68
|
+
@aws_profile = aws_profile
|
|
47
69
|
|
|
48
70
|
parse_args(args)
|
|
49
71
|
|
|
50
72
|
@region = detect_region
|
|
51
|
-
@bucket_name =
|
|
73
|
+
@bucket_name = nil # Resolved after credentials check
|
|
52
74
|
end
|
|
53
75
|
|
|
54
76
|
def run_state_setup
|
|
@@ -62,14 +84,21 @@ module Belt
|
|
|
62
84
|
exit 1
|
|
63
85
|
end
|
|
64
86
|
|
|
87
|
+
# Show which profile we're using
|
|
88
|
+
puts "Using AWS profile: #{@aws_profile}" if @aws_profile && !@quiet
|
|
89
|
+
|
|
65
90
|
# Resolve final bucket name now that we have credentials
|
|
66
|
-
@bucket_name =
|
|
91
|
+
@bucket_name = @custom_bucket || resolve_bucket_name
|
|
67
92
|
|
|
68
93
|
@bucket_name = interactive_bucket_selection if @select_mode
|
|
69
94
|
setup_or_verify_bucket
|
|
70
95
|
apply_lifecycle(@bucket_name)
|
|
71
96
|
say ' ensure lifecycle rules (90-day noncurrent expiration)'
|
|
72
|
-
|
|
97
|
+
|
|
98
|
+
# Only update backend configs when we know the target scope.
|
|
99
|
+
# If --aws-profile is set but no env_name, we're creating a bucket for a specific
|
|
100
|
+
# account (e.g., DNS infrastructure) — don't touch other environments' backends.
|
|
101
|
+
update_backend_config unless @aws_profile && @env_name.nil?
|
|
73
102
|
print_success_message
|
|
74
103
|
end
|
|
75
104
|
|
|
@@ -140,10 +169,14 @@ module Belt
|
|
|
140
169
|
when '--bucket'
|
|
141
170
|
@custom_bucket = args.shift
|
|
142
171
|
abort '✗ --bucket requires a value' unless @custom_bucket
|
|
172
|
+
when '--aws-profile'
|
|
173
|
+
@aws_profile = args.shift
|
|
174
|
+
abort '✗ --aws-profile requires a value' unless @aws_profile
|
|
143
175
|
when '--select'
|
|
144
176
|
@select_mode = true
|
|
145
177
|
when '--help', '-h'
|
|
146
|
-
self.class.
|
|
178
|
+
self.class.print_usage
|
|
179
|
+
exit 0
|
|
147
180
|
else
|
|
148
181
|
@env_name = arg unless arg.start_with?('-')
|
|
149
182
|
end
|
|
@@ -194,7 +227,9 @@ module Belt
|
|
|
194
227
|
end
|
|
195
228
|
|
|
196
229
|
def list_buckets
|
|
197
|
-
|
|
230
|
+
cmd = ['aws', 's3api', 'list-buckets', '--query', 'Buckets[].Name', '--output', 'json']
|
|
231
|
+
cmd += ['--profile', @aws_profile] if @aws_profile
|
|
232
|
+
output = safe_capture(*cmd)
|
|
198
233
|
return [] unless output
|
|
199
234
|
|
|
200
235
|
JSON.parse(output)
|
|
@@ -205,7 +240,9 @@ module Belt
|
|
|
205
240
|
# --- AWS operations ---
|
|
206
241
|
|
|
207
242
|
def aws_configured?
|
|
208
|
-
|
|
243
|
+
cmd = %w[aws sts get-caller-identity]
|
|
244
|
+
cmd += ['--profile', @aws_profile] if @aws_profile
|
|
245
|
+
output, status = Open3.capture2e(*cmd)
|
|
209
246
|
if status.success?
|
|
210
247
|
data = begin
|
|
211
248
|
JSON.parse(output)
|
|
@@ -223,7 +260,9 @@ module Belt
|
|
|
223
260
|
attr_reader :aws_account_id
|
|
224
261
|
|
|
225
262
|
def bucket_exists?(bucket)
|
|
226
|
-
|
|
263
|
+
cmd = ['aws', 's3api', 'head-bucket', '--bucket', bucket]
|
|
264
|
+
cmd += ['--profile', @aws_profile] if @aws_profile
|
|
265
|
+
output, status = Open3.capture2e(*cmd)
|
|
227
266
|
return true if status.success?
|
|
228
267
|
|
|
229
268
|
# 403 = bucket exists but owned by someone else; 404 = doesn't exist
|
|
@@ -239,6 +278,7 @@ module Belt
|
|
|
239
278
|
def create_bucket(bucket)
|
|
240
279
|
args = ['aws', 's3api', 'create-bucket', '--bucket', bucket, '--region', @region]
|
|
241
280
|
args.push('--create-bucket-configuration', "LocationConstraint=#{@region}") unless @region == 'us-east-1'
|
|
281
|
+
args += ['--profile', @aws_profile] if @aws_profile
|
|
242
282
|
run!(*args)
|
|
243
283
|
end
|
|
244
284
|
|
|
@@ -252,11 +292,14 @@ module Belt
|
|
|
252
292
|
AbortIncompleteMultipartUpload: { DaysAfterInitiation: 7 }
|
|
253
293
|
}]
|
|
254
294
|
}
|
|
255
|
-
|
|
256
|
-
|
|
295
|
+
cmd = ['aws', 's3api', 'put-bucket-lifecycle-configuration', '--bucket', bucket,
|
|
296
|
+
'--lifecycle-configuration', JSON.generate(lifecycle)]
|
|
297
|
+
cmd += ['--profile', @aws_profile] if @aws_profile
|
|
298
|
+
run!(*cmd)
|
|
257
299
|
end
|
|
258
300
|
|
|
259
301
|
def run!(*args)
|
|
302
|
+
args = inject_profile(args)
|
|
260
303
|
output, status = Open3.capture2e(*args)
|
|
261
304
|
return if status.success?
|
|
262
305
|
|
|
@@ -265,11 +308,23 @@ module Belt
|
|
|
265
308
|
exit 1
|
|
266
309
|
end
|
|
267
310
|
|
|
268
|
-
def safe_capture(*)
|
|
269
|
-
|
|
311
|
+
def safe_capture(*args)
|
|
312
|
+
args = inject_profile(args)
|
|
313
|
+
output, status = Open3.capture2(*args, err: File::NULL)
|
|
270
314
|
status.success? ? output : nil
|
|
271
315
|
end
|
|
272
316
|
|
|
317
|
+
# Inject --profile flag for AWS CLI commands if @aws_profile is set
|
|
318
|
+
def inject_profile(args)
|
|
319
|
+
return args unless @aws_profile
|
|
320
|
+
return args unless args.first == 'aws'
|
|
321
|
+
|
|
322
|
+
# Already has a profile flag
|
|
323
|
+
return args if args.include?('--profile')
|
|
324
|
+
|
|
325
|
+
args + ['--profile', @aws_profile]
|
|
326
|
+
end
|
|
327
|
+
|
|
273
328
|
def say(message)
|
|
274
329
|
puts message unless @quiet
|
|
275
330
|
end
|
data/lib/belt/version.rb
CHANGED