Skip to content

CHEF-33330: Make knife ec import resilient to recoverable errors and normalize exit status - #206

Merged
sanghinitin merged 6 commits into
mainfrom
CHEF-33330-error-improvement
Sep 24, 2026
Merged

sanghinitin merged 6 commits into
mainfrom
CHEF-33330-error-improvement

Conversation

@sanghinitin

@sanghinitin sanghinitin commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Description

https://progresssoftware.atlassian.net/browse/CHEF-33330

knife ec import aborted mid-run on transient server/network failures, and the process exit status was inconsistent between verbose and non-verbose runs. This PR makes the import resilient to recoverable failures and normalizes the exit status.

lib/chef/knife/ec_import.rb

  • Introduces RECOVERABLE_NETWORK_ERRORS (Net::HTTPClientException, Net::HTTPFatalError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::ETIMEDOUT) so 5xx Internal Server Error responses and dropped connections (Connection reset by peer) are logged and skipped instead of aborting the import.
  • Applies that rescue list consistently to org verification, chef_fs_copy, cookbook freezing, and ACL updates.
  • Narrowly rescues the known Digest::Base cannot be directly inherited RuntimeError raised by some Ruby builds during cookbook checksum computation; every other RuntimeError is re-raised.
  • Surfaces a clear ui.error message for each skipped item, in addition to recording it in the error summary file.

lib/chef/knife/ec_error_handler.rb

  • Tracks the number of recorded errors (has_errors?).
  • Adds consistent_exit_status / override_exit_status so the command exits 1 whenever errors were recorded, and normalizes knife's non-zero exits (e.g. 100) to 1. Previously a run with -VVV and the same run without it reported different statuses.
  • Skips the override under RSpec so the test suite's own exit status is untouched.

Motivation

A single transient Internal Server Error or Connection reset by peer forced a full re-run of a long import. Operators also could not rely on the exit code in automation because it varied with the verbosity flag.

Evidence

New unit coverage in spec/chef/knife/ec_error_handler_spec.rb for #has_errors? and #consistent_exit_status (clean exit, exit 100 normalized to 1, clean SystemExit, raw exception under -VVV, and both error/no-error contexts).

$ bundle exec rspec spec/chef/knife/ec_error_handler_spec.rb spec/chef/knife/ec_import_spec.rb
66 examples, 2 failures

The 2 failures are pre-existing on main and unrelated to this change — verified by running the same spec file in a clean main worktree, which reports the identical 4 examples, 2 failures at the same lines. They are caused by the Net::HTTPServerException / Net::HTTPClientException alias on the local Ruby/Chef version, not by this PR.

Behavioral evidence from the command:

  • 5xx / connection-reset during an item copy -> <pattern> failed to copy: <message> is printed, the error is appended to the error summary file, and the import continues with the remaining items.
  • After such a run the command exits 1 (instead of 0/100), with or without -VVV.

Checklist

  • Commit is signed off (DCO)
  • Unit tests added for the new behavior
  • No change to the public CLI surface or option set
  • Backwards compatible: only previously fatal, unrecoverable paths are affected

Signed-off-by: nitin sanghi <nsanghi@progress.com>
Copilot AI lite review requested due to automatic review settings September 11, 2026 10:20
@sanghinitin
sanghinitin requested review from a team as code owners September 11, 2026 10:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Moderate issues remain in verification error handling and exit-hook coverage.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Updates knife ec import to continue through selected recoverable failures and normalize non-zero exit statuses.

Changes:

  • Adds recoverable network and digest error handling.
  • Tracks errors and standardizes exit status.
  • Adds unit coverage for error tracking and exit behavior.
File summaries
File Description
spec/chef/knife/ec_error_handler_spec.rb Tests error tracking and exit-status normalization.
lib/chef/knife/ec_import.rb Adds resilient handling for import failures.
lib/chef/knife/ec_error_handler.rb Tracks errors and overrides inconsistent exit statuses.
Review details

Suppressed comments (3)

