Class: MuxTf::InitFormatter
- Inherits:
-
Object
- Object
- MuxTf::InitFormatter
- Extended by:
- ErrorHandlingMethods, FormatterCommon, TerraformHelpers, PiotrbCliUtils::Util
- Includes:
- Coloring
- Defined in:
- lib/mux_tf/init_formatter.rb
Class Method Summary collapse
- .handle_init_line(state, line, meta, phase:, stripped_line:) ⇒ Object
- .init_status_to_remedies(status, meta) ⇒ Object
- .parse_tf_ui_line(parsed_line, meta, parser, phase:) ⇒ Object
- .print_init_line(parsed_line, without: [], from: nil) ⇒ Object
- .run_tf_init(upgrade: nil, reconfigure: nil) ⇒ Object
- .setup_init_parser(parser) ⇒ Object
- .tf_init_json(upgrade: nil, reconfigure: nil, &block) ⇒ Object
Methods included from TerraformHelpers
tf_apply, tf_force_unlock, tf_init, tf_plan, tf_show, tf_stream_helper, tf_validate
Methods included from ErrorHandlingMethods
handle_error_states, log_unhandled_line, print_errors, setup_error_handling
Methods included from FormatterCommon
emit_line_helper, format_validation_range, paint_line, parse_non_json_plan_line, print_tg_error_line, print_unhandled_error_line, tf_cmd_json
Methods included from Coloring
Class Method Details
.handle_init_line(state, line, meta, phase:, stripped_line:) ⇒ Object
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 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 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 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 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/mux_tf/init_formatter.rb', line 73 def handle_init_line(state, line, , phase:, stripped_line:) case state when :modules_init if phase == state case stripped_line when /^Downloading (?<repo>[^ ]+) (?<version>[^ ]+) for (?<module>[^ ]+)\.\.\./ print "D" when /^Downloading (?<repo>[^ ]+) for (?<module>[^ ]+)\.\.\./ # rubocop:disable Lint/DuplicateBranch print "D" when /^- (?<module>[^ ]+) in(?: (?<path>.+))?$/ print "." when "" puts else log_unhandled_line(state, line, reason: "unexpected line in :modules_init state") end else phase = state log "Initializing modules ", depth: 1 end when :modules_upgrade if phase == state case stripped_line when /^- (?<module>[^ ]+) in (?<path>.+)$/ print "." when /^Downloading (?<repo>[^ ]+) (?<version>[^ ]+) for (?<module>[^ ]+)\.\.\./ print "D" when /^Downloading (?<repo>[^ ]+) for (?<module>[^ ]+)\.\.\./ # rubocop:disable Lint/DuplicateBranch print "D" when "" puts else log_unhandled_line(state, line, reason: "unexpected line in :modules_upgrade state") end else # first line phase = state log "Upgrding modules ", depth: 1, newline: false end when :backend if phase == state case stripped_line when /^Successfully configured/ log line, depth: 2 when /unless the backend/ # rubocop:disable Lint/DuplicateBranch log line, depth: 2 when "" puts else log_unhandled_line(state, line, reason: "unexpected line in :backend state") end else # first line phase = state log "Initializing the backend ", depth: 1 # , newline: false end when :backend_error if raw_line.match "terraform init -reconfigure" [:need_reconfigure] = true log pastel.red("module needs to be reconfigured"), depth: 2 end if raw_line.match "Error when retrieving token from sso" [:need_auth] = true log pastel.red("authentication problem"), depth: 2 end when :plugins if phase == state case stripped_line when /^- Reusing previous version of (?<module>.+) from the dependency lock file$/ info = $LAST_MATCH_INFO.named_captures log "- [FROM-LOCK] #{info['module']}", depth: 2 when /^- (?<module>.+) is built in to Terraform$/ info = $LAST_MATCH_INFO.named_captures log "- [BUILTIN] #{info['module']}", depth: 2 when /^- Finding (?<module>[^ ]+) versions matching "(?<version>.+)"\.\.\./ info = $LAST_MATCH_INFO.named_captures log "- [FIND] #{info['module']} matching #{info['version'].inspect}", depth: 2 when /^- Finding latest version of (?<module>.+)\.\.\.$/ info = $LAST_MATCH_INFO.named_captures log "- [FIND] #{info['module']}", depth: 2 when /^- Installing (?<module>[^ ]+) v(?<version>.+)\.\.\.$/ info = $LAST_MATCH_INFO.named_captures log "- [INSTALLING] #{info['module']} v#{info['version']}", depth: 2 when /^- Installed (?<module>[^ ]+) v(?<version>[^ ]+) \((?:signed, key ID)?(?:, | by)?(?: a)? (?<signed>.+)\)$/ info = $LAST_MATCH_INFO.named_captures log "- [INSTALLED] #{info['module']} v#{info['version']} (#{info['signed']})", depth: 2 when /^- Using previously-installed (?<module>[^ ]+) v(?<version>.+)$/ info = $LAST_MATCH_INFO.named_captures log "- [USING] #{info['module']} v#{info['version']}", depth: 2 when /^- Downloading plugin for provider "(?<provider>[^"]+)" \((?<provider_path>[^)]+)\) (?<version>.+)\.\.\.$/ info = $LAST_MATCH_INFO.named_captures log "- #{info['provider']} #{info['version']}", depth: 2 when /^- Using (?<provider>[^ ]+) v(?<version>.+) from the shared cache directory$/ info = $LAST_MATCH_INFO.named_captures log "- [CACHE HIT] #{info['provider']} #{info['version']}", depth: 2 when "- Checking for available provider plugins..." # noop when %r{^- terraform\.io/builtin/terraform is built in to OpenTofu} # noop when /Providers are signed by their developers./ # noop when /OpenTofu has created a lock file/ # noop else log_unhandled_line(state, line, reason: "unexpected line in :plugins state") end else # first line phase = state log "Initializing provider plugins ...", depth: 1 end when :plugin_warnings if phase == state log pastel.yellow(line), depth: 1 else # first line phase = state end when :none log_unhandled_line(state, line, reason: "unexpected line in :none state") if line != "" else return false # log_unhandled_line(state, line, reason: "unexpected state") unless handle_error_states(meta, state, line) end { phase: phase } end |
.init_status_to_remedies(status, meta) ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/mux_tf/init_formatter.rb', line 40 def init_status_to_remedies(status, ) remedies = Set.new if status != 0 remedies << :reconfigure if [:need_reconfigure] remedies << :auth if [:need_auth] log "!! expected meta[:errors] to be set, how did we get here?" unless [:errors] [:errors]&.each do |error| remedies << :add_provider_constraint if error[:body].grep(/Could not retrieve the list of available versions for provider/) remedies << :user_error if error[:body].grep(/Unreadable module directory/) end if remedies.empty? log "!! don't know how to generate init remedies for this" log "!! Status: #{status}" log "!! Meta:" log .to_yaml.split("\n").map { |l| "!! #{l}" }.join("\n") remedies << :unknown end end remedies end |
.parse_tf_ui_line(parsed_line, meta, parser, phase:) ⇒ Object
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/mux_tf/init_formatter.rb', line 13 def parse_tf_ui_line(parsed_line, , parser, phase:) # p(parsed_line) case parsed_line[:type] when "version" [:terraform_version] = parsed_line[:terraform] if parsed_line[:terraform] [:ui_version] = parsed_line[:ui] if parsed_line[:ui] [:tofu_version] = parsed_line[:tofu] if parsed_line[:tofu] when "output", "unknown" raw_line = parsed_line[:message] stripped_line = pastel.strip(raw_line.rstrip) parser.parse(raw_line.rstrip) do |state, line| if (handled = handle_init_line(state, line, , phase: phase, stripped_line: stripped_line)) phase = handled[:phase] elsif handle_error_states(, state, line) # no-op else log_unhandled_line(state, line, reason: "unexpected state") end end else print_init_line(parsed_line, from: "parse_tf_ui_line,else") end phase end |
.print_init_line(parsed_line, without: [], from: nil) ⇒ Object
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/mux_tf/init_formatter.rb', line 278 def print_init_line(parsed_line, without: [], from: nil) default_without = [ :level, :module, :type, :stream, :message, :timestamp, :terraform, :ui ] extra = parsed_line.without(*default_without, *without) data = parsed_line.merge(extra: extra).merge(from: from) log_line = [ "%<from>s", "%<level>-6s", "%<module>-12s", "%<type>-10s", "%<message>s", "%<extra>s" ].map { |format_string| field = format_string.match(/%<([^>]+)>/)[1].to_sym data[field].present? ? format(format_string, data) : nil }.compact.join(" | ") log log_line end |
.run_tf_init(upgrade: nil, reconfigure: nil) ⇒ Object
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/mux_tf/init_formatter.rb', line 201 def run_tf_init(upgrade: nil, reconfigure: nil) phase = :init = {} [:seen] = { module_and_type: Set.new } parser = StatefulParser.new(normalizer: pastel.method(:strip)) setup_init_parser(parser) setup_error_handling(parser, from_states: [:plugins, :modules_init]) status = tf_init_json(upgrade: upgrade, reconfigure: reconfigure) { |parsed_line| # seen = proc { |module_arg, type_arg| meta[:seen][:module_and_type].include?([module_arg, type_arg]) } case parsed_line[:level] when "info" # p parsed_line case parsed_line[:module] when "terraform.ui", "tofu.ui" phase = parse_tf_ui_line(parsed_line, , parser, phase: phase) when "non-json-log" print_init_line(parsed_line) else print_init_line(parsed_line, from: "run_tf_init_v2,info,else") end when "error" if parsed_line[:diagnostic] handled_error = false muted_error = false = {} unless parsed_line[:module] == "terragrunt" && parsed_line[:type] == "tf_failed" [:errors] ||= [] = { type: :error, message: parsed_line[:diagnostic]["summary"], body: parsed_line[:diagnostic]["detail"].split("\n") } [:errors] << end if parsed_line[:diagnostic]["summary"] == "Error acquiring the state lock" ["error"] = "lock" .merge!(parse_lock_info(parsed_line[:diagnostic]["detail"])) handled_error = true elsif parsed_line[:module] == "terragrunt" && parsed_line[:type] == "tf_failed" muted_error = true end unless muted_error if handled_error print_init_line(parsed_line, without: [:diagnostic], from: "run_tf_init_v2,error,handled") else # print_init_line(parsed_line, from: "run_tf_init_v2,error,unhandled_error") print_unhandled_error_line(parsed_line) [:printed] = true end end elsif parsed_line[:message] =~ /^\[reset\]/ print_unhandled_error_line(parsed_line) elsif parsed_line[:module] == :stderr && parsed_line[:type] == "unknown" && parsed_line[:message][0] == "{" print_tg_error_line(parsed_line) else print_init_line(parsed_line, from: "run_tf_init_v2,error,else") end end } [status.status, ] end |
.setup_init_parser(parser) ⇒ Object
61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/mux_tf/init_formatter.rb', line 61 def setup_init_parser(parser) parser.state(:modules_init, /^Initializing modules\.\.\./, [:none, :backend]) parser.state(:modules_upgrade, /^Upgrading modules\.\.\./) parser.state(:backend, /^Initializing the backend\.\.\./, [:none, :modules_init, :modules_upgrade]) parser.state(:plugins, /^Initializing provider plugins\.\.\./, [:backend, :modules_init]) parser.state(:backend_error, /Error when retrieving token from sso/, [:backend]) parser.state(:plugin_warnings, /^$/, [:plugins]) parser.state(:backend_error, /Error:/, [:backend]) end |
.tf_init_json(upgrade: nil, reconfigure: nil, &block) ⇒ Object
272 273 274 275 276 |
# File 'lib/mux_tf/init_formatter.rb', line 272 def tf_init_json(upgrade: nil, reconfigure: nil, &block) tf_cmd_json(proc { |handler| tf_init(upgrade: upgrade, reconfigure: reconfigure, json: true, &handler) }, &block) end |