@@ -426,15 +426,93 @@ func isBroken(st *state.State, snapName string) (bool, error) {
426426 return false , nil
427427}
428428
429+ func cloneConnAttrs (attrs map [string ]any ) map [string ]any {
430+ if attrs == nil {
431+ return nil
432+ }
433+ return utils .CopyAttributes (attrs )
434+ }
435+
436+ func cloneConnState (connState * schema.ConnState ) * schema.ConnState {
437+ clone := * connState
438+ clone .StaticPlugAttrs = cloneConnAttrs (connState .StaticPlugAttrs )
439+ clone .DynamicPlugAttrs = cloneConnAttrs (connState .DynamicPlugAttrs )
440+ clone .StaticSlotAttrs = cloneConnAttrs (connState .StaticSlotAttrs )
441+ clone .DynamicSlotAttrs = cloneConnAttrs (connState .DynamicSlotAttrs )
442+ return & clone
443+ }
444+
445+ // saveChangedConnectionsForSetupProfilesRestore records original connection
446+ // states for setup-profiles do tasks whose undo can restore them.
447+ func saveChangedConnectionsForSetupProfilesRestore (task * state.Task , instanceName string , changedConns map [string ]* schema.ConnState ) error {
448+ if len (changedConns ) == 0 {
449+ return nil
450+ }
451+
452+ // undo setup-profiles also calls this code while rebuilding old profiles,
453+ // but restoration data should only come from the original do path
454+ if task .Status () == state .UndoingStatus {
455+ return nil
456+ }
457+
458+ if ! shouldUndoSetupProfiles (task , instanceName ) {
459+ return nil
460+ }
461+
462+ var originalConns map [string ]* schema.ConnState
463+ err := task .Get ("original-connection-states" , & originalConns )
464+ if err != nil && ! errors .Is (err , state .ErrNoState ) {
465+ return err
466+ }
467+ if originalConns == nil {
468+ originalConns = make (map [string ]* schema.ConnState )
469+ }
470+ for connID , connState := range changedConns {
471+ if originalConns [connID ] != nil {
472+ // a setup-profiles task can be retried after saving original states
473+ // and unlocking for backend setup. keep the original snapshot.
474+ continue
475+ }
476+ originalConns [connID ] = connState
477+ }
478+
479+ task .Set ("original-connection-states" , originalConns )
480+ return nil
481+ }
482+
483+ // restoreConnectionsForSetupProfiles restores connection states saved on a
484+ // setup-profiles task.
485+ func restoreConnectionsForSetupProfiles (task * state.Task ) error {
486+ var originalConns map [string ]* schema.ConnState
487+ err := task .Get ("original-connection-states" , & originalConns )
488+ if errors .Is (err , state .ErrNoState ) {
489+ return nil
490+ }
491+ if err != nil {
492+ return err
493+ }
494+
495+ conns , err := getConns (task .State ())
496+ if err != nil {
497+ return err
498+ }
499+ for connID , connState := range originalConns {
500+ conns [connID ] = cloneConnState (connState )
501+ }
502+ setConns (task .State (), conns )
503+ return nil
504+ }
505+
429506// reloadConnections reloads connections stored in the state in the repository.
430507// Using non-empty snapName the operation can be scoped to connections
431508// affecting a given snap.
432509//
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 ) {
510+ // The return value is the list of affected snap names and their connection IDs,
511+ // plus the original connection states that were changed.
512+ func (m * InterfaceManager ) reloadConnections (snapName string ) (reloadedConnectionIDs []string , changedConns map [string ]* schema.ConnState , err error ) {
435513 conns , err := getConns (m .state )
436514 if err != nil {
437- return nil , err
515+ return nil , nil , err
438516 }
439517
440518 var policyChecker interfaces.PolicyFunc
@@ -445,21 +523,22 @@ func (m *InterfaceManager) reloadConnections(snapName string) (reloadedConnectio
445523 if errors .Is (err , state .ErrNoState ) {
446524 // everything else is a noop, as no model means no connections
447525 // to reload
448- return nil , nil
526+ return nil , nil , nil
449527 } else if err != nil {
450- return nil , err
528+ return nil , nil , err
451529 }
452530 autoChecker , err = newAutoConnectChecker (m .state , m .repo , deviceCtx )
453531 if err != nil {
454- return nil , err
532+ return nil , nil , err
455533 }
456534
457535 connChecker , err = newConnectChecker (m .state , deviceCtx )
458536 if err != nil {
459- return nil , err
537+ return nil , nil , err
460538 }
461539
462540 connStateChanged := false
541+ changedConns = make (map [string ]* schema.ConnState )
463542
464543 var reloadedConnections []string
465544ConnsLoop:
@@ -473,7 +552,7 @@ ConnsLoop:
473552 }
474553 connRef , err := interfaces .ParseConnRef (connId )
475554 if err != nil {
476- return nil , err
555+ return nil , nil , err
477556 }
478557 // Apply filtering, this allows us to reload only a subset of
479558 // connections (and similarly, refresh the static attributes of only a
@@ -497,13 +576,14 @@ ConnsLoop:
497576 for _ , snapName := range []string {connRef .PlugRef .Snap , connRef .SlotRef .Snap } {
498577 broken , err := isBroken (m .state , snapName )
499578 if err != nil {
500- return nil , err
579+ return nil , nil , err
501580 }
502581 if broken {
503582 logger .Noticef ("Snap %q is broken, ignored by reloadConnections" , snapName )
504583 continue ConnsLoop
505584 }
506585 }
586+ changedConns [connId ] = cloneConnState (connState )
507587 delete (conns , connId )
508588 connStateChanged = true
509589 }
@@ -540,11 +620,11 @@ ConnsLoop:
540620
541621 plugAppSet , err := interfaces .NewSnapAppSet (plugInfo .Snap , nil )
542622 if err != nil {
543- return nil , err
623+ return nil , nil , err
544624 }
545625 slotAppSet , err := interfaces .NewSnapAppSet (slotInfo .Snap , nil )
546626 if err != nil {
547- return nil , err
627+ return nil , nil , err
548628 }
549629
550630 cplug := interfaces .NewConnectedPlug (plugInfo , plugAppSet , newStaticPlugAttrs , connState .DynamicPlugAttrs )
@@ -568,6 +648,7 @@ ConnsLoop:
568648 reloadedConnections = append (reloadedConnections , connId )
569649
570650 if updateStaticAttrs {
651+ changedConns [connId ] = cloneConnState (connState )
571652 connState .StaticPlugAttrs = staticPlugAttrs
572653 connState .StaticSlotAttrs = staticSlotAttrs
573654 connStateChanged = true
@@ -578,7 +659,7 @@ ConnsLoop:
578659 setConns (m .state , conns )
579660 }
580661
581- return reloadedConnections , nil
662+ return reloadedConnections , changedConns , nil
582663}
583664
584665// removeConnections disconnects all connections of the snap in the repo. It should only be used if the snap
0 commit comments