Skip to content

Migrate oc_chef_authz_cleanup from gen_fsm to gen_statem - #4240

Open
tas50 wants to merge 1 commit into
chef:mainfrom
tas50:fix/gen-fsm-deprecation-oc-chef-authz-cleanup
Open

Migrate oc_chef_authz_cleanup from gen_fsm to gen_statem#4240
tas50 wants to merge 1 commit into
chef:mainfrom
tas50:fix/gen-fsm-deprecation-oc-chef-authz-cleanup

Conversation

@tas50

@tas50 tas50 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Problem

oc_chef_authz_cleanup is the last gen_fsm in the tree. gen_fsm has been deprecated since OTP 20, and on OTP 28 the compiler emits a deprecated-callback warning for every gen_fsm callback the module implements:

oc_chef_authz_cleanup.erl:31:2: the callback gen_fsm:code_change(_,_,_,_) is deprecated; use the 'gen_statem' module instead
oc_chef_authz_cleanup.erl:31:2: the callback gen_fsm:handle_event(_,_,_) is deprecated; ...
oc_chef_authz_cleanup.erl:31:2: the callback gen_fsm:handle_info(_,_,_) is deprecated; ...
oc_chef_authz_cleanup.erl:31:2: the callback gen_fsm:handle_sync_event(_,_,_,_) is deprecated; ...
oc_chef_authz_cleanup.erl:31:2: the callback gen_fsm:init(_) is deprecated; ...
oc_chef_authz_cleanup.erl:31:2: the callback gen_fsm:terminate(_,_,_) is deprecated; ...

src/oc_erchef/rebar.config sets warnings_as_errors, so this is not cosmetic — it is a hard build failure on OTP 28.

The module already carries -compile(nowarn_deprecated_function), but that option only suppresses deprecated function calls; it does not suppress deprecated callbacks, so it does not help here.

Measured with erlc -Werror against the official erlang:{26,27,28}-alpine images:

OTP before after
26 passes passes
27 passes passes
28 fails passes

So the current pin ({require_otp_vsn, "26.2.5.21"}) is safe today, but this module blocks a move to OTP 28.

What changed

Only oc_chef_authz_cleanup.erl. The behaviour is translated, not redesigned:

  • state_functions callback mode, so stopped/2 and started/2 become stopped/3 and started/3 with a leading event type. State names and every transition are unchanged.
  • send_event/send_all_state_eventgen_statem:cast/2, and sync_send_all_state_eventgen_statem:call/3. gen_statem routes all events to the current state function, so the old handle_event/3 (all-state), handle_sync_event/4 (all-state sync) and handle_info/3 bodies are consolidated into a single handle_common/3 that each state function falls through to.
  • The prune timer moves from gen_fsm:start_timer/2 to erlang:start_timer/3. This deliberately keeps the existing timer_ref field and the {timeout, Ref, prune} message shape (now arriving as an info event) rather than switching to gen_statem's built-in state/named timeouts, to keep the diff small and the semantics identical — including the existing behaviour where a stray prune timeout that fires while stopped is ignored and not re-armed. Happy to switch to native gen_statem timeouts if you'd prefer the more idiomatic form.

The public API, the state names, and the #state{} record are all unchanged, so oc_chef_authz_sup and the caller in oc_chef_authz_scoped_name need no changes, and oc_chef_authz_cleanup_tests needs no changes.

Verification

Compilationerlc -Werror on OTP 26, 27 and 28: clean on all three (OTP 28 fails on main).

Behaviour — because this is a behaviour swap rather than a logic change, I exercised the module directly against stubbed envy and oc_chef_authz modules on both OTP 26 and OTP 28, and ran the same assertions against the unmodified gen_fsm version on OTP 26 to confirm equivalence. All of the following pass identically before and after:

  • initial state defaults to empty sets
  • add_authz_ids/2 stores actors/groups, and unions with what is already there
  • add_authz_ids/2 still works in the started state (i.e. the former all-state event is handled from every state)
  • prune/0 deletes via oc_chef_authz:delete_resource/3 with the correct actor/group type and superuser id
  • prune/0 honours cleanup_batch_size (10 of 25 removed, 15 left)
  • the timer re-arms after start/0 and prunes on its own
  • stop/0 cancels the timer and no further pruning happens
  • unknown casts/info messages do not crash the process, and unknown sync events still reply ok

gen_fsm has been deprecated since OTP 20. On OTP 28 the compiler emits
deprecated-callback warnings for every gen_fsm callback the module
implements, and because oc_erchef builds with warnings_as_errors this
turns into a hard build failure:

    oc_chef_authz_cleanup.erl:31:2: the callback gen_fsm:init(_) is
      deprecated; use the 'gen_statem' module instead

The existing -compile(nowarn_deprecated_function) does not silence
these, as it only covers deprecated function calls, not deprecated
callbacks. OTP 26 and 27 are unaffected; OTP 28 is where it breaks.

Translation of the behaviour:

  * state_functions callback mode, so stopped/2 and started/2 become
    stopped/3 and started/3 with a leading event type.
  * send_event/send_all_state_event become gen_statem:cast/2 and
    sync_send_all_state_event becomes gen_statem:call/3. gen_statem
    routes every event to the current state function, so the former
    handle_event/3, handle_sync_event/4 and handle_info/3 bodies are
    consolidated into handle_common/3, which each state function falls
    through to.
  * The prune timer moves from gen_fsm:start_timer/2 to
    erlang:start_timer/3, keeping the existing timer_ref field and the
    {timeout, Ref, prune} message shape, now delivered as an info event.

State names, transitions, the public API and the #state{} record are
unchanged, so oc_chef_authz_sup and the callers in
oc_chef_authz_scoped_name need no changes.

Verified by compiling the module with -Werror on OTP 26, 27 and 28
(clean on all three; it fails on 28 before this change), and by
exercising the module against stubbed envy/oc_chef_authz on OTP 26 and
28 to confirm initial state, set union on add_authz_ids, batch-size
limited pruning, automatic pruning after start/0, timer cancellation on
stop/0 and tolerance of unknown events all behave as they did under
gen_fsm.

Signed-off-by: Tim Smith <tim@mondoo.com>
@tas50
tas50 requested review from a team as code owners September 7, 2026 07:08
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.

1 participant