putpaws 0.0.9 → 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 +4 -4
- data/README.md +153 -2
- data/lib/Putpawsfile +4 -0
- data/lib/putpaws/ai/ai_task.rb +1 -0
- data/lib/putpaws/ai/guide.rb +119 -0
- data/lib/putpaws/ai/tasks/ai_task.rake +14 -0
- data/lib/putpaws/application_config.rb +15 -2
- data/lib/putpaws/ecs/run_command.rb +115 -0
- data/lib/putpaws/ecs/task_command.rb +63 -9
- data/lib/putpaws/ecs/tasks/ecs_task.rake +113 -6
- data/lib/putpaws/iam/grant_command.rb +205 -0
- data/lib/putpaws/iam/iam_task.rb +1 -0
- data/lib/putpaws/iam/operator_config.rb +46 -0
- data/lib/putpaws/iam/tasks/iam_task.rake +65 -0
- data/lib/putpaws/info.rb +1 -0
- data/lib/putpaws/provision/all.rb +29 -0
- data/lib/putpaws/provision/aws_clients.rb +59 -0
- data/lib/putpaws/provision/config_writer.rb +88 -0
- data/lib/putpaws/provision/policy_generator.rb +221 -0
- data/lib/putpaws/provision/preset.rb +91 -0
- data/lib/putpaws/provision/presets/rails-nginx/preset.json +32 -0
- data/lib/putpaws/provision/presets/rails-nginx/templates/taskdef-app.json.erb +29 -0
- data/lib/putpaws/provision/presets/rails-nginx/templates/taskdef-web.json.erb +48 -0
- data/lib/putpaws/provision/provision_config.rb +156 -0
- data/lib/putpaws/provision/provision_task.rb +1 -0
- data/lib/putpaws/provision/resources/base.rb +49 -0
- data/lib/putpaws/provision/resources/cluster.rb +30 -0
- data/lib/putpaws/provision/resources/codebuild_project.rb +85 -0
- data/lib/putpaws/provision/resources/iam_role.rb +89 -0
- data/lib/putpaws/provision/resources/log_group.rb +50 -0
- data/lib/putpaws/provision/resources/security_group.rb +72 -0
- data/lib/putpaws/provision/resources/service.rb +71 -0
- data/lib/putpaws/provision/resources/task_definition.rb +119 -0
- data/lib/putpaws/provision/runner.rb +218 -0
- data/lib/putpaws/provision/state.rb +45 -0
- data/lib/putpaws/provision/tasks/provision.rake +129 -0
- data/lib/putpaws/provision/util.rb +50 -0
- data/lib/putpaws/tasks/info.rake +35 -0
- data/lib/putpaws/version.rb +1 -1
- metadata +62 -2
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
require 'putpaws/provision/util'
|
|
4
|
+
require 'putpaws/provision/state'
|
|
5
|
+
require 'putpaws/provision/aws_clients'
|
|
6
|
+
require 'putpaws/provision/policy_generator'
|
|
7
|
+
require 'putpaws/provision/config_writer'
|
|
8
|
+
require 'putpaws/provision/resources/base'
|
|
9
|
+
require 'putpaws/provision/resources/iam_role'
|
|
10
|
+
require 'putpaws/provision/resources/security_group'
|
|
11
|
+
require 'putpaws/provision/resources/cluster'
|
|
12
|
+
require 'putpaws/provision/resources/log_group'
|
|
13
|
+
require 'putpaws/provision/resources/task_definition'
|
|
14
|
+
require 'putpaws/provision/resources/service'
|
|
15
|
+
require 'putpaws/provision/resources/codebuild_project'
|
|
16
|
+
|
|
17
|
+
module Putpaws
|
|
18
|
+
module Provision
|
|
19
|
+
# Orchestrates the provisioning commands:
|
|
20
|
+
# ready (provision:init) ... interview + drafts
|
|
21
|
+
# steady (provision:roles) ... create IAM roles from the reviewed drafts
|
|
22
|
+
# ahead (provision:plan) ... dry-run
|
|
23
|
+
# up (provision:up) ... build the rest, idempotent
|
|
24
|
+
#
|
|
25
|
+
# Running `steady` IS the user's explicit confirmation of the drafts:
|
|
26
|
+
# `up` never touches IAM and stops until the role ARNs are filled.
|
|
27
|
+
class Runner
|
|
28
|
+
ROLE_KEYS = [:task_execution_role_arn, :task_role_arn, :codebuild_role_arn, :scheduler_role_arn]
|
|
29
|
+
ROLE_DRAFTS = {
|
|
30
|
+
task_execution_role_arn: 'role-task-execution.json',
|
|
31
|
+
task_role_arn: 'role-task.json',
|
|
32
|
+
codebuild_role_arn: 'role-codebuild.json',
|
|
33
|
+
scheduler_role_arn: 'role-scheduler.json',
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
attr_reader :config, :state, :clients, :prompt, :io
|
|
37
|
+
def initialize(config:, state: nil, clients: nil, prompt: nil, io: $stdout)
|
|
38
|
+
@config = config
|
|
39
|
+
@state = state || State.load(config)
|
|
40
|
+
@clients = clients || AwsClients.new(region: config.region)
|
|
41
|
+
@prompt = prompt
|
|
42
|
+
@io = io
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def blank_role_keys
|
|
46
|
+
ROLE_KEYS.select{|k| Util.blank?(config.roles[k])}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# A role is managed by `steady` when its ARN is blank (to be created) or
|
|
50
|
+
# when the filled ARN points at the draft's own role name (created by a
|
|
51
|
+
# previous `steady`; kept updatable). An ARN with a different role name
|
|
52
|
+
# is treated as external (org-managed) and never touched.
|
|
53
|
+
def steady_role_entries
|
|
54
|
+
args = {config: config, state: state, clients: clients}
|
|
55
|
+
ROLE_KEYS.map do |key|
|
|
56
|
+
resource = Resources::IamRole.new(roles_key: key, draft_file: ROLE_DRAFTS[key], **args)
|
|
57
|
+
arn = config.roles[key]
|
|
58
|
+
external = !Util.blank?(arn) && arn.split('/').last != resource.name
|
|
59
|
+
{key: key, resource: resource, external: external}
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def service_resources
|
|
64
|
+
args = {config: config, state: state, clients: clients}
|
|
65
|
+
[
|
|
66
|
+
Resources::SecurityGroup.new(**args),
|
|
67
|
+
Resources::LogGroup.new(log_group_name: config.log_group, output_key: :log_group, **args),
|
|
68
|
+
Resources::Cluster.new(**args),
|
|
69
|
+
*config.preset.task_definitions.map{|entry| Resources::TaskDefinition.new(entry: entry, **args)},
|
|
70
|
+
Resources::Service.new(**args),
|
|
71
|
+
]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def devops_resources
|
|
75
|
+
args = {config: config, state: state, clients: clients}
|
|
76
|
+
[
|
|
77
|
+
Resources::LogGroup.new(log_group_name: config.build_log_group, output_key: :build_log_group, **args),
|
|
78
|
+
Resources::CodebuildProject.new(**args),
|
|
79
|
+
]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def missing_devops_requirements
|
|
83
|
+
missing = []
|
|
84
|
+
missing << 'devops.source_repository (provision.json)' if Util.blank?(config.devops_settings[:source_repository])
|
|
85
|
+
missing
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# `putpaws steady`: show each draft inline, confirm one by one, then
|
|
89
|
+
# apply it as-is and fill the ARN into provision.json.
|
|
90
|
+
# Idempotent: re-run after editing drafts to update.
|
|
91
|
+
def steady!
|
|
92
|
+
io.puts "Applying IAM roles from drafts in #{config.policies_dir}/"
|
|
93
|
+
filled = {}
|
|
94
|
+
steady_role_entries.each do |entry|
|
|
95
|
+
if entry[:external]
|
|
96
|
+
print_action('SKIP', 'IamRole', "#{config.roles[entry[:key]]} (external)")
|
|
97
|
+
next
|
|
98
|
+
end
|
|
99
|
+
resource = entry[:resource]
|
|
100
|
+
action = resource.plan[:action]
|
|
101
|
+
|
|
102
|
+
if action == :skip
|
|
103
|
+
print_action('SKIP', 'IamRole', resource.name)
|
|
104
|
+
else
|
|
105
|
+
io.puts ""
|
|
106
|
+
io.puts "=== #{resource.draft_file} -> #{action.to_s.upcase} role: #{resource.name} ==="
|
|
107
|
+
io.puts JSON.pretty_generate(resource.draft)
|
|
108
|
+
unless prompt.yes?("Apply this content as role #{resource.name}?")
|
|
109
|
+
io.puts "Skipped #{resource.name}"
|
|
110
|
+
next
|
|
111
|
+
end
|
|
112
|
+
print_action(action.to_s.upcase, 'IamRole', resource.name)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
outputs = resource.ensure!
|
|
116
|
+
state.merge_resources!(outputs)
|
|
117
|
+
filled[entry[:key]] = outputs[entry[:key]]
|
|
118
|
+
end
|
|
119
|
+
write_roles_to_provision_json!(filled)
|
|
120
|
+
io.puts "Next: check with `putpaws ahead`, then build with `putpaws up`."
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def plan
|
|
124
|
+
io.puts "Service: #{config.service_name} (region: #{config.region}, account: #{config.account_id})"
|
|
125
|
+
io.puts ""
|
|
126
|
+
resources = steady_role_entries.reject{|e| e[:external]}.map{|e| e[:resource]}
|
|
127
|
+
resources += service_resources
|
|
128
|
+
resources += devops_resources if missing_devops_requirements.empty?
|
|
129
|
+
resources.each do |r|
|
|
130
|
+
p = r.plan
|
|
131
|
+
print_action(p[:action].to_s.upcase, p[:kind], p[:name])
|
|
132
|
+
end
|
|
133
|
+
if blank_role_keys.any?
|
|
134
|
+
io.puts "[stop point] IAM roles are not created yet. Review the drafts, then run `putpaws steady`."
|
|
135
|
+
end
|
|
136
|
+
if missing_devops_requirements.any?
|
|
137
|
+
io.puts "[stop point] Devops step is blocked. Fill in: #{missing_devops_requirements.join(', ')}"
|
|
138
|
+
end
|
|
139
|
+
check_ssm_parameters
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def up!
|
|
143
|
+
if blank_role_keys.any?
|
|
144
|
+
io.puts "[stop point] IAM roles are not ready: #{blank_role_keys.join(', ')}"
|
|
145
|
+
io.puts "1. Review and edit the drafts in #{config.policies_dir}/"
|
|
146
|
+
io.puts "2. Run `putpaws steady` to create the roles and fill their ARNs."
|
|
147
|
+
io.puts " (or fill in existing role ARNs in provision.json yourself)"
|
|
148
|
+
return
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
io.puts "Applying service step for #{config.service_name} (account: #{config.account_id}, region: #{config.region})"
|
|
152
|
+
apply_resources(service_resources)
|
|
153
|
+
state.mark_step!(:service, 'done')
|
|
154
|
+
|
|
155
|
+
ConfigWriter.new(config: config, state: state, prompt: prompt, io: io).apply!
|
|
156
|
+
|
|
157
|
+
if missing_devops_requirements.any?
|
|
158
|
+
io.puts "[stop point] Devops step is not ready. Fill in: #{missing_devops_requirements.join(', ')}"
|
|
159
|
+
io.puts "Then run `putpaws up` again."
|
|
160
|
+
return
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
io.puts "Applying devops step"
|
|
164
|
+
apply_resources(devops_resources)
|
|
165
|
+
state.mark_step!(:devops, 'done')
|
|
166
|
+
ConfigWriter.new(config: config, state: state, prompt: prompt, io: io).apply!
|
|
167
|
+
|
|
168
|
+
io.puts "All steps completed."
|
|
169
|
+
io.puts "Verify with: bundle exec putpaws #{config.service_name} ecs:run cmd='echo ok' wait=true"
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
private
|
|
173
|
+
|
|
174
|
+
def print_action(action, kind, name)
|
|
175
|
+
io.puts format("%-8s %-18s %s", action, kind, name)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def write_roles_to_provision_json!(filled)
|
|
179
|
+
return if filled.empty?
|
|
180
|
+
path = config.dir.join('provision.json')
|
|
181
|
+
data = JSON.parse(File.read(path), symbolize_names: true)
|
|
182
|
+
data[:roles] ||= {}
|
|
183
|
+
changed = filled.reject{|k, arn| data[:roles][k] == arn}
|
|
184
|
+
return if changed.empty?
|
|
185
|
+
FileUtils.cp(path, "#{path}.bak")
|
|
186
|
+
changed.each{|k, arn| data[:roles][k] = arn}
|
|
187
|
+
Util.write_json(path, data)
|
|
188
|
+
config.data[:roles] = data[:roles]
|
|
189
|
+
io.puts "Filled role ARNs into #{path}:"
|
|
190
|
+
changed.each{|k, arn| io.puts " #{k}: #{arn}"}
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def apply_resources(resources)
|
|
194
|
+
resources.each do |r|
|
|
195
|
+
print_action(r.plan[:action].to_s.upcase, r.kind, r.name)
|
|
196
|
+
outputs = r.ensure!
|
|
197
|
+
state.merge_resources!(outputs)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def check_ssm_parameters
|
|
202
|
+
paths = config.resolved_secrets.values
|
|
203
|
+
return if paths.empty?
|
|
204
|
+
res = clients.ssm.describe_parameters(
|
|
205
|
+
parameter_filters: [{key: 'Name', option: 'Equals', values: paths}]
|
|
206
|
+
)
|
|
207
|
+
existing = res.parameters.map(&:name)
|
|
208
|
+
missing = paths - existing
|
|
209
|
+
if missing.any?
|
|
210
|
+
io.puts "[warning] Missing SSM parameters (register them yourself, putpaws never touches values):"
|
|
211
|
+
missing.each{|p| io.puts " aws ssm put-parameter --type SecureString --name #{p} --value '...'"}
|
|
212
|
+
end
|
|
213
|
+
rescue StandardError => e
|
|
214
|
+
io.puts "(ssm parameter check skipped: #{e.class})"
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'putpaws/provision/util'
|
|
3
|
+
|
|
4
|
+
module Putpaws
|
|
5
|
+
module Provision
|
|
6
|
+
class State
|
|
7
|
+
def self.load(config)
|
|
8
|
+
path = config.dir.join('state.json')
|
|
9
|
+
data = path.exist? ? JSON.parse(File.read(path), symbolize_names: true) : {}
|
|
10
|
+
new(path: path.to_s, data: data)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
attr_reader :path, :data
|
|
14
|
+
def initialize(path:, data:)
|
|
15
|
+
@path = path
|
|
16
|
+
@data = data
|
|
17
|
+
@data[:resources] ||= {}
|
|
18
|
+
@data[:steps] ||= {}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def resources
|
|
22
|
+
data[:resources]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def steps
|
|
26
|
+
data[:steps]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def merge_resources!(outputs)
|
|
30
|
+
resources.merge!(outputs.map{|k, v| [k.to_sym, v]}.to_h)
|
|
31
|
+
save!
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def mark_step!(name, status)
|
|
35
|
+
steps[name.to_sym] = status
|
|
36
|
+
save!
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def save!
|
|
40
|
+
data[:updated_at] = Time.now.to_s
|
|
41
|
+
Util.write_json(path, data)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
require "putpaws/prompt"
|
|
3
|
+
require "putpaws/provision/all"
|
|
4
|
+
|
|
5
|
+
namespace :provision do
|
|
6
|
+
desc "Interview basic infra inputs and generate provision.json + IAM policy drafts. (alias: ready)"
|
|
7
|
+
task :init do
|
|
8
|
+
prompt = Putpaws::Prompt.safe
|
|
9
|
+
util = Putpaws::Provision::Util
|
|
10
|
+
|
|
11
|
+
service_name = ENV['service'] || prompt.ask("Application name (Ex: awesome-api-staging):") do |q|
|
|
12
|
+
q.required(true)
|
|
13
|
+
q.validate(/\A[a-z0-9][a-z0-9\-]*\z/, "lowercase letters, numbers and hyphens only")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
dir = Putpaws::Provision::ProvisionConfig.dir_for(service_name)
|
|
17
|
+
if dir.join('provision.json').exist?
|
|
18
|
+
unless prompt.yes?("#{dir}/provision.json already exists. Overwrite?", default: false)
|
|
19
|
+
puts "Aborted."
|
|
20
|
+
next
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
region = prompt.ask("Region:", default: 'ap-northeast-1')
|
|
25
|
+
preset_choices = Putpaws::Provision::Preset.catalog.map{|e| ["#{e[:name]} (#{e[:source]})", e[:name]]}.to_h
|
|
26
|
+
preset_name = prompt.select("Preset:", preset_choices)
|
|
27
|
+
|
|
28
|
+
puts "--- Basic infra (existing resources; used to generate IAM policies) ---"
|
|
29
|
+
vpc_id = prompt.ask("VPC ID:"){|q| q.required(true)}
|
|
30
|
+
subnets = prompt.ask("Subnet IDs (comma separated):"){|q| q.required(true)}.split(',').map(&:strip)
|
|
31
|
+
sg_ids = prompt.ask("Security group IDs (existing, comma separated, empty to create a new one):")
|
|
32
|
+
.to_s.split(',').map(&:strip).reject(&:empty?)
|
|
33
|
+
ecr_arn = prompt.ask("ECR repository ARN:"){|q| q.required(true)}
|
|
34
|
+
ses_arn = prompt.ask("SES identity ARN (empty to skip):")
|
|
35
|
+
s3_arn = prompt.ask("S3 bucket ARN (empty to skip):")
|
|
36
|
+
ssm_prefix = prompt.ask("SSM parameter path prefix (existing; putpaws never writes values):", default: "/#{service_name}")
|
|
37
|
+
repo = prompt.ask("CodeBuild source repository URL (GitHub, empty to fill later):")
|
|
38
|
+
|
|
39
|
+
data = {
|
|
40
|
+
service_name: service_name,
|
|
41
|
+
region: region,
|
|
42
|
+
preset: preset_name,
|
|
43
|
+
base: {
|
|
44
|
+
vpc_id: vpc_id,
|
|
45
|
+
subnets: subnets,
|
|
46
|
+
security_group_ids: sg_ids,
|
|
47
|
+
ecr_repository_arn: ecr_arn,
|
|
48
|
+
ses_identity_arn: util.blank?(ses_arn) ? nil : ses_arn,
|
|
49
|
+
s3_bucket_arn: util.blank?(s3_arn) ? nil : s3_arn,
|
|
50
|
+
ssm_parameter_prefix: ssm_prefix,
|
|
51
|
+
},
|
|
52
|
+
roles: {
|
|
53
|
+
task_execution_role_arn: nil,
|
|
54
|
+
task_role_arn: nil,
|
|
55
|
+
codebuild_role_arn: nil,
|
|
56
|
+
scheduler_role_arn: nil,
|
|
57
|
+
},
|
|
58
|
+
overrides: {
|
|
59
|
+
cluster_name: nil,
|
|
60
|
+
cpu: nil,
|
|
61
|
+
memory: nil,
|
|
62
|
+
desired_count: nil,
|
|
63
|
+
env: {},
|
|
64
|
+
secrets: {},
|
|
65
|
+
},
|
|
66
|
+
devops: {
|
|
67
|
+
source_repository: util.blank?(repo) ? nil : repo,
|
|
68
|
+
buildspec: "buildspec.yml",
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
Putpaws::Provision::Preset.install(preset_name)
|
|
73
|
+
FileUtils.mkdir_p(dir)
|
|
74
|
+
util.write_json(dir.join('provision.json'), data)
|
|
75
|
+
|
|
76
|
+
config = Putpaws::Provision::ProvisionConfig.new(data)
|
|
77
|
+
files = Putpaws::Provision::PolicyGenerator.new(config).write_step1_drafts!
|
|
78
|
+
|
|
79
|
+
puts ""
|
|
80
|
+
puts "Generated:"
|
|
81
|
+
puts " #{dir}/provision.json"
|
|
82
|
+
files.each{|f| puts " #{f}"}
|
|
83
|
+
puts ""
|
|
84
|
+
puts "Next steps:"
|
|
85
|
+
puts " 1. Review and edit the role drafts in policies/."
|
|
86
|
+
puts " When confirmed, run `putpaws steady` to create the IAM roles from the drafts as-is."
|
|
87
|
+
puts " (or fill in existing role ARNs in provision.json instead)"
|
|
88
|
+
puts " 2. Register SSM parameters (Ex: #{ssm_prefix}/RAILS_MASTER_KEY) yourself."
|
|
89
|
+
puts " 3. Check with `putpaws ahead`, then build with `putpaws up`."
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
desc "Create IAM roles from the reviewed drafts and fill their ARNs into provision.json. Each role is shown and confirmed one by one. Idempotent. (alias: steady)"
|
|
93
|
+
task :roles do
|
|
94
|
+
config = Putpaws::Provision.resolve_config
|
|
95
|
+
prompt = Putpaws::Prompt.safe
|
|
96
|
+
puts "Service: #{config.service_name} / Account: #{config.account_id} / Region: #{config.region}"
|
|
97
|
+
Putpaws::Provision::Runner.new(config: config, prompt: prompt).steady!
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
desc "Show what `putpaws up` would do (dry-run). (alias: ahead)"
|
|
101
|
+
task :plan do
|
|
102
|
+
config = Putpaws::Provision.resolve_config
|
|
103
|
+
Putpaws::Provision::Runner.new(config: config, prompt: Putpaws::Prompt.safe).plan
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
desc "Build infra as far as possible, stopping where manual steps are required. Re-runnable. (alias: up)"
|
|
107
|
+
task :up do
|
|
108
|
+
config = Putpaws::Provision.resolve_config
|
|
109
|
+
prompt = Putpaws::Prompt.safe
|
|
110
|
+
puts "Service: #{config.service_name} / Account: #{config.account_id} / Region: #{config.region}"
|
|
111
|
+
unless prompt.yes?("Proceed?")
|
|
112
|
+
puts "Aborted."
|
|
113
|
+
next
|
|
114
|
+
end
|
|
115
|
+
Putpaws::Provision::Runner.new(config: config, prompt: prompt).up!
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
desc "Alias of provision:init"
|
|
120
|
+
task ready: 'provision:init'
|
|
121
|
+
|
|
122
|
+
desc "Alias of provision:roles"
|
|
123
|
+
task steady: 'provision:roles'
|
|
124
|
+
|
|
125
|
+
desc "Alias of provision:up"
|
|
126
|
+
task up: 'provision:up'
|
|
127
|
+
|
|
128
|
+
desc "Alias of provision:plan"
|
|
129
|
+
task ahead: 'provision:plan'
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
module Putpaws
|
|
4
|
+
module Provision
|
|
5
|
+
module Util
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def underscore(str)
|
|
9
|
+
str.to_s.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Task definition JSON is written in camelCase convention.
|
|
13
|
+
# Convert it into snake_case symbol keys for aws-sdk params.
|
|
14
|
+
def deep_underscore_keys(obj)
|
|
15
|
+
case obj
|
|
16
|
+
when Hash
|
|
17
|
+
obj.map{|k, v| [underscore(k).to_sym, deep_underscore_keys(v)]}.to_h
|
|
18
|
+
when Array
|
|
19
|
+
obj.map{|x| deep_underscore_keys(x)}
|
|
20
|
+
else
|
|
21
|
+
obj
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def arn_account_id(arn)
|
|
26
|
+
arn.to_s.split(':')[4]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def arn_region(arn)
|
|
30
|
+
arn.to_s.split(':')[3]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def ecr_repository_name(arn)
|
|
34
|
+
arn.to_s.split(':', 6).last.to_s.sub(/\Arepository\//, '')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def ecr_repository_uri(arn)
|
|
38
|
+
"#{arn_account_id(arn)}.dkr.ecr.#{arn_region(arn)}.amazonaws.com/#{ecr_repository_name(arn)}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def blank?(value)
|
|
42
|
+
value.nil? || value.to_s.strip.empty?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def write_json(path, data)
|
|
46
|
+
File.write(path, JSON.pretty_generate(data) + "\n")
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
desc "Show resolved settings for the service. No AWS access."
|
|
2
|
+
task :info do
|
|
3
|
+
app = fetch(:app)
|
|
4
|
+
row = ->(key, value){ puts format("%-26s %s", "#{key}:", value) unless value.nil? }
|
|
5
|
+
|
|
6
|
+
row.call('name', app.name)
|
|
7
|
+
row.call('region', app.region)
|
|
8
|
+
row.call('cluster', app.cluster)
|
|
9
|
+
row.call('service', app.service)
|
|
10
|
+
row.call('task_name_prefix', app.task_name_prefix)
|
|
11
|
+
row.call('ecs_region', app.ecs_region)
|
|
12
|
+
row.call('log_group_prefix', app.log_group_prefix)
|
|
13
|
+
row.call('log_region', app.log_region)
|
|
14
|
+
row.call('build_project_name_prefix', app.build_project_name_prefix)
|
|
15
|
+
row.call('build_log_group_prefix', app.build_log_group_prefix)
|
|
16
|
+
row.call('build_region', app.build_region)
|
|
17
|
+
|
|
18
|
+
if (network = app.network)
|
|
19
|
+
row.call('network', network.name)
|
|
20
|
+
row.call(' subnets', network.subnets && network.subnets.join(', '))
|
|
21
|
+
row.call(' security_groups', network.security_groups && network.security_groups.join(', '))
|
|
22
|
+
row.call(' assign_public_ip', network.assign_public_ip)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if (target = app.target)
|
|
26
|
+
row.call('target', target.name)
|
|
27
|
+
row.call(' cluster', target.cluster)
|
|
28
|
+
row.call(' task_definition', target.task_definition)
|
|
29
|
+
row.call(' container_name', target.container_name)
|
|
30
|
+
row.call(' scheduler_role', target.scheduler_role)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
schedules = app.schedules
|
|
34
|
+
row.call('schedules', schedules.map(&:name).join(', ')) unless schedules.empty?
|
|
35
|
+
end
|
data/lib/putpaws/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: putpaws
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- metheglin
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-09-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|
|
@@ -66,6 +66,34 @@ dependencies:
|
|
|
66
66
|
- - ">="
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: aws-sdk-ec2
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: aws-sdk-iam
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :runtime
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
69
97
|
- !ruby/object:Gem::Dependency
|
|
70
98
|
name: aws-sdk-cloudwatchlogs
|
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -119,6 +147,9 @@ files:
|
|
|
119
147
|
- bin/putpaws
|
|
120
148
|
- lib/Putpawsfile
|
|
121
149
|
- lib/putpaws.rb
|
|
150
|
+
- lib/putpaws/ai/ai_task.rb
|
|
151
|
+
- lib/putpaws/ai/guide.rb
|
|
152
|
+
- lib/putpaws/ai/tasks/ai_task.rake
|
|
122
153
|
- lib/putpaws/all.rb
|
|
123
154
|
- lib/putpaws/application.rb
|
|
124
155
|
- lib/putpaws/application_config.rb
|
|
@@ -131,19 +162,48 @@ files:
|
|
|
131
162
|
- lib/putpaws/code_build/tasks/build_task.rake
|
|
132
163
|
- lib/putpaws/dsl.rb
|
|
133
164
|
- lib/putpaws/ecs/ecs_task.rb
|
|
165
|
+
- lib/putpaws/ecs/run_command.rb
|
|
134
166
|
- lib/putpaws/ecs/task_command.rb
|
|
135
167
|
- lib/putpaws/ecs/tasks/ecs_task.rake
|
|
136
168
|
- lib/putpaws/env.rb
|
|
137
169
|
- lib/putpaws/hello.rb
|
|
170
|
+
- lib/putpaws/iam/grant_command.rb
|
|
171
|
+
- lib/putpaws/iam/iam_task.rb
|
|
172
|
+
- lib/putpaws/iam/operator_config.rb
|
|
173
|
+
- lib/putpaws/iam/tasks/iam_task.rake
|
|
174
|
+
- lib/putpaws/info.rb
|
|
138
175
|
- lib/putpaws/infra_network_config.rb
|
|
139
176
|
- lib/putpaws/infra_target_config.rb
|
|
140
177
|
- lib/putpaws/prompt.rb
|
|
178
|
+
- lib/putpaws/provision/all.rb
|
|
179
|
+
- lib/putpaws/provision/aws_clients.rb
|
|
180
|
+
- lib/putpaws/provision/config_writer.rb
|
|
181
|
+
- lib/putpaws/provision/policy_generator.rb
|
|
182
|
+
- lib/putpaws/provision/preset.rb
|
|
183
|
+
- lib/putpaws/provision/presets/rails-nginx/preset.json
|
|
184
|
+
- lib/putpaws/provision/presets/rails-nginx/templates/taskdef-app.json.erb
|
|
185
|
+
- lib/putpaws/provision/presets/rails-nginx/templates/taskdef-web.json.erb
|
|
186
|
+
- lib/putpaws/provision/provision_config.rb
|
|
187
|
+
- lib/putpaws/provision/provision_task.rb
|
|
188
|
+
- lib/putpaws/provision/resources/base.rb
|
|
189
|
+
- lib/putpaws/provision/resources/cluster.rb
|
|
190
|
+
- lib/putpaws/provision/resources/codebuild_project.rb
|
|
191
|
+
- lib/putpaws/provision/resources/iam_role.rb
|
|
192
|
+
- lib/putpaws/provision/resources/log_group.rb
|
|
193
|
+
- lib/putpaws/provision/resources/security_group.rb
|
|
194
|
+
- lib/putpaws/provision/resources/service.rb
|
|
195
|
+
- lib/putpaws/provision/resources/task_definition.rb
|
|
196
|
+
- lib/putpaws/provision/runner.rb
|
|
197
|
+
- lib/putpaws/provision/state.rb
|
|
198
|
+
- lib/putpaws/provision/tasks/provision.rake
|
|
199
|
+
- lib/putpaws/provision/util.rb
|
|
141
200
|
- lib/putpaws/schedule_config.rb
|
|
142
201
|
- lib/putpaws/scheduler/schedule_command.rb
|
|
143
202
|
- lib/putpaws/scheduler/scheduler_task.rb
|
|
144
203
|
- lib/putpaws/scheduler/tasks/scheduler_task.rake
|
|
145
204
|
- lib/putpaws/setup.rb
|
|
146
205
|
- lib/putpaws/tasks/hello.rake
|
|
206
|
+
- lib/putpaws/tasks/info.rake
|
|
147
207
|
- lib/putpaws/version.rb
|
|
148
208
|
homepage: https://rubygems.org/gems/putpaws
|
|
149
209
|
licenses:
|