lib/chef/knife/ec_error_handler.rb:47

  • The new exit hook is registered only when EcErrorHandler#initialize runs, but EcBase#knife_ec_error_handler is lazy and EcImport#run reaches it only inside recovery/error paths. If the import fails before any recoverable error is recorded—for example, warn_on_incorrect_clients_group raises while parsing a malformed group file—no hook runs and knife's 100/1 verbosity difference is unchanged. Register the handler at command start (or attach the exit normalization independently of error-file creation) if this normalization is meant to cover all command exits.
        at_exit do
          display(@err_file)
          override_exit_status
        end

lib/chef/knife/ec_import.rb:441

  • A recoverable failure during the ACL GET/PUT is only written to the summary file and produces no UI message, so an import can silently skip a user's ACL and the operator cannot tell which item failed. Emit a contextual ui.error here before recording the exception, consistent with the copy failure path.
      rescue *RECOVERABLE_NETWORK_ERRORS => ex
        knife_ec_error_handler.add(ex)

lib/chef/knife/ec_import.rb:172

  • This recoverable verification path returns false, and run treats false as proof that the organization does not exist (see line 141). A timeout, reset, or server failure can consequently report that a valid organization is missing, masking the actual failure; use a result that distinguishes not-found from verification failure or only print the not-found message for a 404.
      rescue *RECOVERABLE_NETWORK_ERRORS => ex
        ui.error "Failed to verify organization #{orgname}: #{ex.message}"
        knife_ec_error_handler.add(ex)
        false
  • Files reviewed: 3/3 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lib/chef/knife/ec_import.rb Outdated
Comment on lines +312 to +315
rescue RuntimeError => ex
# Only swallow the known Digest::Base inheritance error so the import
# continues; re-raise anything else.
raise unless ex.message.to_s.include?(DIGEST_INHERITANCE_ERROR)
Comment thread lib/chef/knife/ec_import.rb
@sanghinitin sanghinitin added the ai-assisted Work completed with AI assistance following Progress AI policies label Sep 16, 2026
Copilot AI review requested due to automatic review settings September 21, 2026 08:52
@jashaik

jashaik commented Sep 21, 2026

Copy link
Copy Markdown

LGTM

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved recovery-path, reporting, coverage, and exit-status issues remain.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity · 1 Low severity

Open (2)
Resolved since last review (2)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Surface recoverable ACL failures to the operator

lib/​chef/​knife/​ec_import.rb:438

A recoverable ACL failure is recorded here but never surfaced to the operator, unlike the organization, copy, and cookbook paths above. This leaves a skipped user/group ACL update invisible during the run; emit a ui.error message containing url and ex.message before recording the exception.

Comment thread lib/chef/knife/ec_import.rb
Signed-off-by: nitin sanghi <nsanghi@progress.com>
Copilot AI review requested due to automatic review settings September 21, 2026 08:59
@sanghinitin
sanghinitin force-pushed the CHEF-33330-error-improvement branch from 7d04ccc to a4ae7dd Compare September 21, 2026 08:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Four moderate review findings remain unresolved in lib/chef/knife/ec_import.rb.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity · 1 Medium severity · 1 Low severity

Open (3)

Comment thread lib/chef/knife/ec_import.rb
Signed-off-by: nitin sanghi <nsanghi@progress.com>
Copilot AI review requested due to automatic review settings September 21, 2026 09:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Address the exit-status hook, misleading organization message, and missing targeted exception-handling coverage.

Review effort: Lite
Findings: 1 High severity · 1 Low severity

Open (2)
Resolved since last review (1)
Previously missed (1)

In code that hasn't changed since last review

Low severity Add coverage for recoverable network exception paths

lib/​chef/​knife/​ec_import.rb:43

The newly added recoverable exception cases are not exercised by the import specs: import_net_exception constructs Net::HTTPServerException (aliased to Net::HTTPClientException in this suite), so it does not verify Net::HTTPFatalError or the ECONNRESET/ECONNREFUSED/ETIMEDOUT paths. Add focused examples that assert each is logged and the import continues; otherwise a regression in this rescue list can make the original failure fatal again.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Exit normalization unintentionally affects backup and restore commands, and key recovery paths lack coverage.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 2 High severity · 1 Medium severity · 1 Low severity

Open (4)

