Class: Putpaws::Provision::Runner

Inherits:
Object
  • Object
show all
Defined in:
lib/putpaws/provision/runner.rb

Overview

Orchestrates the provisioning commands:

ready  (provision:init)  ... interview + drafts
steady (provision:roles) ... create IAM roles from the reviewed drafts
ahead  (provision:plan)  ... dry-run
up     (provision:up)    ... build the rest, idempotent

Running steady IS the user's explicit confirmation of the drafts: up never touches IAM and stops until the role ARNs are filled.

Constant Summary collapse

ROLE_KEYS =
[:task_execution_role_arn, :task_role_arn, :codebuild_role_arn, :scheduler_role_arn]
ROLE_DRAFTS =
{
  task_execution_role_arn: 'role-task-execution.json',
  task_role_arn: 'role-task.json',
  codebuild_role_arn: 'role-codebuild.json',
  scheduler_role_arn: 'role-scheduler.json',
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config:, state: nil, clients: nil, prompt: nil, io: $stdout) ⇒ Runner

Returns a new instance of Runner.



37
38
39
40
41
42
43
# File 'lib/putpaws/provision/runner.rb', line 37

def initialize(config:, state: nil, clients: nil, prompt: nil, io: $stdout)
  @config = config
  @state = state || State.load(config)
  @clients = clients || AwsClients.new(region: config.region)
  @prompt = prompt
  @io = io
end

Instance Attribute Details

#clientsObject (readonly)

Returns the value of attribute clients.



36
37
38
# File 'lib/putpaws/provision/runner.rb', line 36

def clients
  @clients
end

#configObject (readonly)

Returns the value of attribute config.



36
37
38
# File 'lib/putpaws/provision/runner.rb', line 36

def config
  @config
end

#ioObject (readonly)

Returns the value of attribute io.



36
37
38
# File 'lib/putpaws/provision/runner.rb', line 36

def io
  @io
end

#promptObject (readonly)

Returns the value of attribute prompt.



36
37
38
# File 'lib/putpaws/provision/runner.rb', line 36

def prompt
  @prompt
end

#stateObject (readonly)

Returns the value of attribute state.



36
37
38
# File 'lib/putpaws/provision/runner.rb', line 36

def state
  @state
end

Instance Method Details

#blank_role_keysObject



45
46
47
# File 'lib/putpaws/provision/runner.rb', line 45

def blank_role_keys
  ROLE_KEYS.select{|k| Util.blank?(config.roles[k])}
end

#devops_resourcesObject



74
75
76
77
78
79
80
# File 'lib/putpaws/provision/runner.rb', line 74

def devops_resources
  args = {config: config, state: state, clients: clients}
  [
    Resources::LogGroup.new(log_group_name: config.build_log_group, output_key: :build_log_group, **args),
    Resources::CodebuildProject.new(**args),
  ]
end

#missing_devops_requirementsObject



82
83
84
85
86
# File 'lib/putpaws/provision/runner.rb', line 82

def missing_devops_requirements
  missing = []
  missing << 'devops.source_repository (provision.json)' if Util.blank?(config.devops_settings[:source_repository])
  missing
end

#planObject



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/putpaws/provision/runner.rb', line 123

def plan
  io.puts "Service: #{config.service_name} (region: #{config.region}, account: #{config.account_id})"
  io.puts ""
  resources = steady_role_entries.reject{|e| e[:external]}.map{|e| e[:resource]}
  resources += service_resources
  resources += devops_resources if missing_devops_requirements.empty?
  resources.each do |r|
    p = r.plan
    print_action(p[:action].to_s.upcase, p[:kind], p[:name])
  end
  if blank_role_keys.any?
    io.puts "[stop point] IAM roles are not created yet. Review the drafts, then run `putpaws steady`."
  end
  if missing_devops_requirements.any?
    io.puts "[stop point] Devops step is blocked. Fill in: #{missing_devops_requirements.join(', ')}"
  end
  check_ssm_parameters
