Skip to content

chore: Alias pkg/errors as pkgerrors, enable errorlint, and use stdlib errors where it suffices - #133

Merged
hrak merged 2 commits into
developfrom
pkgerrors_alias
Sep 15, 2026
Merged

hrak merged 2 commits into
developfrom
pkgerrors_alias

Conversation

@hrak

@hrak hrak commented Sep 15, 2026

Copy link
Copy Markdown
Member

Summary

github.com/pkg/errors was imported unaliased in all 29 files that use it, so the bare identifier errors meant pkg/errors everywhere and the standard library errors package was unreachable without a rename. The package a reader sees as errors was not the one they would expect.

This adopts the convention upstream Cluster API uses, rather than diverging from it.

Two commits:

  1. b79dc03 — the alias rename plus lint config
  2. a261495 — the seven sites where stdlib errors suffices

Why alias rather than migrate off pkg/errors

The obvious move is to replace pkg/errors with fmt.Errorf/%w. I looked into that and it does not hold up:

  • The dependency does not go away. sigs.k8s.io/cluster-api@v1.14.2 requires github.com/pkg/errors directly. Dropping every CAPC import only demotes it to // indirect; it stays in the build regardless.
  • Upstream is not migrating. CAPI v1.14.2 imports it in 278 files, with 901 Wrapf, 415 Wrap, 451 Errorf and 317 New calls. What it did do is standardise on the alias pkgerrors at all 281 import sites, freeing bare errors for the standard library — which it then uses for New, Is and As. That is exactly what this PR does.

A full rewrite would mean ~191 behaviour-bearing call sites for no dependency reduction and a permanent style split with upstream. It would also have to handle sites where errors.Wrap(nil, ...) returning nil is load-bearing — pkg/cloud/isolated_network.go:723 wraps a raw call result and :891 wraps an unguarded err; a naive fmt.Errorf("...: %w", ...) there would make every successful isolated-network reconcile return a non-nil error.

The rename is provably mechanical

Same package, same functions, no behaviour change. Two checks back that up:

  • Inverting the substitution on the working tree reproduces the previous revision byte for byte across all 29 files.
  • Per-function call counts are conserved: 54 New, 49 Wrap, 42 Errorf, 39 Wrapf, 5 Is, 1 Cause, 1 As — 191 before, 191 after.

The apierrors and kerrors aliases in the seven files that also import an apimachinery errors package are untouched.

Lint config

  • importas gains a github.com/pkg/errors → pkgerrors entry, so a future unaliased import fails lint instead of quietly reintroducing the shadow.
  • errorlint is enabled. It already passes with zero findings on the current tree, so it costs nothing today; it guards the fmt.Errorf/%w sites pkg/cloud/instance.go already has and anything added later. Worth knowing: it only inspects fmt.Errorf, so it cannot see pkgerrors.Errorf/Wrapf.

Second commit: stdlib where it suffices

Seven sites where pkg/errors adds nothing:

  • The five errors.Is and one errors.As calls. pkg/errors' versions are thin forwarders to the standard library, so these call the identical function before and after.
  • cloud.ErrNotFound. It is returned bare, never wrapped, and every comparison is by identity, so stdlib errors.New is equivalent — and drops a stack trace nothing ever formatted.

pkg/metrics/metrics.go had errors.As as its only pkg/errors use, so it no longer imports pkg/errors at all (29 → 28 files).

Found while surveying — deliberately NOT in this PR

Real issues, none caused by this work. Filing separately; flagging here so they are not lost:

  • ⚠️ Credentials written to test output. test/e2e/common.go:406 does Fail(fmt.Sprintf("Invalid secret: %+v, %s, %s, %s", secret.Data, apiURL, apiKey, secretKey)) — printing the whole secret map plus the CloudStack api-key and secret-key in cleartext into CI logs on the invalid-secret path. Worth fixing regardless of anything here.
  • Duplicate error append. pkg/cloud/zone.go:34 and :36 append the identical Wrapf(err, "could not get Zone ID from %v", ...) twice, with the metrics call between them — a copy-paste slip that duplicates the message in the multierror output.
  • %+v on an error that is never wrapped. internal/controllers/cloudstackmachine_controller.go:214 uses Errorf("failed to reconcile LB attachment: %+v", err). Errorf does not wrap, so errors.Is/As already cannot see through it.

Test plan

  • Inverse-substitution proof — all 29 files byte-identical to the previous revision once the rename is undone
  • Call-site counts conserved, per function
  • make build
  • make lint — 0 issues, 43 active linters including the two added
  • make test — all ginkgo suites plus the internal/controllers envtest suite. pkg/cloud matters most here: it asserts on exact error strings (instance_test.go uses MatchError with literals, zone_test.go:73-75 encodes the ": " wrap separator), so a passing run confirms no message shapes moved
  • go vet -tags e2e ./... in test/e2e — not covered by make lint, which excludes test/

🤖 Generated with Claude Code

hrak and others added 2 commits September 15, 2026 12:36
github.com/pkg/errors was imported unaliased in all 29 files that use it,
so the bare identifier errors meant pkg/errors everywhere and the standard
library errors package was unreachable without a rename. The package a
reader sees as errors was not the one they would expect.

This adopts the convention upstream Cluster API uses. CAPI v1.14.2 imports
pkg/errors in 278 files and aliases it as pkgerrors at every one of those
sites, which frees the bare errors for the standard library. Doing the same
here keeps us aligned with upstream rather than diverging from it.

Migrating off pkg/errors entirely was considered and rejected. CAPI requires
it directly, so it stays in our module graph as an indirect dependency
whether or not we import it, and upstream shows no sign of moving: v1.14.2
still has 901 Wrapf, 415 Wrap, 451 Errorf and 317 New calls. A rewrite would
mean ~191 behaviour-bearing call sites, including a few where Wrap(nil)
returning nil is load-bearing, for no dependency reduction and a permanent
style split with upstream.

The rename itself is a pure identifier substitution: same package, same
functions, no behaviour change. Inverting it on the working tree reproduces
the previous revision byte for byte, and the per-function call counts are
unchanged at 54 New, 49 Wrap, 42 Errorf, 39 Wrapf, 5 Is, 1 Cause and 1 As.
The apierrors and kerrors aliases in the seven files that also import an
apimachinery errors package are untouched.

An importas entry pins the alias, so a future unaliased import fails lint
instead of quietly reintroducing the shadow. errorlint is enabled alongside
it and already passes with no findings; it guards the fmt.Errorf %w sites
that pkg/cloud/instance.go already has, and anything added later. Note it
only inspects fmt.Errorf, so it cannot see pkgerrors.Errorf or Wrapf.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… sentinel

With pkg/errors now aliased as pkgerrors, the bare errors identifier is free
for the standard library. These seven sites are the ones where pkg/errors
adds nothing, so they move over. This mirrors upstream Cluster API, which
uses stdlib errors.New, errors.Is and errors.As alongside pkgerrors.

pkg/errors' Is and As are thin forwarders to the standard library, so the
six call sites in cloudstackmachine_controller.go, pkg/cloud/instance.go,
pkg/cloud/client.go and pkg/metrics/metrics.go call exactly the same
function before and after.

cloud.ErrNotFound is returned bare and never wrapped, and every comparison
against it is by identity, so stdlib errors.New is equivalent. It also drops
a stack trace that nothing ever formatted.

pkg/metrics/metrics.go had errors.As as its only pkg/errors use, so it no
longer imports pkg/errors at all. The other three files keep it for Wrap,
Wrapf, Errorf and New.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@hrak
hrak merged commit 99ace38 into develop Sep 15, 2026
3 checks passed
@hrak
hrak deleted the pkgerrors_alias branch September 15, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants