OK, following discussion, I've broken the upgrade process into 3 basic transitions (plus associated error/retry) ones: "begin_charm_upgrade", "replace_charm", "finish_charm_upgrade", with 2 new (non-error) states: "charm_upgrade_ready" and "charm_replaced".
There's no more first_attempt malarkey, BUT the "begin_charm_upgrade" transition will raise an error if there is no new charm available, hence aborting the upgrade operation (and keeping the workflow in state "started"); and thereby ensuring that any upgrade operation that makes it to the "charm_upgrade_ready" state represents a real upgrade, and must therefore pass through "replace_charm" and "finish_charm_upgrade" before it returns to the "started" state.
[2]
Hmm, very sensible, not sure why I did that :-/.
[3]
Sounds good, done.
[4/5]
OK, following discussion, I've broken the upgrade process into 3 basic transitions (plus associated error/retry) ones: "begin_ charm_upgrade" , "replace_charm", "finish_ charm_upgrade" , with 2 new (non-error) states: "charm_ upgrade_ ready" and "charm_replaced".
There's no more first_attempt malarkey, BUT the "begin_ charm_upgrade" transition will raise an error if there is no new charm available, hence aborting the upgrade operation (and keeping the workflow in state "started"); and thereby ensuring that any upgrade operation that makes it to the "charm_ upgrade_ ready" state represents a real upgrade, and must therefore pass through "replace_charm" and "finish_ charm_upgrade" before it returns to the "started" state.