Comment thread lib/chef/knife/ec_error_handler.rb
Comment on lines +38 to +43
RECOVERABLE_NETWORK_ERRORS = [
Net::HTTPClientException, # 4xx
Net::HTTPFatalError, # 5xx, e.g. Internal Server Error
Errno::ECONNRESET, # Connection reset by peer
Errno::ECONNREFUSED,
Errno::ETIMEDOUT,
Signed-off-by: nitin sanghi <nsanghi@progress.com>
Copilot AI review requested due to automatic review settings September 22, 2026 05:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Lazy handler initialization prevents some non-zero exits from being normalized.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity · 1 Low severity

Open (3)
Resolved since last review (1)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Register exit-status hook before lazy error handling

lib/​chef/​knife/​ec_error_handler.rb:48

The exit-status hook is only registered when EcErrorHandler is instantiated, but EcBase#knife_ec_error_handler creates it lazily and EcImport#run only accesses it after a recoverable error is recorded. Therefore an import that reaches Knife's non-zero exit (for example, 100) before any recorded error never installs this hook and remains unnormalized, despite consistent_exit_status handling the no-error case. Initialize/register the import handler at command startup (or install the status hook independently of lazy error logging), and cover the real lifecycle rather than only calling consistent_exit_status on a manually constructed handler.

nikhil2611
nikhil2611 previously approved these changes Sep 22, 2026
ashiqueps
ashiqueps previously approved these changes Sep 22, 2026
jashaik
jashaik previously approved these changes Sep 23, 2026

@marcparadise marcparadise left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, though I'd recommend maybe_exit_status as the name instead of consistent_exit_status, it follows a pattern established in chef, knife , and other projects where we use 'maybe' if something can return a value or nil.

Signed-off-by: nitin sanghi <nsanghi@progress.com>
Copilot AI review requested due to automatic review settings September 23, 2026 13:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Exit normalization is not installed for error-free handler paths and may disrupt non-import commands.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity · 1 Low severity

Open (3)
Resolved since last review (1)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Initialize exit-status hook before imports can fail

lib/​chef/​knife/​ec_error_handler.rb:48

The exit-status hook is registered only when EcErrorHandler is instantiated, but EcBase#knife_ec_error_handler creates it lazily on the first recorded error. An import that fails before calling add—the exact no-recorded-error SystemExit(100) case covered by the unit helper—never installs this hook, so the real command still exits 100 instead of 1. Initialize the handler at import startup (or register the hook independently of error recording) so normalization applies to every import run.

Comment thread lib/chef/knife/ec_error_handler.rb Outdated
Signed-off-by: nitin sanghi <nsanghi@progress.com>
Copilot AI review requested due to automatic review settings September 23, 2026 13:13

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Exit normalization is not installed before early failures, and some newly introduced recovery paths lack coverage.

Review effort: Balanced
Findings: 1 Medium severity · 1 Low severity

Open (2)
Resolved since last review (1)
Previously missed (3)

In code that hasn't changed since last review

Medium severity Initialize exit normalization before lazy error handler creation

lib/​chef/​knife/​ec_error_handler.rb:48

This exit hook is only registered after an EcErrorHandler instance is created, but EcBase#knife_ec_error_handler constructs it lazily and EcImport#run does not initialize it. Consequently, a Knife SystemExit(100) raised before any recoverable error is recorded has no hook and still exits 100, even though the new no-error spec expects normalization to 1. Initialize the handler when an import starts, or register the normalization independently of the lazy error-recording path.

Medium severity Distinguish verification failures from confirmed missing organizations

lib/​chef/​knife/​ec_import.rb:169

For a newly handled 5xx or socket failure, returning false makes run also print Organization <name> does not exist. Skipping. The organization was not found to be absent—the check was inconclusive—so operators receive a contradictory diagnosis. Use a distinct result for verification failures (or handle them separately in the caller) and reserve false for a confirmed 404.

Low severity Test Net::HTTPFatalError handling for real 5xx responses

lib/​chef/​knife/​ec_import.rb:40

The specs labeled as 500 responses construct Net::HTTPServerException, which is an alias of Net::HTTPClientException in the environment noted in the PR, so they do not exercise this newly added Net::HTTPFatalError branch. Add a spec that directly raises Net::HTTPFatalError to verify real 5xx responses are recorded and skipped as intended.

@sanghinitin
sanghinitin merged commit 7467119 into main Sep 24, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-assisted Work completed with AI assistance following Progress AI policies

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants