@@ -426,15 +426,103 @@ func isBroken(st *state.State, snapName string) (bool, error) {
426426 return false , nil
427427}
428428
429+ func cloneConnState (connState * schema.ConnState ) * schema.ConnState {
430+ clone := * connState
431+
432+ cloneAttrs := func (attrs map [string ]any ) map [string ]any {
433+ if attrs == nil {
434+ return nil
435+ }
436+ return utils .CopyAttributes (attrs )
437+ }
438+
439+ clone .StaticPlugAttrs = cloneAttrs (connState .StaticPlugAttrs )
440+ clone .DynamicPlugAttrs = cloneAttrs (connState .DynamicPlugAttrs )
441+ clone .StaticSlotAttrs = cloneAttrs (connState .StaticSlotAttrs )
442+ clone .DynamicSlotAttrs = cloneAttrs (connState .DynamicSlotAttrs )
443+
444+ return & clone
445+ }
446+
447+ // saveChangedConnectionsForSetupProfilesRestore records original connection
448+ // states for setup-profiles do tasks whose undo can restore them.
449+ func saveChangedConnectionsForSetupProfilesRestore (task * state.Task , instanceName string , changedConns map [string ]* schema.ConnState ) error {
450+ if len (changedConns ) == 0 {
451+ return nil
452+ }
453+
454+ // undo setup-profiles also calls this code while rebuilding old profiles,
455+ // but restoration data should only come from the original do path
456+ if task .Status () == state .UndoingStatus {
457+ return nil
458+ }
459+
460+ // if this isn't the setup-profiles task that is going to handle the undo,
461+ // then we don't need to keep track of these on the task
462+ if ! shouldUndoSetupProfiles (task , instanceName ) {
463+ return nil
464+ }
465+
466+ var originalConns map [string ]* schema.ConnState
467+ err := task .Get ("original-connection-states" , & originalConns )
468+ if err != nil && ! errors .Is (err , state .ErrNoState ) {
469+ return err
470+ }
471+ if originalConns == nil {
472+ originalConns = make (map [string ]* schema.ConnState )
473+ }
474+
475+ for connID , connState := range changedConns {
476+ if originalConns [connID ] != nil {
477+ // a setup-profiles task can be retried after saving original states
478+ // and unlocking for backend setup. keep the original snapshot.
479+ continue
480+ }
481+ originalConns [connID ] = connState
482+ }
483+
484+ task .Set ("original-connection-states" , originalConns )
485+
486+ return nil
487+ }
488+
489+ // restoreConnectionsForSetupProfiles restores connection states saved on a
490+ // setup-profiles task.
491+ func restoreConnectionsForSetupProfiles (task * state.Task ) error {
492+ var original map [string ]* schema.ConnState
493+ err := task .Get ("original-connection-states" , & original )
494+ if errors .Is (err , state .ErrNoState ) {
495+ return nil
496+ }
497+ if err != nil {
498+ return err
499+ }
500+
501+ st := task .State ()
502+
503+ conns , err := getConns (st )
504+ if err != nil {
505+ return err
506+ }
507+
508+ for connID , connState := range original {
509+ conns [connID ] = connState
510+ }
511+ setConns (st , conns )
512+
513+ return nil
514+ }
515+
429516// reloadConnections reloads connections stored in the state in the repository.
430517// Using non-empty snapName the operation can be scoped to connections
431518// affecting a given snap.
432519//
433- // The return value is the list of affected snap names and their connection IDs.
434- func (m * InterfaceManager ) reloadConnections (snapName string ) (reloadedConnectionIDs []string , err error ) {
520+ // The return value is the list of affected snap names and their connection IDs,
521+ // plus the original connection states that were changed.
522+ func (m * InterfaceManager ) reloadConnections (snapName string ) (reloadedConnectionIDs []string , changedConns map [string ]* schema.ConnState , err error ) {
435523 conns , err := getConns (m .state )
436524 if err != nil {
437- return nil , err
525+ return nil , nil , err
438526 }
439527
440528 var policyChecker interfaces.PolicyFunc
@@ -445,21 +533,22 @@ func (m *InterfaceManager) reloadConnections(snapName string) (reloadedConnectio
445533 if errors .Is (err , state .ErrNoState ) {
446534 // everything else is a noop, as no model means no connections
447535 // to reload
448- return nil , nil
536+ return nil , nil , nil
449537 } else if err != nil {
450- return nil , err
538+ return nil , nil , err
451539 }
452540 autoChecker , err = newAutoConnectChecker (m .state , m .repo , deviceCtx )
453541 if err != nil {
454- return nil , err
542+ return nil , nil , err
455543 }
456544
457545 connChecker , err = newConnectChecker (m .state , deviceCtx )
458546 if err != nil {
459- return nil , err
547+ return nil , nil , err
460548 }
461549
462550 connStateChanged := false
551+ changedConns = make (map [string ]* schema.ConnState )
463552
464553 var reloadedConnections []string
465554ConnsLoop:
@@ -473,7 +562,7 @@ ConnsLoop:
473562 }
474563 connRef , err := interfaces .ParseConnRef (connId )
475564 if err != nil {
476- return nil , err
565+ return nil , nil , err
477566 }
478567 // Apply filtering, this allows us to reload only a subset of
479568 // connections (and similarly, refresh the static attributes of only a
@@ -497,13 +586,14 @@ ConnsLoop:
497586 for _ , snapName := range []string {connRef .PlugRef .Snap , connRef .SlotRef .Snap } {
498587 broken , err := isBroken (m .state , snapName )
499588 if err != nil {
500- return nil , err
589+ return nil , nil , err
501590 }
502591 if broken {
503592 logger .Noticef ("Snap %q is broken, ignored by reloadConnections" , snapName )
504593 continue ConnsLoop
505594 }
506595 }
596+ changedConns [connId ] = cloneConnState (connState )
507597 delete (conns , connId )
508598 connStateChanged = true
509599 }
@@ -540,11 +630,11 @@ ConnsLoop:
540630
541631 plugAppSet , err := interfaces .NewSnapAppSet (plugInfo .Snap , nil )
542632 if err != nil {
543- return nil , err
633+ return nil , nil , err
544634 }
545635 slotAppSet , err := interfaces .NewSnapAppSet (slotInfo .Snap , nil )
546636 if err != nil {
547- return nil , err
637+ return nil , nil , err
548638 }
549639
550640 cplug := interfaces .NewConnectedPlug (plugInfo , plugAppSet , newStaticPlugAttrs , connState .DynamicPlugAttrs )
@@ -568,6 +658,7 @@ ConnsLoop:
568658 reloadedConnections = append (reloadedConnections , connId )
569659
570660 if updateStaticAttrs {
661+ changedConns [connId ] = cloneConnState (connState )
571662 connState .StaticPlugAttrs = staticPlugAttrs
572663 connState .StaticSlotAttrs = staticSlotAttrs
573664 connStateChanged = true
@@ -578,7 +669,7 @@ ConnsLoop:
578669 setConns (m .state , conns )
579670 }
580671
581- return reloadedConnections , nil
672+ return reloadedConnections , changedConns , nil
582673}
583674
584675// removeConnections disconnects all connections of the snap in the repo. It should only be used if the snap
0 commit comments