end

#service_resourcesObject



63
64
65
66
67
68
69
70
71
72
# File 'lib/putpaws/provision/runner.rb', line 63

def service_resources
  args = {config: config, state: state, clients: clients}
  [
    Resources::SecurityGroup.new(**args),
    Resources::LogGroup.new(log_group_name: config.log_group, output_key: :log_group, **args),
    Resources::Cluster.new(**args),
    *config.preset.task_definitions.map{|entry| Resources::TaskDefinition.new(entry: entry, **args)},
    Resources::Service.new(**args),
  ]
end

#steady!Object

putpaws steady: show each draft inline, confirm one by one, then apply it as-is and fill the ARN into provision.json. Idempotent: re-run after editing drafts to update.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/putpaws/provision/runner.rb', line 91

def steady!
  io.puts "Applying IAM roles from drafts in #{config.policies_dir}/"
  filled = {}
  steady_role_entries.each do |entry|
    if entry[:external]
      print_action('SKIP', 'IamRole', "#{config.roles[entry[:key]]} (external)")
      next
    end
    resource = entry[:resource]
    action = resource.plan[:action]

    if action == :skip
      print_action('SKIP', 'IamRole', resource.name)
    else
      io.puts ""
      io.puts "=== #{resource.draft_file} -> #{action.to_s.upcase} role: #{resource.name} ==="
      io.puts JSON.pretty_generate(resource.draft)
      unless prompt.yes?("Apply this content as role #{resource.name}?")
        io.puts "Skipped #{resource.name}"
        next
      end
      print_action(action.to_s.upcase, 'IamRole', resource.name)
    end

    outputs = resource.ensure!
    state.merge_resources!(outputs)
    filled[entry[:key]] = outputs[entry[:key]]
  end
  write_roles_to_provision_json!(filled)
  io.puts "Next: check with `putpaws ahead`, then build with `putpaws up`."
end

#steady_role_entriesObject

A role is managed by steady when its ARN is blank (to be created) or when the filled ARN points at the draft's own role name (created by a previous steady; kept updatable). An ARN with a different role name is treated as external (org-managed) and never touched.



53
54
55
56
57
58
59
60
61
# File 'lib/putpaws/provision/runner.rb', line 53

def steady_role_entries
  args = {config: config, state: state, clients: clients}
  ROLE_KEYS.map do |key|
    resource = Resources::IamRole.new(roles_key: key, draft_file: ROLE_DRAFTS[key], **args)
    arn = config.roles[key]
    external = !Util.blank?(arn) && arn.split('/').last != resource.name
    {key: key, resource: resource, external: external}
  end
end

#up!Object



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/putpaws/provision/runner.rb', line 142

def up!
  if blank_role_keys.any?
    io.puts "[stop point] IAM roles are not ready: #{blank_role_keys.join(', ')}"
    io.puts "1. Review and edit the drafts in #{config.policies_dir}/"
    io.puts "2. Run `putpaws steady` to create the roles and fill their ARNs."
    io.puts "   (or fill in existing role ARNs in provision.json yourself)"
    return
  end

  io.puts "Applying service step for #{config.service_name} (account: #{config.account_id}, region: #{config.region})"
  apply_resources(service_resources)
  state.mark_step!(:service, 'done')

  ConfigWriter.new(config: config, state: state, prompt: prompt, io: io).apply!

  if missing_devops_requirements.any?
    io.puts "[stop point] Devops step is not ready. Fill in: #{missing_devops_requirements.join(', ')}"
    io.puts "Then run `putpaws up` again."
    return
  end

  io.puts "Applying devops step"
  apply_resources(devops_resources)
  state.mark_step!(:devops, 'done')
  ConfigWriter.new(config: config, state: state, prompt: prompt, io: io).apply!

  io.puts "All steps completed."
  io.puts "Verify with: bundle exec putpaws #{config.service_name} ecs:run cmd='echo ok' wait=true"
end